Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
UTFConvert.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
8#include "Stroika/Foundation/Execution/Exceptions.h"
9
10#include "UTFConvert.h"
11
12using namespace Stroika::Foundation;
14
15namespace {
16 // crazy - from https://en.cppreference.com/w/cpp/locale/codecvt
17 template <typename FACET>
18 struct deletable_facet_ : FACET {
19 template <typename... Args>
20 deletable_facet_ (Args&&... args)
21 : FACET{forward<Args> (args)...}
22 {
23 }
24 ~deletable_facet_ ()
25 {
26 }
27 };
28}
29
30/**
31 * BASED on
32 * https://github.com/codebrainz/libutfxx/blob/master/utf/ConvertUTF.h
33 * https://github.com/codebrainz/libutfxx/blob/master/utf/ConvertUTF.c
34 * http://docs.ros.org/lunar/api/rtabmap/html/ConvertUTF_8h_source.html,
35 *
36 * but updated for C++.
37 *
38 * Copyright 2001-2004 Unicode, Inc.
39 *
40 * Disclaimer
41 *
42 * This source code is provided as is by Unicode, Inc. No claims are
43 * made as to fitness for any particular purpose. No warranties of any
44 * kind are expressed or implied. The recipient agrees to determine
45 * applicability of information provided. If this file has been
46 * purchased on magnetic or optical media from Unicode, Inc., the
47 * sole remedy for any claim will be exchange of defective media
48 * within 90 days of receipt.
49 *
50 * Limitations on Rights to Redistribute This Code
51 *
52 * Unicode, Inc. hereby grants the right to freely use the information
53 * supplied in this file in the creation of products supporting the
54 * Unicode Standard, and to make copies of this file in any form
55 * for internal or external distribution as long as this notice
56 * remains attached.
57 *
58 *
59 * Author: Mark E. Davis, 1994.
60 * Rev History: Rick McGowan, fixes & updates May 2001.
61 * Fixes & updates, Sept 2001.
62 */
63namespace {
64 namespace UTFConvert_libutfxx_ {
65 // This codec deliberately mixes char8_t/char16_t/char32_t while packing/unpacking encoded bytes and
66 // code units (that's the whole point of a UTF converter) - so -Wcharacter-conversion's heuristic (a
67 // useful check in ordinary code) is just noise here.
68#if defined(__clang_major__) and (20 < __clang_major__)
69 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wcharacter-conversion\"")
70#endif
71
72 // static constexpr char32_t UNI_REPLACEMENT_CHAR = (char32_t)0x0000FFFD;
73 static constexpr char32_t UNI_MAX_BMP = (char32_t)0x0000FFFF;
74 static constexpr char32_t UNI_MAX_UTF16 = (char32_t)0x0010FFFF;
75 static constexpr char32_t UNI_MAX_LEGAL_UTF32 = (char32_t)0x0010FFFF;
76
77 enum ConversionResult {
78 conversionOK, /* conversion successful */
79 sourceExhausted, /* partial character in source, but hit end */
80 targetExhausted, /* insuff. room in target for conversion */
81 sourceIllegal /* source sequence is illegal/malformed */
82 };
83
84 UTFConvert::ConversionStatusFlag cvt_ (ConversionResult cr)
85 {
86 switch (cr) {
87 case conversionOK:
88 return UTFConvert::ConversionStatusFlag::ok;
89 case sourceExhausted:
90 return UTFConvert::ConversionStatusFlag::sourceExhausted;
91 case targetExhausted:
92 RequireNotReached (); // API doesn't allow this
93 return UTFConvert::ConversionStatusFlag::sourceIllegal;
94 case sourceIllegal:
95 return UTFConvert::ConversionStatusFlag::sourceIllegal;
96 default:
97 RequireNotReached (); // API doesn't allow this
98 return UTFConvert::ConversionStatusFlag::sourceIllegal;
99 }
100 }
101
102 constexpr int halfShift = 10; /* used for shifting by 10 bits */
103
104 constexpr char32_t halfBase = 0x0010000UL;
105 constexpr char32_t halfMask = 0x3FFUL;
106
107 /*
108 * Magic values subtracted from a buffer value during UTF8 conversion.
109 * This table contains as many values as there might be trailing bytes
110 * in a UTF-8 sequence.
111 */
112 constexpr char32_t offsetsFromUTF8[6] = {0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL};
113
114 /*
115 * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
116 * into the first byte, depending on how many bytes follow. There are
117 * as many entries in this table as there are UTF-8 sequence types.
118 * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
119 * for *legal* UTF-8 will be 4 or fewer bytes total.
120 */
121 constexpr char8_t firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
122
123 /*
124 * Index into the table below with the first byte of a UTF-8 sequence to
125 * get the number of trailing bytes that are supposed to follow it.
126 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
127 * left as-is for anyone who may want to do such conversion, which was
128 * allowed in earlier algorithms.
129 */
130 constexpr char trailingBytesForUTF8[256] = {
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
136 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
137
138 /*
139 * Utility routine to tell whether a sequence of bytes is legal UTF-8.
140 * This must be called with the length pre-determined by the first byte.
141 * If not calling this from ConvertUTF8to*, then the length can be set by:
142 * length = trailingBytesForUTF8[*source]+1;
143 * and the sequence is illegal right away if there aren't that many bytes
144 * available.
145 * If presented with a length > 4, this returns false. The Unicode
146 * definition of UTF-8 goes up to 4-byte sequences.
147 */
148 bool isLegalUTF8_ (const char8_t* source, int length)
149 {
150 char8_t a;
151 const char8_t* srcptr = source + length;
152 switch (length) {
153 default:
154 return false;
155 /* Everything else falls through when "true"... */
156 case 4:
157 if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
158 return false;
159 case 3:
160 if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
161 return false;
162 case 2:
163 if ((a = (*--srcptr)) > 0xBF)
164 return false;
165
166 switch ((unsigned char)*source) {
167 /* no fall-through in this inner switch */
168 case 0xE0:
169 if (a < 0xA0)
170 return false;
171 break;
172 case 0xED:
173 if (a > 0x9F)
174 return false;
175 break;
176 case 0xF0:
177 if (a < 0x90)
178 return false;
179 break;
180 case 0xF4:
181 if (a > 0x8F)
182 return false;
183 break;
184 default:
185 if (a < 0x80)
186 return false;
187 }
188
189 case 1:
190 if (*source >= 0x80 && *source < 0xC2)
191 return false;
192 }
193 if (*source > 0xF4)
194 return false;
195 return true;
196 }
197
198 inline ConversionResult ConvertUTF8toUTF16_ (const char8_t** sourceStart, const char8_t* sourceEnd, char16_t** targetStart,
199 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement)
200 {
201 ConversionResult result = conversionOK;
202 const char8_t* source = *sourceStart;
203 char16_t* target = *targetStart;
204 auto addMissing = [&] () {
205 if (Character{*missingCharacterReplacement}.IsSurrogatePair ()) {
206 auto p = Character{*missingCharacterReplacement}.GetSurrogatePair ();
207 *target++ = p.first;
208 *target++ = p.second;
209 }
210 else {
211 *target++ = static_cast<char16_t> (*missingCharacterReplacement);
212 }
213 };
214 while (source < sourceEnd) {
215 char32_t ch = 0;
216 unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
217 if (source + extraBytesToRead >= sourceEnd) {
218 result = sourceExhausted;
219 break;
220 }
221 if (!isLegalUTF8_ (source, extraBytesToRead + 1)) {
222 if (missingCharacterReplacement) {
223 AssertNotImplemented (); // @todo - not hard - but not done
224 }
225 else {
226 result = sourceIllegal;
227 break;
228 }
229 }
230 /*
231 * The cases all fall through. See "Note A" below.
232 */
233 switch (extraBytesToRead) {
234 case 5:
235 ch += *source++;
236 ch <<= 6; /* remember, illegal UTF-8 */
237 case 4:
238 ch += *source++;
239 ch <<= 6; /* remember, illegal UTF-8 */
240 case 3:
241 ch += *source++;
242 ch <<= 6;
243 case 2:
244 ch += *source++;
245 ch <<= 6;
246 case 1:
247 ch += *source++;
248 ch <<= 6;
249 case 0:
250 ch += *source++;
251 }
252 ch -= offsetsFromUTF8[extraBytesToRead];
253
254 if (target >= targetEnd) [[unlikely]] {
255 source -= (extraBytesToRead + 1); /* Back up source pointer! */
256 result = targetExhausted;
257 break;
258 }
259 if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
260 /* UTF-16 surrogate values are illegal in UTF-32 */
261 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) {
262 if (missingCharacterReplacement) {
263 addMissing ();
264 }
265 else {
266 source -= (extraBytesToRead + 1); /* return to the illegal value itself */
267 result = sourceIllegal;
268 break;
269 }
270 }
271 else {
272 *target++ = (char16_t)ch; /* normal case */
273 }
274 }
275 else if (ch > UNI_MAX_UTF16) {
276 if (missingCharacterReplacement) {
277 addMissing ();
278 }
279 else {
280 result = sourceIllegal;
281 source -= (extraBytesToRead + 1); /* return to the start */
282 break; /* Bail out; shouldn't continue */
283 }
284 }
285 else {
286 /* target is a character in range 0xFFFF - 0x10FFFF. */
287 if (target + 1 >= targetEnd) {
288 source -= (extraBytesToRead + 1); /* Back up source pointer! */
289 result = targetExhausted;
290 break;
291 }
292 ch -= halfBase;
293 *target++ = (char16_t)((ch >> halfShift) + Character::kUNICODESurrogate_High_Start);
294 *target++ = (char16_t)((ch & halfMask) + Character::kUNICODESurrogate_Low_Start);
295 }
296 }
297 *sourceStart = source;
298 *targetStart = target;
299 return result;
300 }
301 inline ConversionResult ConvertUTF16toUTF8_ (const char16_t** sourceStart, const char16_t* sourceEnd, char8_t** targetStart,
302 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement)
303 {
304 ConversionResult result = conversionOK;
305 const char16_t* source = *sourceStart;
306 char8_t* target = *targetStart;
307 while (source < sourceEnd) {
308 char32_t ch;
309 unsigned short bytesToWrite = 0;
310 constexpr char32_t byteMask = 0xBF;
311 constexpr char32_t byteMark = 0x80;
312 const char16_t* oldSource = source; /* In case we have to back up because of target overflow. */
313 ch = *source++;
314 /* If we have a surrogate pair, convert to char32_t first. */
315 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_High_End) [[unlikely]] {
316 /* If the 16 bits following the high surrogate are in the source buffer... */
317 if (source < sourceEnd) [[likely]] {
318 char32_t ch2 = *source;
319 /* If it's a low surrogate, convert to char32_t. */
320 if (ch2 >= Character::kUNICODESurrogate_Low_Start && ch2 <= Character::kUNICODESurrogate_Low_End) {
321 ch = ((ch - Character::kUNICODESurrogate_High_Start) << halfShift) + (ch2 - Character::kUNICODESurrogate_Low_Start) + halfBase;
322 ++source;
323 }
324 else if (missingCharacterReplacement == nullopt) { /* it's an unpaired high surrogate */
325 --source; /* return to the illegal value itself */
326 result = sourceIllegal;
327 break;
328 }
329 }
330 else { /* We don't have the 16 bits following the high surrogate. */
331 --source; /* return to the high surrogate */
332 result = sourceExhausted;
333 break;
334 }
335 }
336 else if (missingCharacterReplacement == nullopt) {
337 /* UTF-16 surrogate values are illegal in UTF-32 */
338 if (ch >= Character::kUNICODESurrogate_Low_Start && ch <= Character::kUNICODESurrogate_Low_End) {
339 --source; /* return to the illegal value itself */
340 result = sourceIllegal;
341 break;
342 }
343 }
344 /* Figure out how many bytes the result will require */
345 if (ch < (char32_t)0x80) {
346 bytesToWrite = 1;
347 }
348 else if (ch < (char32_t)0x800) {
349 bytesToWrite = 2;
350 }
351 else if (ch < (char32_t)0x10000) {
352 bytesToWrite = 3;
353 }
354 else if (ch < (char32_t)0x110000) {
355 bytesToWrite = 4;
356 }
357 else {
358 bytesToWrite = 3;
359 Assert (missingCharacterReplacement.has_value ()); // I THINK sb caught above if 'strict' mode
360 ch = *missingCharacterReplacement;
361 }
362
363 target += bytesToWrite;
364 if (target > targetEnd) {
365 source = oldSource; /* Back up source pointer! */
366 target -= bytesToWrite;
367 result = targetExhausted;
368 break;
369 }
370 switch (bytesToWrite) { /* note: everything falls through. */
371 case 4:
372 *--target = (char8_t)((ch | byteMark) & byteMask);
373 ch >>= 6;
374 case 3:
375 *--target = (char8_t)((ch | byteMark) & byteMask);
376 ch >>= 6;
377 case 2:
378 *--target = (char8_t)((ch | byteMark) & byteMask);
379 ch >>= 6;
380 case 1:
381 *--target = (char8_t)(ch | firstByteMark[bytesToWrite]);
382 }
383 target += bytesToWrite;
384 }
385 *sourceStart = source;
386 *targetStart = target;
387 return result;
388 }
389 DISABLE_COMPILER_MSC_WARNING_START (4701) // potentially uninitialized local variable 'ch' used (WRONG cuz if we get into loop, initialized
390 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wmaybe-uninitialized\""); // potentially uninitialized local variable 'ch' used (WRONG cuz if we get into loop, initialized
391 inline ConversionResult ConvertUTF16toUTF32_ (const char16_t** sourceStart, const char16_t* sourceEnd, char32_t** targetStart,
392 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement)
393 {
394 ConversionResult result = conversionOK;
395 const char16_t* source = *sourceStart;
396 char32_t* target = *targetStart;
397 char32_t ch, ch2;
398 while (source < sourceEnd) {
399 const char16_t* oldSource = source; /* In case we have to back up because of target overflow. */
400 ch = *source++;
401 /* If we have a surrogate pair, convert to UTF32 first. */
402 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_High_End) [[unlikely]] {
403 /* If the 16 bits following the high surrogate are in the source buffer... */
404 if (source < sourceEnd) {
405 ch2 = *source;
406 /* If it's a low surrogate, convert to UTF32. */
407 if (ch2 >= Character::kUNICODESurrogate_Low_Start && ch2 <= Character::kUNICODESurrogate_Low_End) {
408 ch = ((ch - Character::kUNICODESurrogate_High_Start) << halfShift) + (ch2 - Character::kUNICODESurrogate_Low_Start) + halfBase;
409 ++source;
410 }
411 else if (missingCharacterReplacement == nullopt) { /* it's an unpaired high surrogate */
412 --source; /* return to the illegal value itself */
413 result = sourceIllegal;
414 break;
415 }
416 }
417 else { /* We don't have the 16 bits following the high surrogate. */
418 --source; /* return to the high surrogate */
419 result = sourceExhausted;
420 break;
421 }
422 }
423 else if (missingCharacterReplacement == nullopt) {
424 /* UTF-16 surrogate values are illegal in UTF-32 */
425 if (ch >= Character::kUNICODESurrogate_Low_Start && ch <= Character::kUNICODESurrogate_Low_End) {
426 --source; /* return to the illegal value itself */
427 result = sourceIllegal;
428 break;
429 }
430 }
431 if (target >= targetEnd) {
432 source = oldSource; /* Back up source pointer! */
433 result = targetExhausted;
434 break;
435 }
436 *target++ = ch;
437 }
438 *sourceStart = source;
439 *targetStart = target;
440 if (result == sourceIllegal) {
441 using namespace Characters::Literals;
442 DbgTrace ("ConvertUTF16toUTF32 illegal seq 0x{:x},0x{:x}"_f, static_cast<int> (ch), static_cast<int> (ch2));
443 }
444 return result;
445 }
446 DISABLE_COMPILER_MSC_WARNING_END (4701)
447 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wmaybe-uninitialized\""); // potentially uninitialized local variable 'ch' used (WRONG cuz if we get into loop, initialized
448 inline ConversionResult ConvertUTF32toUTF16_ (const char32_t** sourceStart, const char32_t* sourceEnd, char16_t** targetStart,
449 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement)
450 {
451 ConversionResult result = conversionOK;
452 const char32_t* source = *sourceStart;
453 char16_t* target = *targetStart;
454 auto addMissing = [&] () {
455 if (Character{*missingCharacterReplacement}.IsSurrogatePair ()) {
456 auto p = Character{*missingCharacterReplacement}.GetSurrogatePair ();
457 *target++ = p.first;
458 *target++ = p.second;
459 }
460 else {
461 *target++ = static_cast<char16_t> (*missingCharacterReplacement);
462 }
463 };
464 while (source < sourceEnd) {
465 char32_t ch;
466 if (target >= targetEnd) {
467 result = targetExhausted;
468 break;
469 }
470 ch = *source++;
471 if (ch <= UNI_MAX_BMP) [[likely]] { /* Target is a character <= 0xFFFF */
472 /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
473 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) [[unlikely]] {
474 if (missingCharacterReplacement == nullopt) {
475 --source; /* return to the illegal value itself */
476 result = sourceIllegal;
477 break;
478 }
479 else {
480 addMissing ();
481 }
482 }
483 else {
484 *target++ = (char16_t)ch; /* normal case */
485 }
486 }
487 else if (ch > UNI_MAX_LEGAL_UTF32) {
488 if (missingCharacterReplacement) {
489 addMissing ();
490 }
491 else {
492 result = sourceIllegal;
493 }
494 }
495 else {
496 /* target is a character in range 0xFFFF - 0x10FFFF. */
497 if (target + 1 >= targetEnd) {
498 --source; /* Back up source pointer! */
499 result = targetExhausted;
500 break;
501 }
502 ch -= halfBase;
503 *target++ = (char16_t)((ch >> halfShift) + Character::kUNICODESurrogate_High_Start);
504 *target++ = (char16_t)((ch & halfMask) + Character::kUNICODESurrogate_Low_Start);
505 }
506 }
507 *sourceStart = source;
508 *targetStart = target;
509 return result;
510 }
511 inline ConversionResult ConvertUTF8toUTF32_ (const char8_t** sourceStart, const char8_t* sourceEnd, char32_t** targetStart,
512 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement)
513 {
514 ConversionResult result = conversionOK;
515 const char8_t* source = *sourceStart;
516 char32_t* target = *targetStart;
517 while (source < sourceEnd) {
518 char32_t ch = 0;
519 unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
520 if (source + extraBytesToRead >= sourceEnd) [[unlikely]] {
521 result = sourceExhausted;
522 break;
523 }
524 if (!isLegalUTF8_ (source, extraBytesToRead + 1)) {
525 if (missingCharacterReplacement) {
526 AssertNotImplemented (); // @todo - not hard - but not done
527 }
528 else {
529 result = sourceIllegal;
530 break;
531 }
532 }
533 /*
534 * The cases all fall through. See "Note A" below.
535 */
536 switch (extraBytesToRead) {
537 case 5:
538 ch += *source++;
539 ch <<= 6;
540 case 4:
541 ch += *source++;
542 ch <<= 6;
543 case 3:
544 ch += *source++;
545 ch <<= 6;
546 case 2:
547 ch += *source++;
548 ch <<= 6;
549 case 1:
550 ch += *source++;
551 ch <<= 6;
552 case 0:
553 ch += *source++;
554 }
555 ch -= offsetsFromUTF8[extraBytesToRead];
556
557 if (target >= targetEnd) [[unlikely]] {
558 source -= (extraBytesToRead + 1); /* Back up the source pointer! */
559 result = targetExhausted;
560 break;
561 }
562 if (ch <= UNI_MAX_LEGAL_UTF32) [[likely]] {
563 /*
564 * UTF-16 surrogate values are illegal in UTF-32, and anything
565 * over Plane 17 (> 0x10FFFF) is illegal.
566 */
567 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) {
568 if (missingCharacterReplacement) {
569 *target++ = *missingCharacterReplacement;
570 }
571 else {
572 source -= (extraBytesToRead + 1); /* return to the illegal value itself */
573 result = sourceIllegal;
574 break;
575 }
576 }
577 else {
578 *target++ = ch;
579 }
580 }
581 else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
582 if (missingCharacterReplacement) {
583 *target++ = *missingCharacterReplacement;
584 }
585 else {
586 result = sourceIllegal;
587 break;
588 }
589 }
590 }
591 *sourceStart = source;
592 *targetStart = target;
593 return result;
594 }
595 inline ConversionResult ConvertUTF32toUTF8_ (const char32_t** sourceStart, const char32_t* sourceEnd, char8_t** targetStart,
596 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement)
597 {
598 ConversionResult result = conversionOK;
599 const char32_t* source = *sourceStart;
600 char8_t* target = *targetStart;
601 while (source < sourceEnd) {
602 char32_t ch;
603 unsigned short bytesToWrite = 0;
604 const char32_t byteMask = 0xBF;
605 const char32_t byteMark = 0x80;
606 ch = *source++;
607 if (missingCharacterReplacement == nullopt) {
608 /* UTF-16 surrogate values are illegal in UTF-32 */
609 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) [[unlikely]] {
610 --source; /* return to the illegal value itself */
611 result = sourceIllegal;
612 break;
613 }
614 }
615 /*
616 * Figure out how many bytes the result will require. Turn any
617 * illegally large UTF32 things (> Plane 17) into replacement chars.
618 */
619 if (ch < (char32_t)0x80) {
620 bytesToWrite = 1;
621 }
622 else if (ch < (char32_t)0x800) {
623 bytesToWrite = 2;
624 }
625 else if (ch < (char32_t)0x10000) {
626 bytesToWrite = 3;
627 }
628 else if (ch <= UNI_MAX_LEGAL_UTF32) {
629 bytesToWrite = 4;
630 }
631 else {
632 if (missingCharacterReplacement) {
633 ch = *missingCharacterReplacement;
634 bytesToWrite = 3; // @todo WRONG - must get right number for this character
635 }
636 else {
637 result = sourceIllegal;
638 break;
639 }
640 }
641
642 target += bytesToWrite;
643 if (target > targetEnd) {
644 --source; /* Back up source pointer! */
645 target -= bytesToWrite;
646 result = targetExhausted;
647 break;
648 }
649 switch (bytesToWrite) { /* note: everything falls through. */
650 case 4:
651 *--target = (char8_t)((ch | byteMark) & byteMask);
652 ch >>= 6;
653 case 3:
654 *--target = (char8_t)((ch | byteMark) & byteMask);
655 ch >>= 6;
656 case 2:
657 *--target = (char8_t)((ch | byteMark) & byteMask);
658 ch >>= 6;
659 case 1:
660 *--target = (char8_t)(ch | firstByteMark[bytesToWrite]);
661 }
662 target += bytesToWrite;
663 }
664 *sourceStart = source;
665 *targetStart = target;
666 return result;
667 }
668
669#if defined(__clang_major__) and (20 < __clang_major__)
670 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wcharacter-conversion\"")
671#endif
672 }
673}
674
675namespace {
676 namespace UTFConvert_codecvtSupport_ {
677 inline UTFConvert::ConversionStatusFlag cvt_stdcodecvt_results_ (int i)
678 {
679 switch (i) {
680 case codecvt_base::ok:
681 return UTFConvert::ConversionStatusFlag::ok;
682 case codecvt_base::error:
683 return UTFConvert::ConversionStatusFlag::sourceIllegal;
684 case codecvt_base::partial:
685 return UTFConvert::ConversionStatusFlag::sourceExhausted; // not quite - could be target exhausted?
686 case codecvt_base::noconv:
687 return UTFConvert::ConversionStatusFlag::sourceIllegal; // not quite
688 default:
689 Assert (false);
690 return UTFConvert::ConversionStatusFlag::sourceIllegal;
691 }
692 }
693 inline UTFConvert::ConversionStatusFlag ConvertUTF8toUTF16_codecvt_ (const char8_t** sourceStart, const char8_t* sourceEnd,
694 char16_t** targetStart, char16_t* targetEnd)
695 {
696 DISABLE_COMPILER_MSC_WARNING_START (4996)
697 static const deletable_facet_<codecvt<char16_t, char8_t, mbstate_t>> cvt;
698 mbstate_t ignoredMBState{};
699 const char8_t* sourceCursor = *sourceStart;
700 char16_t* outCursor = *targetStart;
701 codecvt_base::result rr = cvt.in (ignoredMBState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
702 *sourceStart = reinterpret_cast<const char8_t*> (sourceCursor);
703 *targetStart = outCursor;
704 return cvt_stdcodecvt_results_ (rr);
705 DISABLE_COMPILER_MSC_WARNING_END (4996)
706 }
707 inline UTFConvert::ConversionStatusFlag ConvertUTF16toUTF8_codecvt_ (const char16_t** sourceStart, const char16_t* sourceEnd,
708 char8_t** targetStart, char8_t* targetEnd)
709 {
710 DISABLE_COMPILER_MSC_WARNING_START (4996)
711 static const deletable_facet_<codecvt<char16_t, char8_t, mbstate_t>> cvt;
712 mbstate_t ignoredMBState{};
713 const char16_t* sourceCursor = *sourceStart;
714 char8_t* outCursor = *targetStart;
715 codecvt_base::result rr = cvt.out (ignoredMBState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
716 *sourceStart = reinterpret_cast<const char16_t*> (sourceCursor);
717 *targetStart = reinterpret_cast<char8_t*> (outCursor);
718 return cvt_stdcodecvt_results_ (rr);
719 DISABLE_COMPILER_MSC_WARNING_END (4996)
720 }
721 inline UTFConvert::ConversionStatusFlag ConvertUTF8toUTF32_codecvt_ (const char8_t** sourceStart, const char8_t* sourceEnd,
722 char32_t** targetStart, char32_t* targetEnd)
723 {
724 DISABLE_COMPILER_MSC_WARNING_START (4996)
725 static const deletable_facet_<codecvt<char32_t, char8_t, mbstate_t>> cvt;
726 mbstate_t ignoredState{};
727
728 const char8_t* sourceCursor = *sourceStart;
729 char32_t* outCursor = *targetStart;
730 codecvt_base::result rr = cvt.in (ignoredState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
731 *sourceStart = reinterpret_cast<const char8_t*> (sourceCursor);
732 *targetStart = outCursor;
733 return cvt_stdcodecvt_results_ (rr);
734 DISABLE_COMPILER_MSC_WARNING_END (4996)
735 }
736 inline UTFConvert::ConversionStatusFlag ConvertUTF32toUTF8_codecvt_ (const char32_t** sourceStart, const char32_t* sourceEnd,
737 char8_t** targetStart, char8_t* targetEnd)
738 {
739 DISABLE_COMPILER_MSC_WARNING_START (4996)
740 static const deletable_facet_<codecvt<char32_t, char8_t, mbstate_t>> cvt;
741 mbstate_t ignoredState{};
742 const char32_t* sourceCursor = *sourceStart;
743 char8_t* outCursor = *targetStart;
744 codecvt_base::result rr = cvt.out (ignoredState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
745 *sourceStart = reinterpret_cast<const char32_t*> (sourceCursor);
746 *targetStart = reinterpret_cast<char8_t*> (outCursor);
747 return cvt_stdcodecvt_results_ (rr);
748 DISABLE_COMPILER_MSC_WARNING_END (4996)
749 }
750 }
751}
752
753/*
754 ********************************************************************************
755 ***************************** Characters::UTFConvert ***************************
756 ********************************************************************************
757 */
758namespace {
759 using ConversionResultWithStatus = Characters::UTFConvert::ConversionResultWithStatus;
760 using ConversionStatusFlag = Characters::UTFConvert::ConversionStatusFlag;
761
762 template <typename IN_T, typename OUT_T, regular_invocable<const IN_T**, const IN_T*, OUT_T**, OUT_T*, optional<char32_t>> FUN2DO_REAL_WORK>
763 inline auto ConvertQuietly_StroikaPortable_helper_ (optional<Character> invalidCharacterReplacement, span<const IN_T> source,
764 span<OUT_T> target, FUN2DO_REAL_WORK&& realWork) -> ConversionResultWithStatus
765 {
766 using namespace UTFConvert_libutfxx_;
767 const IN_T* sourceStart = source.data ();
768 const IN_T* sourceEnd = sourceStart + source.size ();
769 OUT_T* targetStart = target.data ();
770 OUT_T* targetEnd = targetStart + target.size ();
771 // convert replacement character to target character set, and then pass that
772 ConversionResult r =
773 realWork (&sourceStart, sourceEnd, &targetStart, targetEnd,
774 invalidCharacterReplacement.has_value () ? invalidCharacterReplacement->As<char32_t> () : optional<char32_t>{});
775 return ConversionResultWithStatus{
776 {static_cast<size_t> (sourceStart - source.data ()), static_cast<size_t> (targetStart - target.data ())}, cvt_ (r)};
777 }
778}
779auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char8_t> source, span<char16_t> target)
780 -> ConversionResultWithStatus
781{
782#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
783 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
784 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
785 [] (const char8_t** sourceStart, const char8_t* sourceEnd, char16_t** targetStart,
786 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
787 return UTFConvert_libutfxx_::ConvertUTF8toUTF16_ (
788 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
789 });
790 }
791#endif
792 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF8toUTF16_);
793}
794auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char8_t> source, span<char32_t> target)
795 -> ConversionResultWithStatus
796{
797#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
798 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
799 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
800 [] (const char8_t** sourceStart, const char8_t* sourceEnd, char32_t** targetStart,
801 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
802 return UTFConvert_libutfxx_::ConvertUTF8toUTF32_ (
803 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
804 });
805 }
806#endif
807 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF8toUTF32_);
808}
809auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char16_t> source,
810 span<char32_t> target) -> ConversionResultWithStatus
811{
812#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
813 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
814 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
815 [] (const char16_t** sourceStart, const char16_t* sourceEnd, char32_t** targetStart,
816 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
817 return UTFConvert_libutfxx_::ConvertUTF16toUTF32_ (
818 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
819 });
820 }
821#endif
822 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF16toUTF32_);
823}
824auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char32_t> source,
825 span<char16_t> target) -> ConversionResultWithStatus
826{
827#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
828 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
829 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
830 [] (const char32_t** sourceStart, const char32_t* sourceEnd, char16_t** targetStart,
831 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
832 return UTFConvert_libutfxx_::ConvertUTF32toUTF16_ (
833 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
834 });
835 }
836#endif
837 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF32toUTF16_);
838}
839auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char32_t> source, span<char8_t> target)
840 -> ConversionResultWithStatus
841{
842#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
843 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
844 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
845 [] (const char32_t** sourceStart, const char32_t* sourceEnd, char8_t** targetStart,
846 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
847 return UTFConvert_libutfxx_::ConvertUTF32toUTF8_ (
848 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
849 });
850 }
851#endif
852 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF32toUTF8_);
853}
854auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char16_t> source, span<char8_t> target)
855 -> ConversionResultWithStatus
856{
857#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
858 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
859 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
860 [] (const char16_t** sourceStart, const char16_t* sourceEnd, char8_t** targetStart,
861 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
862 return UTFConvert_libutfxx_::ConvertUTF16toUTF8_ (
863 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
864 });
865 }
866#endif
867 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF16toUTF8_);
868}
869
870namespace {
871 template <typename IN_T, typename OUT_T, typename FUN2DO_REAL_WORK>
872 inline auto ConvertQuietly_codeCvt_helper_ (span<const IN_T> source, const span<OUT_T> target, FUN2DO_REAL_WORK&& realWork) -> ConversionResultWithStatus
873 {
874 using namespace UTFConvert_codecvtSupport_;
875 const IN_T* sourceStart = reinterpret_cast<const IN_T*> (source.data ());
876 const IN_T* sourceEnd = sourceStart + source.size ();
877 OUT_T* targetStart = reinterpret_cast<OUT_T*> (target.data ());
878 OUT_T* targetEnd = targetStart + target.size ();
879 ConversionStatusFlag r = realWork (&sourceStart, sourceEnd, &targetStart, targetEnd);
880 if (r == ConversionStatusFlag::ok) {
881 return ConversionResultWithStatus{{static_cast<size_t> (sourceStart - reinterpret_cast<const IN_T*> (source.data ())),
882 static_cast<size_t> (targetStart - reinterpret_cast<const OUT_T*> (target.data ()))},
883 ConversionStatusFlag::ok};
884 }
885 else {
886 return ConversionResultWithStatus{{0, 0}, r};
887 }
888 }
889}
890auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char8_t> source, span<char16_t> target) -> ConversionResultWithStatus
891{
892 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF8toUTF16_codecvt_);
893}
894auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char16_t> source, span<char8_t> target) -> ConversionResultWithStatus
895{
896 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF16toUTF8_codecvt_);
897}
898auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char8_t> source, span<char32_t> target) -> ConversionResultWithStatus
899{
900 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF8toUTF32_codecvt_);
901}
902auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char32_t> source, span<char8_t> target) -> ConversionResultWithStatus
903{
904 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF32toUTF8_codecvt_);
905}
906
907void UTFConvert::Throw (ConversionStatusFlag cr, size_t errorAtSourceOffset)
908{
909 switch (cr) {
911 static const auto kException_ = Execution::RuntimeErrorException{"Invalid UNICODE source string (incomplete UTF character)"sv};
912 Execution::Throw (kException_);
913 }
916 }
917 default:
919 }
920}
#define AssertNotImplemented()
Definition Assertions.h:402
#define RequireNotReached()
Definition Assertions.h:386
#define AssertNotReached()
Definition Assertions.h:356
#define DbgTrace
Definition Trace.h:317
An error occurred encoding or decoding a character
constexpr pair< char16_t, char16_t > GetSurrogatePair() const
ConversionStatusFlag
used for ConvertQuietly
Definition UTFConvert.h:276
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43