Absolute File Name: | /home/qt/qt5_coco/qt5/qtbase/src/corelib/codecs/qutfcodec.cpp |
Source code | Switch to Preprocessed file |
Line | Source | Count | ||||||
---|---|---|---|---|---|---|---|---|
1 | /**************************************************************************** | - | ||||||
2 | ** | - | ||||||
3 | ** Copyright (C) 2016 The Qt Company Ltd. | - | ||||||
4 | ** Copyright (C) 2016 Intel Corporation. | - | ||||||
5 | ** Contact: https://www.qt.io/licensing/ | - | ||||||
6 | ** | - | ||||||
7 | ** This file is part of the QtCore module of the Qt Toolkit. | - | ||||||
8 | ** | - | ||||||
9 | ** $QT_BEGIN_LICENSE:LGPL$ | - | ||||||
10 | ** Commercial License Usage | - | ||||||
11 | ** Licensees holding valid commercial Qt licenses may use this file in | - | ||||||
12 | ** accordance with the commercial license agreement provided with the | - | ||||||
13 | ** Software or, alternatively, in accordance with the terms contained in | - | ||||||
14 | ** a written agreement between you and The Qt Company. For licensing terms | - | ||||||
15 | ** and conditions see https://www.qt.io/terms-conditions. For further | - | ||||||
16 | ** information use the contact form at https://www.qt.io/contact-us. | - | ||||||
17 | ** | - | ||||||
18 | ** GNU Lesser General Public License Usage | - | ||||||
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser | - | ||||||
20 | ** General Public License version 3 as published by the Free Software | - | ||||||
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | - | ||||||
22 | ** packaging of this file. Please review the following information to | - | ||||||
23 | ** ensure the GNU Lesser General Public License version 3 requirements | - | ||||||
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | - | ||||||
25 | ** | - | ||||||
26 | ** GNU General Public License Usage | - | ||||||
27 | ** Alternatively, this file may be used under the terms of the GNU | - | ||||||
28 | ** General Public License version 2.0 or (at your option) the GNU General | - | ||||||
29 | ** Public license version 3 or any later version approved by the KDE Free | - | ||||||
30 | ** Qt Foundation. The licenses are as published by the Free Software | - | ||||||
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | - | ||||||
32 | ** included in the packaging of this file. Please review the following | - | ||||||
33 | ** information to ensure the GNU General Public License requirements will | - | ||||||
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | - | ||||||
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. | - | ||||||
36 | ** | - | ||||||
37 | ** $QT_END_LICENSE$ | - | ||||||
38 | ** | - | ||||||
39 | ****************************************************************************/ | - | ||||||
40 | - | |||||||
41 | #include "qutfcodec_p.h" | - | ||||||
42 | #include "qlist.h" | - | ||||||
43 | #include "qendian.h" | - | ||||||
44 | #include "qchar.h" | - | ||||||
45 | - | |||||||
46 | #include "private/qsimd_p.h" | - | ||||||
47 | #include "private/qstringiterator_p.h" | - | ||||||
48 | - | |||||||
49 | QT_BEGIN_NAMESPACE | - | ||||||
50 | - | |||||||
51 | enum { Endian = 0, Data = 1 }; | - | ||||||
52 | - | |||||||
53 | static const uchar utf8bom[] = { 0xef, 0xbb, 0xbf }; | - | ||||||
54 | - | |||||||
55 | #if defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSE2) | - | ||||||
56 | static inline bool simdEncodeAscii(uchar *&dst, const ushort *&nextAscii, const ushort *&src, const ushort *end) | - | ||||||
57 | { | - | ||||||
58 | // do sixteen characters at a time | - | ||||||
59 | for ( ; end - src >= 16; src += 16, dst += 16) { | - | ||||||
60 | __m128i data1 = _mm_loadu_si128((const __m128i*)src); | - | ||||||
61 | __m128i data2 = _mm_loadu_si128(1+(const __m128i*)src); | - | ||||||
62 | - | |||||||
63 | - | |||||||
64 | // check if everything is ASCII | - | ||||||
65 | // the highest ASCII value is U+007F | - | ||||||
66 | // Do the packing directly: | - | ||||||
67 | // The PACKUSWB instruction has packs a signed 16-bit integer to an unsigned 8-bit | - | ||||||
68 | // with saturation. That is, anything from 0x0100 to 0x7fff is saturated to 0xff, | - | ||||||
69 | // while all negatives (0x8000 to 0xffff) get saturated to 0x00. To detect non-ASCII, | - | ||||||
70 | // we simply do a signed greater-than comparison to 0x00. That means we detect NULs as | - | ||||||
71 | // "non-ASCII", but it's an acceptable compromise. | - | ||||||
72 | __m128i packed = _mm_packus_epi16(data1, data2); | - | ||||||
73 | __m128i nonAscii = _mm_cmpgt_epi8(packed, _mm_setzero_si128()); | - | ||||||
74 | - | |||||||
75 | // store, even if there are non-ASCII characters here | - | ||||||
76 | _mm_storeu_si128((__m128i*)dst, packed); | - | ||||||
77 | - | |||||||
78 | // n will contain 1 bit set per character in [data1, data2] that is non-ASCII (or NUL) | - | ||||||
79 | ushort n = ~_mm_movemask_epi8(nonAscii); | - | ||||||
80 | if (n) { | - | ||||||
81 | // find the next probable ASCII character | - | ||||||
82 | // we don't want to load 32 bytes again in this loop if we know there are non-ASCII | - | ||||||
83 | // characters still coming | - | ||||||
84 | nextAscii = src + _bit_scan_reverse(n) + 1; | - | ||||||
85 | - | |||||||
86 | n = _bit_scan_forward(n); | - | ||||||
87 | dst += n; | - | ||||||
88 | src += n; | - | ||||||
89 | return false; | - | ||||||
90 | } | - | ||||||
91 | } | - | ||||||
92 | return src == end; | - | ||||||
93 | } | - | ||||||
94 | - | |||||||
95 | static inline bool simdDecodeAscii(ushort *&dst, const uchar *&nextAscii, const uchar *&src, const uchar *end) | - | ||||||
96 | { | - | ||||||
97 | // do sixteen characters at a time | - | ||||||
98 | for ( ; end - src >= 16; src += 16, dst += 16) { | - | ||||||
99 | __m128i data = _mm_loadu_si128((const __m128i*)src); | - | ||||||
100 | - | |||||||
101 | #ifdef __AVX2__ | - | ||||||
102 | const int BitSpacing = 2; | - | ||||||
103 | // load and zero extend to an YMM register | - | ||||||
104 | const __m256i extended = _mm256_cvtepu8_epi16(data); | - | ||||||
105 | - | |||||||
106 | uint n = _mm256_movemask_epi8(extended); | - | ||||||
107 | if (!n) { | - | ||||||
108 | // store | - | ||||||
109 | _mm256_storeu_si256((__m256i*)dst, extended); | - | ||||||
110 | continue; | - | ||||||
111 | } | - | ||||||
112 | #else | - | ||||||
113 | const int BitSpacing = 1; | - | ||||||
114 | - | |||||||
115 | // check if everything is ASCII | - | ||||||
116 | // movemask extracts the high bit of every byte, so n is non-zero if something isn't ASCII | - | ||||||
117 | uint n = _mm_movemask_epi8(data); | - | ||||||
118 | if (!n) { | - | ||||||
119 | // unpack | - | ||||||
120 | _mm_storeu_si128((__m128i*)dst, _mm_unpacklo_epi8(data, _mm_setzero_si128())); | - | ||||||
121 | _mm_storeu_si128(1+(__m128i*)dst, _mm_unpackhi_epi8(data, _mm_setzero_si128())); | - | ||||||
122 | continue; | - | ||||||
123 | } | - | ||||||
124 | #endif | - | ||||||
125 | - | |||||||
126 | // copy the front part that is still ASCII | - | ||||||
127 | while (!(n & 1)) { | - | ||||||
128 | *dst++ = *src++; | - | ||||||
129 | n >>= BitSpacing; | - | ||||||
130 | } | - | ||||||
131 | - | |||||||
132 | // find the next probable ASCII character | - | ||||||
133 | // we don't want to load 16 bytes again in this loop if we know there are non-ASCII | - | ||||||
134 | // characters still coming | - | ||||||
135 | n = _bit_scan_reverse(n); | - | ||||||
136 | nextAscii = src + (n / BitSpacing) + 1; | - | ||||||
137 | return false; | - | ||||||
138 | - | |||||||
139 | } | - | ||||||
140 | return src == end; | - | ||||||
141 | } | - | ||||||
142 | #else | - | ||||||
143 | static inline bool simdEncodeAscii(uchar *, const ushort *, const ushort *, const ushort *) | - | ||||||
144 | { | - | ||||||
145 | return false; | - | ||||||
146 | } | - | ||||||
147 | - | |||||||
148 | static inline bool simdDecodeAscii(ushort *, const uchar *, const uchar *, const uchar *) | - | ||||||
149 | { | - | ||||||
150 | return false; | - | ||||||
151 | } | - | ||||||
152 | #endif | - | ||||||
153 | - | |||||||
154 | QByteArray QUtf8::convertFromUnicode(const QChar *uc, int len) | - | ||||||
155 | { | - | ||||||
156 | // create a QByteArray with the worst case scenario size | - | ||||||
157 | QByteArray result(len * 3, Qt::Uninitialized); | - | ||||||
158 | uchar *dst = reinterpret_cast<uchar *>(const_cast<char *>(result.constData())); | - | ||||||
159 | const ushort *src = reinterpret_cast<const ushort *>(uc); | - | ||||||
160 | const ushort *const end = src + len; | - | ||||||
161 | - | |||||||
162 | while (src != end) { | - | ||||||
163 | const ushort *nextAscii = end; | - | ||||||
164 | if (simdEncodeAscii(dst, nextAscii, src, end)) | - | ||||||
165 | break; | - | ||||||
166 | - | |||||||
167 | do { | - | ||||||
168 | ushort uc = *src++; | - | ||||||
169 | int res = QUtf8Functions::toUtf8<QUtf8BaseTraits>(uc, dst, src, end); | - | ||||||
170 | if (res < 0) { | - | ||||||
171 | // encoding error - append '?' | - | ||||||
172 | *dst++ = '?'; | - | ||||||
173 | } | - | ||||||
174 | } while (src < nextAscii); | - | ||||||
175 | } | - | ||||||
176 | - | |||||||
177 | result.truncate(dst - reinterpret_cast<uchar *>(const_cast<char *>(result.constData()))); | - | ||||||
178 | return result; | - | ||||||
179 | } | - | ||||||
180 | - | |||||||
181 | QByteArray QUtf8::convertFromUnicode(const QChar *uc, int len, QTextCodec::ConverterState *state) | - | ||||||
182 | { | - | ||||||
183 | uchar replacement = '?'; | - | ||||||
184 | int rlen = 3*len; | - | ||||||
185 | int surrogate_high = -1; | - | ||||||
186 | if (state) { | - | ||||||
187 | if (state->flags & QTextCodec::ConvertInvalidToNull) | - | ||||||
188 | replacement = 0; | - | ||||||
189 | if (!(state->flags & QTextCodec::IgnoreHeader)) | - | ||||||
190 | rlen += 3; | - | ||||||
191 | if (state->remainingChars) | - | ||||||
192 | surrogate_high = state->state_data[0]; | - | ||||||
193 | } | - | ||||||
194 | - | |||||||
195 | - | |||||||
196 | QByteArray rstr(rlen, Qt::Uninitialized); | - | ||||||
197 | uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(rstr.constData())); | - | ||||||
198 | const ushort *src = reinterpret_cast<const ushort *>(uc); | - | ||||||
199 | const ushort *const end = src + len; | - | ||||||
200 | - | |||||||
201 | int invalid = 0; | - | ||||||
202 | if (state && !(state->flags & QTextCodec::IgnoreHeader)) { | - | ||||||
203 | // append UTF-8 BOM | - | ||||||
204 | *cursor++ = utf8bom[0]; | - | ||||||
205 | *cursor++ = utf8bom[1]; | - | ||||||
206 | *cursor++ = utf8bom[2]; | - | ||||||
207 | } | - | ||||||
208 | - | |||||||
209 | const ushort *nextAscii = src; | - | ||||||
210 | while (src != end) { | - | ||||||
211 | int res; | - | ||||||
212 | ushort uc; | - | ||||||
213 | if (surrogate_high != -1) { | - | ||||||
214 | uc = surrogate_high; | - | ||||||
215 | surrogate_high = -1; | - | ||||||
216 | res = QUtf8Functions::toUtf8<QUtf8BaseTraits>(uc, cursor, src, end); | - | ||||||
217 | } else { | - | ||||||
218 | if (src >= nextAscii && simdEncodeAscii(cursor, nextAscii, src, end)) | - | ||||||
219 | break; | - | ||||||
220 | - | |||||||
221 | uc = *src++; | - | ||||||
222 | res = QUtf8Functions::toUtf8<QUtf8BaseTraits>(uc, cursor, src, end); | - | ||||||
223 | } | - | ||||||
224 | if (Q_LIKELY(res >= 0)) | - | ||||||
225 | continue; | - | ||||||
226 | - | |||||||
227 | if (res == QUtf8BaseTraits::Error) { | - | ||||||
228 | // encoding error | - | ||||||
229 | ++invalid; | - | ||||||
230 | *cursor++ = replacement; | - | ||||||
231 | } else if (res == QUtf8BaseTraits::EndOfString) { | - | ||||||
232 | surrogate_high = uc; | - | ||||||
233 | break; | - | ||||||
234 | } | - | ||||||
235 | } | - | ||||||
236 | - | |||||||
237 | rstr.resize(cursor - (const uchar*)rstr.constData()); | - | ||||||
238 | if (state) { | - | ||||||
239 | state->invalidChars += invalid; | - | ||||||
240 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
241 | state->remainingChars = 0; | - | ||||||
242 | if (surrogate_high >= 0) { | - | ||||||
243 | state->remainingChars = 1; | - | ||||||
244 | state->state_data[0] = surrogate_high; | - | ||||||
245 | } | - | ||||||
246 | } | - | ||||||
247 | return rstr; | - | ||||||
248 | } | - | ||||||
249 | - | |||||||
250 | QString QUtf8::convertToUnicode(const char *chars, int len) | - | ||||||
251 | { | - | ||||||
252 | // UTF-8 to UTF-16 always needs the exact same number of words or less: | - | ||||||
253 | // UTF-8 UTF-16 | - | ||||||
254 | // 1 byte 1 word | - | ||||||
255 | // 2 bytes 1 word | - | ||||||
256 | // 3 bytes 1 word | - | ||||||
257 | // 4 bytes 2 words (one surrogate pair) | - | ||||||
258 | // That is, we'll use the full buffer if the input is US-ASCII (1-byte UTF-8), | - | ||||||
259 | // half the buffer for U+0080-U+07FF text (e.g., Greek, Cyrillic, Arabic) or | - | ||||||
260 | // non-BMP text, and one third of the buffer for U+0800-U+FFFF text (e.g, CJK). | - | ||||||
261 | // | - | ||||||
262 | // The table holds for invalid sequences too: we'll insert one replacement char | - | ||||||
263 | // per invalid byte. | - | ||||||
264 | QString result(len, Qt::Uninitialized); | - | ||||||
265 | ushortQChar *dstdata = reinterpret_cast<ushort *>(const_cast<QChar*>(result.constData()));()); // we know we're not shared | - | ||||||
266 | const QChar *end = convertToUnicode(data, chars, len); | - | ||||||
267 | result.truncate(end - data); | - | ||||||
268 | return result; executed 3930821 times by 532 tests: return result; Executed by:
| 3930821 | ||||||
269 | } | - | ||||||
270 | - | |||||||
271 | /*! | - | ||||||
272 | \since 5.7 | - | ||||||
273 | \overload | - | ||||||
274 | - | |||||||
275 | Converts the UTF-8 sequence of \a len octets beginning at \a chars to | - | ||||||
276 | a sequence of QChar starting at \a buffer. The buffer is expected to be | - | ||||||
277 | large enough to hold the result. An upper bound for the size of the | - | ||||||
278 | buffer is \a len QChars. | - | ||||||
279 | - | |||||||
280 | If, during decoding, an error occurs, a QChar::ReplacementCharacter is | - | ||||||
281 | written. | - | ||||||
282 | - | |||||||
283 | Returns a pointer to one past the last QChar written. | - | ||||||
284 | - | |||||||
285 | This function never throws. | - | ||||||
286 | */ | - | ||||||
287 | - | |||||||
288 | QChar *QUtf8::convertToUnicode(QChar *buffer, const char *chars, int len) Q_DECL_NOTHROW | - | ||||||
289 | { | - | ||||||
290 | ushort *dst = reinterpret_cast<ushort *>(buffer); | - | ||||||
291 | const uchar *src = reinterpret_cast<const uchar *>(chars); | - | ||||||
292 | const uchar *end = src + len; | - | ||||||
293 | - | |||||||
294 | // attempt to do a full decoding in SIMD | - | ||||||
295 | const uchar *nextAscii = end; | - | ||||||
296 | if (!simdDecodeAscii(dst, nextAscii, src, end)) {
| 598854-4015432 | ||||||
297 | // at least one non-ASCII entry | - | ||||||
298 | // check if we failed to decode the UTF-8 BOM; if so, skip it | - | ||||||
299 | if (Q_UNLIKELY(src == reinterpret_cast<const uchar *>(chars))
| 535942-3479490 | ||||||
300 | && end - src >= 3
| 1605604-1873886 | ||||||
301 | && Q_UNLIKELY(src[0] == utf8bom[0] && src[1] == utf8bom[1] && src[2] == utf8bom[2])) {
| 5-1873881 | ||||||
302 | src += 3; | - | ||||||
303 | } executed 5 times by 2 tests: end of block Executed by:
| 5 | ||||||
304 | - | |||||||
305 | while (src < end) {
| 4015424-4064914 | ||||||
306 | nextAscii = end; | - | ||||||
307 | if (simdDecodeAscii(dst, nextAscii, src, end))
| 8-4064906 | ||||||
308 | break; executed 8 times by 1 test: break; Executed by:
| 8 | ||||||
309 | - | |||||||
310 | do { | - | ||||||
311 | uchar b = *src++; | - | ||||||
312 | int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, dst, src, end); | - | ||||||
313 | if (res < 0) {
| 530-17941153 | ||||||
314 | // decoding error | - | ||||||
315 | *dst++ = QChar::ReplacementCharacter; | - | ||||||
316 | } executed 530 times by 5 tests: end of block Executed by:
| 530 | ||||||
317 | } while (src < nextAscii); executed 17941683 times by 546 tests: end of block Executed by:
| 4064906-17941683 | ||||||
318 | } executed 4064906 times by 546 tests: end of block Executed by:
| 4064906 | ||||||
319 | } executed 4015432 times by 546 tests: end of block Executed by:
| 4015432 | ||||||
320 | - | |||||||
321 | result.truncate(dst -return reinterpret_cast<const ushortQChar *>(result.constData())); executed 4614286 times by 546 tests: return reinterpret_cast<QChar *>(dst); Executed by:
| 4614286 | ||||||
return result; executed 4614286 times by 546 tests: dst);return reinterpret_cast<QChar *>(dst); Executed by:
executed 4614286 times by 546 tests: return reinterpret_cast<QChar *>(dst); Executed by:
| ||||||||
322 | } | - | ||||||
323 | - | |||||||
324 | QString QUtf8::convertToUnicode(const char *chars, int len, QTextCodec::ConverterState *state) | - | ||||||
325 | { | - | ||||||
326 | bool headerdone = false; | - | ||||||
327 | ushort replacement = QChar::ReplacementCharacter; | - | ||||||
328 | int invalid = 0; | - | ||||||
329 | int res; | - | ||||||
330 | uchar ch = 0; | - | ||||||
331 | - | |||||||
332 | // See above for buffer requirements for stateless decoding. However, that | - | ||||||
333 | // fails if the state is not empty. The following situations can add to the | - | ||||||
334 | // requirements: | - | ||||||
335 | // state contains chars starts with requirement | - | ||||||
336 | // 1 of 2 bytes valid continuation 0 | - | ||||||
337 | // 2 of 3 bytes same 0 | - | ||||||
338 | // 3 bytes of 4 same +1 (need to insert surrogate pair) | - | ||||||
339 | // 1 of 2 bytes invalid continuation +1 (need to insert replacement and restart) | - | ||||||
340 | // 2 of 3 bytes same +1 (same) | - | ||||||
341 | // 3 of 4 bytes same +1 (same) | - | ||||||
342 | QString result(len + 1, Qt::Uninitialized); | - | ||||||
343 | - | |||||||
344 | ushort *dst = reinterpret_cast<ushort *>(const_cast<QChar *>(result.constData())); | - | ||||||
345 | const uchar *src = reinterpret_cast<const uchar *>(chars); | - | ||||||
346 | const uchar *end = src + len; | - | ||||||
347 | - | |||||||
348 | if (state) { | - | ||||||
349 | if (state->flags & QTextCodec::IgnoreHeader) | - | ||||||
350 | headerdone = true; | - | ||||||
351 | if (state->flags & QTextCodec::ConvertInvalidToNull) | - | ||||||
352 | replacement = QChar::Null; | - | ||||||
353 | if (state->remainingChars) { | - | ||||||
354 | // handle incoming state first | - | ||||||
355 | uchar remainingCharsData[4]; // longest UTF-8 sequence possible | - | ||||||
356 | int remainingCharsCount = state->remainingChars; | - | ||||||
357 | int newCharsToCopy = qMin<int>(sizeof(remainingCharsData) - remainingCharsCount, end - src); | - | ||||||
358 | - | |||||||
359 | memset(remainingCharsData, 0, sizeof(remainingCharsData)); | - | ||||||
360 | memcpy(remainingCharsData, &state->state_data[0], remainingCharsCount); | - | ||||||
361 | memcpy(remainingCharsData + remainingCharsCount, src, newCharsToCopy); | - | ||||||
362 | - | |||||||
363 | const uchar *begin = &remainingCharsData[1]; | - | ||||||
364 | res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(remainingCharsData[0], dst, begin, | - | ||||||
365 | static_cast<const uchar *>(remainingCharsData) + remainingCharsCount + newCharsToCopy); | - | ||||||
366 | if (res == QUtf8BaseTraits::Error || (res == QUtf8BaseTraits::EndOfString && len == 0)) { | - | ||||||
367 | // special case for len == 0: | - | ||||||
368 | // if we were supplied an empty string, terminate the previous, unfinished sequence with error | - | ||||||
369 | ++invalid; | - | ||||||
370 | *dst++ = replacement; | - | ||||||
371 | } else if (res == QUtf8BaseTraits::EndOfString) { | - | ||||||
372 | // if we got EndOfString again, then there were too few bytes in src; | - | ||||||
373 | // copy to our state and return | - | ||||||
374 | state->remainingChars = remainingCharsCount + newCharsToCopy; | - | ||||||
375 | memcpy(&state->state_data[0], remainingCharsData, state->remainingChars); | - | ||||||
376 | return QString(); | - | ||||||
377 | } else if (!headerdone && res >= 0) { | - | ||||||
378 | // eat the UTF-8 BOM | - | ||||||
379 | headerdone = true; | - | ||||||
380 | if (dst[-1] == 0xfeff) | - | ||||||
381 | --dst; | - | ||||||
382 | } | - | ||||||
383 | - | |||||||
384 | // adjust src now that we have maybe consumed a few chars | - | ||||||
385 | if (res >= 0) { | - | ||||||
386 | Q_ASSERT(res > remainingCharsCount); | - | ||||||
387 | src += res - remainingCharsCount; | - | ||||||
388 | } | - | ||||||
389 | } | - | ||||||
390 | } | - | ||||||
391 | - | |||||||
392 | // main body, stateless decoding | - | ||||||
393 | res = 0; | - | ||||||
394 | const uchar *nextAscii = src; | - | ||||||
395 | const uchar *start = src; | - | ||||||
396 | while (res >= 0 && src < end) { | - | ||||||
397 | if (src >= nextAscii && simdDecodeAscii(dst, nextAscii, src, end)) | - | ||||||
398 | break; | - | ||||||
399 | - | |||||||
400 | ch = *src++; | - | ||||||
401 | res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(ch, dst, src, end); | - | ||||||
402 | if (!headerdone && res >= 0) { | - | ||||||
403 | headerdone = true; | - | ||||||
404 | if (src == start + 3) { // 3 == sizeof(utf8-bom) | - | ||||||
405 | // eat the UTF-8 BOM (it can only appear at the beginning of the string). | - | ||||||
406 | if (dst[-1] == 0xfeff) | - | ||||||
407 | --dst; | - | ||||||
408 | } | - | ||||||
409 | } | - | ||||||
410 | if (res == QUtf8BaseTraits::Error) { | - | ||||||
411 | res = 0; | - | ||||||
412 | ++invalid; | - | ||||||
413 | *dst++ = replacement; | - | ||||||
414 | } | - | ||||||
415 | } | - | ||||||
416 | - | |||||||
417 | if (!state && res == QUtf8BaseTraits::EndOfString) { | - | ||||||
418 | // unterminated UTF sequence | - | ||||||
419 | *dst++ = QChar::ReplacementCharacter; | - | ||||||
420 | while (src++ < end) | - | ||||||
421 | *dst++ = QChar::ReplacementCharacter; | - | ||||||
422 | } | - | ||||||
423 | - | |||||||
424 | result.truncate(dst - (const ushort *)result.unicode()); | - | ||||||
425 | if (state) { | - | ||||||
426 | state->invalidChars += invalid; | - | ||||||
427 | if (headerdone) | - | ||||||
428 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
429 | if (res == QUtf8BaseTraits::EndOfString) { | - | ||||||
430 | --src; // unread the byte in ch | - | ||||||
431 | state->remainingChars = end - src; | - | ||||||
432 | memcpy(&state->state_data[0], src, end - src); | - | ||||||
433 | } else { | - | ||||||
434 | state->remainingChars = 0; | - | ||||||
435 | } | - | ||||||
436 | } | - | ||||||
437 | return result; | - | ||||||
438 | } | - | ||||||
439 | - | |||||||
440 | QByteArray QUtf16::convertFromUnicode(const QChar *uc, int len, QTextCodec::ConverterState *state, DataEndianness e) | - | ||||||
441 | { | - | ||||||
442 | DataEndianness endian = e; | - | ||||||
443 | int length = 2*len; | - | ||||||
444 | if (!state || (!(state->flags & QTextCodec::IgnoreHeader))) { | - | ||||||
445 | length += 2; | - | ||||||
446 | } | - | ||||||
447 | if (e == DetectEndianness) { | - | ||||||
448 | endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) ? BigEndianness : LittleEndianness; | - | ||||||
449 | } | - | ||||||
450 | - | |||||||
451 | QByteArray d; | - | ||||||
452 | d.resize(length); | - | ||||||
453 | char *data = d.data(); | - | ||||||
454 | if (!state || !(state->flags & QTextCodec::IgnoreHeader)) { | - | ||||||
455 | QChar bom(QChar::ByteOrderMark); | - | ||||||
456 | if (endian == BigEndianness) { | - | ||||||
457 | data[0] = bom.row(); | - | ||||||
458 | data[1] = bom.cell(); | - | ||||||
459 | } else { | - | ||||||
460 | data[0] = bom.cell(); | - | ||||||
461 | data[1] = bom.row(); | - | ||||||
462 | } | - | ||||||
463 | data += 2; | - | ||||||
464 | } | - | ||||||
465 | if (endian == BigEndianness) { | - | ||||||
466 | for (int i = 0; i < len; ++i) { | - | ||||||
467 | *(data++) = uc[i].row(); | - | ||||||
468 | *(data++) = uc[i].cell(); | - | ||||||
469 | } | - | ||||||
470 | } else { | - | ||||||
471 | for (int i = 0; i < len; ++i) { | - | ||||||
472 | *(data++) = uc[i].cell(); | - | ||||||
473 | *(data++) = uc[i].row(); | - | ||||||
474 | } | - | ||||||
475 | } | - | ||||||
476 | - | |||||||
477 | if (state) { | - | ||||||
478 | state->remainingChars = 0; | - | ||||||
479 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
480 | } | - | ||||||
481 | return d; | - | ||||||
482 | } | - | ||||||
483 | - | |||||||
484 | QString QUtf16::convertToUnicode(const char *chars, int len, QTextCodec::ConverterState *state, DataEndianness e) | - | ||||||
485 | { | - | ||||||
486 | DataEndianness endian = e; | - | ||||||
487 | bool half = false; | - | ||||||
488 | uchar buf = 0; | - | ||||||
489 | bool headerdone = false; | - | ||||||
490 | if (state) { | - | ||||||
491 | headerdone = state->flags & QTextCodec::IgnoreHeader; | - | ||||||
492 | if (endian == DetectEndianness) | - | ||||||
493 | endian = (DataEndianness)state->state_data[Endian]; | - | ||||||
494 | if (state->remainingChars) { | - | ||||||
495 | half = true; | - | ||||||
496 | buf = state->state_data[Data]; | - | ||||||
497 | } | - | ||||||
498 | } | - | ||||||
499 | if (headerdone && endian == DetectEndianness) | - | ||||||
500 | endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) ? BigEndianness : LittleEndianness; | - | ||||||
501 | - | |||||||
502 | QString result(len, Qt::Uninitialized); // worst case | - | ||||||
503 | QChar *qch = (QChar *)result.data(); | - | ||||||
504 | while (len--) { | - | ||||||
505 | if (half) { | - | ||||||
506 | QChar ch; | - | ||||||
507 | if (endian == LittleEndianness) { | - | ||||||
508 | ch.setRow(*chars++); | - | ||||||
509 | ch.setCell(buf); | - | ||||||
510 | } else { | - | ||||||
511 | ch.setRow(buf); | - | ||||||
512 | ch.setCell(*chars++); | - | ||||||
513 | } | - | ||||||
514 | if (!headerdone) { | - | ||||||
515 | headerdone = true; | - | ||||||
516 | if (endian == DetectEndianness) { | - | ||||||
517 | if (ch == QChar::ByteOrderSwapped) { | - | ||||||
518 | endian = LittleEndianness; | - | ||||||
519 | } else if (ch == QChar::ByteOrderMark) { | - | ||||||
520 | endian = BigEndianness; | - | ||||||
521 | } else { | - | ||||||
522 | if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { | - | ||||||
523 | endian = BigEndianness; | - | ||||||
524 | } else { | - | ||||||
525 | endian = LittleEndianness; | - | ||||||
526 | ch = QChar((ch.unicode() >> 8) | ((ch.unicode() & 0xff) << 8)); | - | ||||||
527 | } | - | ||||||
528 | *qch++ = ch; | - | ||||||
529 | } | - | ||||||
530 | } else if (ch != QChar::ByteOrderMark) { | - | ||||||
531 | *qch++ = ch; | - | ||||||
532 | } | - | ||||||
533 | } else { | - | ||||||
534 | *qch++ = ch; | - | ||||||
535 | } | - | ||||||
536 | half = false; | - | ||||||
537 | } else { | - | ||||||
538 | buf = *chars++; | - | ||||||
539 | half = true; | - | ||||||
540 | } | - | ||||||
541 | } | - | ||||||
542 | result.truncate(qch - result.unicode()); | - | ||||||
543 | - | |||||||
544 | if (state) { | - | ||||||
545 | if (headerdone) | - | ||||||
546 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
547 | state->state_data[Endian] = endian; | - | ||||||
548 | if (half) { | - | ||||||
549 | state->remainingChars = 1; | - | ||||||
550 | state->state_data[Data] = buf; | - | ||||||
551 | } else { | - | ||||||
552 | state->remainingChars = 0; | - | ||||||
553 | state->state_data[Data] = 0; | - | ||||||
554 | } | - | ||||||
555 | } | - | ||||||
556 | return result; | - | ||||||
557 | } | - | ||||||
558 | - | |||||||
559 | QByteArray QUtf32::convertFromUnicode(const QChar *uc, int len, QTextCodec::ConverterState *state, DataEndianness e) | - | ||||||
560 | { | - | ||||||
561 | DataEndianness endian = e; | - | ||||||
562 | int length = 4*len; | - | ||||||
563 | if (!state || (!(state->flags & QTextCodec::IgnoreHeader))) { | - | ||||||
564 | length += 4; | - | ||||||
565 | } | - | ||||||
566 | if (e == DetectEndianness) { | - | ||||||
567 | endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) ? BigEndianness : LittleEndianness; | - | ||||||
568 | } | - | ||||||
569 | - | |||||||
570 | QByteArray d(length, Qt::Uninitialized); | - | ||||||
571 | char *data = d.data(); | - | ||||||
572 | if (!state || !(state->flags & QTextCodec::IgnoreHeader)) { | - | ||||||
573 | if (endian == BigEndianness) { | - | ||||||
574 | data[0] = 0; | - | ||||||
575 | data[1] = 0; | - | ||||||
576 | data[2] = (char)0xfe; | - | ||||||
577 | data[3] = (char)0xff; | - | ||||||
578 | } else { | - | ||||||
579 | data[0] = (char)0xff; | - | ||||||
580 | data[1] = (char)0xfe; | - | ||||||
581 | data[2] = 0; | - | ||||||
582 | data[3] = 0; | - | ||||||
583 | } | - | ||||||
584 | data += 4; | - | ||||||
585 | } | - | ||||||
586 | - | |||||||
587 | QStringIterator i(uc, uc + len); | - | ||||||
588 | if (endian == BigEndianness) { | - | ||||||
589 | while (i.hasNext()) { | - | ||||||
590 | uint cp = i.next(); | - | ||||||
591 | - | |||||||
592 | *(data++) = cp >> 24; | - | ||||||
593 | *(data++) = (cp >> 16) & 0xff; | - | ||||||
594 | *(data++) = (cp >> 8) & 0xff; | - | ||||||
595 | *(data++) = cp & 0xff; | - | ||||||
596 | } | - | ||||||
597 | } else { | - | ||||||
598 | while (i.hasNext()) { | - | ||||||
599 | uint cp = i.next(); | - | ||||||
600 | - | |||||||
601 | *(data++) = cp & 0xff; | - | ||||||
602 | *(data++) = (cp >> 8) & 0xff; | - | ||||||
603 | *(data++) = (cp >> 16) & 0xff; | - | ||||||
604 | *(data++) = cp >> 24; | - | ||||||
605 | } | - | ||||||
606 | } | - | ||||||
607 | - | |||||||
608 | if (state) { | - | ||||||
609 | state->remainingChars = 0; | - | ||||||
610 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
611 | } | - | ||||||
612 | return d; | - | ||||||
613 | } | - | ||||||
614 | - | |||||||
615 | QString QUtf32::convertToUnicode(const char *chars, int len, QTextCodec::ConverterState *state, DataEndianness e) | - | ||||||
616 | { | - | ||||||
617 | DataEndianness endian = e; | - | ||||||
618 | uchar tuple[4]; | - | ||||||
619 | int num = 0; | - | ||||||
620 | bool headerdone = false; | - | ||||||
621 | if (state) { | - | ||||||
622 | headerdone = state->flags & QTextCodec::IgnoreHeader; | - | ||||||
623 | if (endian == DetectEndianness) { | - | ||||||
624 | endian = (DataEndianness)state->state_data[Endian]; | - | ||||||
625 | } | - | ||||||
626 | num = state->remainingChars; | - | ||||||
627 | memcpy(tuple, &state->state_data[Data], 4); | - | ||||||
628 | } | - | ||||||
629 | if (headerdone && endian == DetectEndianness) | - | ||||||
630 | endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) ? BigEndianness : LittleEndianness; | - | ||||||
631 | - | |||||||
632 | QString result; | - | ||||||
633 | result.resize((num + len) >> 2 << 1); // worst case | - | ||||||
634 | QChar *qch = (QChar *)result.data(); | - | ||||||
635 | - | |||||||
636 | const char *end = chars + len; | - | ||||||
637 | while (chars < end) { | - | ||||||
638 | tuple[num++] = *chars++; | - | ||||||
639 | if (num == 4) { | - | ||||||
640 | if (!headerdone) { | - | ||||||
641 | if (endian == DetectEndianness) { | - | ||||||
642 | if (tuple[0] == 0xff && tuple[1] == 0xfe && tuple[2] == 0 && tuple[3] == 0 && endian != BigEndianness) { | - | ||||||
643 | endian = LittleEndianness; | - | ||||||
644 | num = 0; | - | ||||||
645 | continue; | - | ||||||
646 | } else if (tuple[0] == 0 && tuple[1] == 0 && tuple[2] == 0xfe && tuple[3] == 0xff && endian != LittleEndianness) { | - | ||||||
647 | endian = BigEndianness; | - | ||||||
648 | num = 0; | - | ||||||
649 | continue; | - | ||||||
650 | } else if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { | - | ||||||
651 | endian = BigEndianness; | - | ||||||
652 | } else { | - | ||||||
653 | endian = LittleEndianness; | - | ||||||
654 | } | - | ||||||
655 | } else if (((endian == BigEndianness) ? qFromBigEndian<quint32>(tuple) : qFromLittleEndian<quint32>(tuple)) == QChar::ByteOrderMark) { | - | ||||||
656 | num = 0; | - | ||||||
657 | continue; | - | ||||||
658 | } | - | ||||||
659 | } | - | ||||||
660 | uint code = (endian == BigEndianness) ? qFromBigEndian<quint32>(tuple) : qFromLittleEndian<quint32>(tuple); | - | ||||||
661 | if (QChar::requiresSurrogates(code)) { | - | ||||||
662 | *qch++ = QChar::highSurrogate(code); | - | ||||||
663 | *qch++ = QChar::lowSurrogate(code); | - | ||||||
664 | } else { | - | ||||||
665 | *qch++ = code; | - | ||||||
666 | } | - | ||||||
667 | num = 0; | - | ||||||
668 | } | - | ||||||
669 | } | - | ||||||
670 | result.truncate(qch - result.unicode()); | - | ||||||
671 | - | |||||||
672 | if (state) { | - | ||||||
673 | if (headerdone) | - | ||||||
674 | state->flags |= QTextCodec::IgnoreHeader; | - | ||||||
675 | state->state_data[Endian] = endian; | - | ||||||
676 | state->remainingChars = num; | - | ||||||
677 | memcpy(&state->state_data[Data], tuple, 4); | - | ||||||
678 | } | - | ||||||
679 | return result; | - | ||||||
680 | } | - | ||||||
681 | - | |||||||
682 | - | |||||||
683 | #ifndef QT_NO_TEXTCODEC | - | ||||||
684 | - | |||||||
685 | QUtf8Codec::~QUtf8Codec() | - | ||||||
686 | { | - | ||||||
687 | } | - | ||||||
688 | - | |||||||
689 | QByteArray QUtf8Codec::convertFromUnicode(const QChar *uc, int len, ConverterState *state) const | - | ||||||
690 | { | - | ||||||
691 | return QUtf8::convertFromUnicode(uc, len, state); | - | ||||||
692 | } | - | ||||||
693 | - | |||||||
694 | void QUtf8Codec::convertToUnicode(QString *target, const char *chars, int len, ConverterState *state) const | - | ||||||
695 | { | - | ||||||
696 | *target += QUtf8::convertToUnicode(chars, len, state); | - | ||||||
697 | } | - | ||||||
698 | - | |||||||
699 | QString QUtf8Codec::convertToUnicode(const char *chars, int len, ConverterState *state) const | - | ||||||
700 | { | - | ||||||
701 | return QUtf8::convertToUnicode(chars, len, state); | - | ||||||
702 | } | - | ||||||
703 | - | |||||||
704 | QByteArray QUtf8Codec::name() const | - | ||||||
705 | { | - | ||||||
706 | return "UTF-8"; | - | ||||||
707 | } | - | ||||||
708 | - | |||||||
709 | int QUtf8Codec::mibEnum() const | - | ||||||
710 | { | - | ||||||
711 | return 106; | - | ||||||
712 | } | - | ||||||
713 | - | |||||||
714 | QUtf16Codec::~QUtf16Codec() | - | ||||||
715 | { | - | ||||||
716 | } | - | ||||||
717 | - | |||||||
718 | QByteArray QUtf16Codec::convertFromUnicode(const QChar *uc, int len, ConverterState *state) const | - | ||||||
719 | { | - | ||||||
720 | return QUtf16::convertFromUnicode(uc, len, state, e); | - | ||||||
721 | } | - | ||||||
722 | - | |||||||
723 | QString QUtf16Codec::convertToUnicode(const char *chars, int len, ConverterState *state) const | - | ||||||
724 | { | - | ||||||
725 | return QUtf16::convertToUnicode(chars, len, state, e); | - | ||||||
726 | } | - | ||||||
727 | - | |||||||
728 | int QUtf16Codec::mibEnum() const | - | ||||||
729 | { | - | ||||||
730 | return 1015; | - | ||||||
731 | } | - | ||||||
732 | - | |||||||
733 | QByteArray QUtf16Codec::name() const | - | ||||||
734 | { | - | ||||||
735 | return "UTF-16"; | - | ||||||
736 | } | - | ||||||
737 | - | |||||||
738 | QList<QByteArray> QUtf16Codec::aliases() const | - | ||||||
739 | { | - | ||||||
740 | return QList<QByteArray>(); | - | ||||||
741 | } | - | ||||||
742 | - | |||||||
743 | int QUtf16BECodec::mibEnum() const | - | ||||||
744 | { | - | ||||||
745 | return 1013; | - | ||||||
746 | } | - | ||||||
747 | - | |||||||
748 | QByteArray QUtf16BECodec::name() const | - | ||||||
749 | { | - | ||||||
750 | return "UTF-16BE"; | - | ||||||
751 | } | - | ||||||
752 | - | |||||||
753 | QList<QByteArray> QUtf16BECodec::aliases() const | - | ||||||
754 | { | - | ||||||
755 | QList<QByteArray> list; | - | ||||||
756 | return list; | - | ||||||
757 | } | - | ||||||
758 | - | |||||||
759 | int QUtf16LECodec::mibEnum() const | - | ||||||
760 | { | - | ||||||
761 | return 1014; | - | ||||||
762 | } | - | ||||||
763 | - | |||||||
764 | QByteArray QUtf16LECodec::name() const | - | ||||||
765 | { | - | ||||||
766 | return "UTF-16LE"; | - | ||||||
767 | } | - | ||||||
768 | - | |||||||
769 | QList<QByteArray> QUtf16LECodec::aliases() const | - | ||||||
770 | { | - | ||||||
771 | QList<QByteArray> list; | - | ||||||
772 | return list; | - | ||||||
773 | } | - | ||||||
774 | - | |||||||
775 | QUtf32Codec::~QUtf32Codec() | - | ||||||
776 | { | - | ||||||
777 | } | - | ||||||
778 | - | |||||||
779 | QByteArray QUtf32Codec::convertFromUnicode(const QChar *uc, int len, ConverterState *state) const | - | ||||||
780 | { | - | ||||||
781 | return QUtf32::convertFromUnicode(uc, len, state, e); | - | ||||||
782 | } | - | ||||||
783 | - | |||||||
784 | QString QUtf32Codec::convertToUnicode(const char *chars, int len, ConverterState *state) const | - | ||||||
785 | { | - | ||||||
786 | return QUtf32::convertToUnicode(chars, len, state, e); | - | ||||||
787 | } | - | ||||||
788 | - | |||||||
789 | int QUtf32Codec::mibEnum() const | - | ||||||
790 | { | - | ||||||
791 | return 1017; | - | ||||||
792 | } | - | ||||||
793 | - | |||||||
794 | QByteArray QUtf32Codec::name() const | - | ||||||
795 | { | - | ||||||
796 | return "UTF-32"; | - | ||||||
797 | } | - | ||||||
798 | - | |||||||
799 | QList<QByteArray> QUtf32Codec::aliases() const | - | ||||||
800 | { | - | ||||||
801 | QList<QByteArray> list; | - | ||||||
802 | return list; | - | ||||||
803 | } | - | ||||||
804 | - | |||||||
805 | int QUtf32BECodec::mibEnum() const | - | ||||||
806 | { | - | ||||||
807 | return 1018; | - | ||||||
808 | } | - | ||||||
809 | - | |||||||
810 | QByteArray QUtf32BECodec::name() const | - | ||||||
811 | { | - | ||||||
812 | return "UTF-32BE"; | - | ||||||
813 | } | - | ||||||
814 | - | |||||||
815 | QList<QByteArray> QUtf32BECodec::aliases() const | - | ||||||
816 | { | - | ||||||
817 | QList<QByteArray> list; | - | ||||||
818 | return list; | - | ||||||
819 | } | - | ||||||
820 | - | |||||||
821 | int QUtf32LECodec::mibEnum() const | - | ||||||
822 | { | - | ||||||
823 | return 1019; | - | ||||||
824 | } | - | ||||||
825 | - | |||||||
826 | QByteArray QUtf32LECodec::name() const | - | ||||||
827 | { | - | ||||||
828 | return "UTF-32LE"; | - | ||||||
829 | } | - | ||||||
830 | - | |||||||
831 | QList<QByteArray> QUtf32LECodec::aliases() const | - | ||||||
832 | { | - | ||||||
833 | QList<QByteArray> list; | - | ||||||
834 | return list; | - | ||||||
835 | } | - | ||||||
836 | - | |||||||
837 | #endif //QT_NO_TEXTCODEC | - | ||||||
838 | - | |||||||
839 | QT_END_NAMESPACE | - | ||||||
Source code | Switch to Preprocessed file |