Stroika Library 3.0d25
 
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 /*
199 * Could [source, end) - fewer bytes than the character its first byte starts - be the start of a legal
200 * character? If not, it is malformed now; if so, the rest may just not have arrived yet.
201 */
202 bool isLegalUTF8Prefix_ (const char8_t* source, const char8_t* end)
203 {
204 char8_t lead = *source;
205 if (lead < 0xC2 or lead > 0xF4) {
206 return false;
207 }
208 for (const char8_t* p = source + 1; p < end; ++p) {
209 char8_t lo = 0x80;
210 char8_t hi = 0xBF;
211 if (p == source + 1) {
212 switch (lead) {
213 case 0xE0:
214 lo = 0xA0;
215 break;
216 case 0xED:
217 hi = 0x9F;
218 break;
219 case 0xF0:
220 lo = 0x90;
221 break;
222 case 0xF4:
223 hi = 0x8F;
224 break;
225 }
226 }
227 if (*p < lo or *p > hi) {
228 return false;
229 }
230 }
231 return true;
232 }
233
234 inline ConversionResult ConvertUTF8toUTF16_ (const char8_t** sourceStart, const char8_t* sourceEnd, char16_t** targetStart,
235 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement)
236 {
237 ConversionResult result = conversionOK;
238 const char8_t* source = *sourceStart;
239 char16_t* target = *targetStart;
240 auto addMissing = [&] () {
241 if (Character{*missingCharacterReplacement}.IsSurrogatePair ()) {
242 auto p = Character{*missingCharacterReplacement}.GetSurrogatePair ();
243 *target++ = p.first;
244 *target++ = p.second;
245 }
246 else {
247 *target++ = static_cast<char16_t> (*missingCharacterReplacement);
248 }
249 };
250 while (source < sourceEnd) {
251 char32_t ch = 0;
252 unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
253 bool cutShort = source + extraBytesToRead >= sourceEnd;
254 if (cutShort ? not isLegalUTF8Prefix_ (source, sourceEnd) : not isLegalUTF8_ (source, extraBytesToRead + 1)) {
255 if (not missingCharacterReplacement) {
256 result = sourceIllegal;
257 break;
258 }
259 if (target >= targetEnd) [[unlikely]] {
260 result = targetExhausted;
261 break;
262 }
263 addMissing ();
264 ++source; // resynchronize at the next byte
265 continue;
266 }
267 if (cutShort) {
268 result = sourceExhausted;
269 break;
270 }
271 /*
272 * The cases all fall through. See "Note A" below.
273 */
274 switch (extraBytesToRead) {
275 case 5:
276 ch += *source++;
277 ch <<= 6; /* remember, illegal UTF-8 */
278 case 4:
279 ch += *source++;
280 ch <<= 6; /* remember, illegal UTF-8 */
281 case 3:
282 ch += *source++;
283 ch <<= 6;
284 case 2:
285 ch += *source++;
286 ch <<= 6;
287 case 1:
288 ch += *source++;
289 ch <<= 6;
290 case 0:
291 ch += *source++;
292 }
293 ch -= offsetsFromUTF8[extraBytesToRead];
294
295 if (target >= targetEnd) [[unlikely]] {
296 source -= (extraBytesToRead + 1); /* Back up source pointer! */
297 result = targetExhausted;
298 break;
299 }
300 if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
301 /* UTF-16 surrogate values are illegal in UTF-32 */
302 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) {
303 if (missingCharacterReplacement) {
304 addMissing ();
305 }
306 else {
307 source -= (extraBytesToRead + 1); /* return to the illegal value itself */
308 result = sourceIllegal;
309 break;
310 }
311 }
312 else {
313 *target++ = (char16_t)ch; /* normal case */
314 }
315 }
316 else if (ch > UNI_MAX_UTF16) {
317 if (missingCharacterReplacement) {
318 addMissing ();
319 }
320 else {
321 result = sourceIllegal;
322 source -= (extraBytesToRead + 1); /* return to the start */
323 break; /* Bail out; shouldn't continue */
324 }
325 }
326 else {
327 /* target is a character in range 0xFFFF - 0x10FFFF. */
328 if (target + 1 >= targetEnd) {
329 source -= (extraBytesToRead + 1); /* Back up source pointer! */
330 result = targetExhausted;
331 break;
332 }
333 ch -= halfBase;
334 *target++ = (char16_t)((ch >> halfShift) + Character::kUNICODESurrogate_High_Start);
335 *target++ = (char16_t)((ch & halfMask) + Character::kUNICODESurrogate_Low_Start);
336 }
337 }
338 *sourceStart = source;
339 *targetStart = target;
340 return result;
341 }
342 inline ConversionResult ConvertUTF16toUTF8_ (const char16_t** sourceStart, const char16_t* sourceEnd, char8_t** targetStart,
343 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement)
344 {
345 ConversionResult result = conversionOK;
346 const char16_t* source = *sourceStart;
347 char8_t* target = *targetStart;
348 while (source < sourceEnd) {
349 char32_t ch;
350 unsigned short bytesToWrite = 0;
351 constexpr char32_t byteMask = 0xBF;
352 constexpr char32_t byteMark = 0x80;
353 const char16_t* oldSource = source; /* In case we have to back up because of target overflow. */
354 ch = *source++;
355 /* If we have a surrogate pair, convert to char32_t first. */
356 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_High_End) [[unlikely]] {
357 /* If the 16 bits following the high surrogate are in the source buffer... */
358 if (source < sourceEnd) [[likely]] {
359 char32_t ch2 = *source;
360 /* If it's a low surrogate, convert to char32_t. */
361 if (ch2 >= Character::kUNICODESurrogate_Low_Start && ch2 <= Character::kUNICODESurrogate_Low_End) {
362 ch = ((ch - Character::kUNICODESurrogate_High_Start) << halfShift) + (ch2 - Character::kUNICODESurrogate_Low_Start) + halfBase;
363 ++source;
364 }
365 else if (missingCharacterReplacement == nullopt) { /* it's an unpaired high surrogate */
366 --source; /* return to the illegal value itself */
367 result = sourceIllegal;
368 break;
369 }
370 }
371 else { /* We don't have the 16 bits following the high surrogate. */
372 --source; /* return to the high surrogate */
373 result = sourceExhausted;
374 break;
375 }
376 }
377 else if (missingCharacterReplacement == nullopt) {
378 /* UTF-16 surrogate values are illegal in UTF-32 */
379 if (ch >= Character::kUNICODESurrogate_Low_Start && ch <= Character::kUNICODESurrogate_Low_End) {
380 --source; /* return to the illegal value itself */
381 result = sourceIllegal;
382 break;
383 }
384 }
385 /* Figure out how many bytes the result will require */
386 if (ch < (char32_t)0x80) {
387 bytesToWrite = 1;
388 }
389 else if (ch < (char32_t)0x800) {
390 bytesToWrite = 2;
391 }
392 else if (ch < (char32_t)0x10000) {
393 bytesToWrite = 3;
394 }
395 else if (ch < (char32_t)0x110000) {
396 bytesToWrite = 4;
397 }
398 else {
399 bytesToWrite = 3;
400 Assert (missingCharacterReplacement.has_value ()); // I THINK sb caught above if 'strict' mode
401 ch = *missingCharacterReplacement;
402 }
403
404 target += bytesToWrite;
405 if (target > targetEnd) {
406 source = oldSource; /* Back up source pointer! */
407 target -= bytesToWrite;
408 result = targetExhausted;
409 break;
410 }
411 switch (bytesToWrite) { /* note: everything falls through. */
412 case 4:
413 *--target = (char8_t)((ch | byteMark) & byteMask);
414 ch >>= 6;
415 case 3:
416 *--target = (char8_t)((ch | byteMark) & byteMask);
417 ch >>= 6;
418 case 2:
419 *--target = (char8_t)((ch | byteMark) & byteMask);
420 ch >>= 6;
421 case 1:
422 *--target = (char8_t)(ch | firstByteMark[bytesToWrite]);
423 }
424 target += bytesToWrite;
425 }
426 *sourceStart = source;
427 *targetStart = target;
428 return result;
429 }
430 DISABLE_COMPILER_MSC_WARNING_START (4701) // potentially uninitialized local variable 'ch' used (WRONG cuz if we get into loop, initialized
431 inline ConversionResult ConvertUTF16toUTF32_ (const char16_t** sourceStart, const char16_t* sourceEnd, char32_t** targetStart,
432 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement)
433 {
434 ConversionResult result = conversionOK;
435 const char16_t* source = *sourceStart;
436 char32_t* target = *targetStart;
437 char32_t ch, ch2;
438 while (source < sourceEnd) {
439 const char16_t* oldSource = source; /* In case we have to back up because of target overflow. */
440 ch = *source++;
441 /* If we have a surrogate pair, convert to UTF32 first. */
442 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_High_End) [[unlikely]] {
443 /* If the 16 bits following the high surrogate are in the source buffer... */
444 if (source < sourceEnd) {
445 ch2 = *source;
446 /* If it's a low surrogate, convert to UTF32. */
447 if (ch2 >= Character::kUNICODESurrogate_Low_Start && ch2 <= Character::kUNICODESurrogate_Low_End) {
448 ch = ((ch - Character::kUNICODESurrogate_High_Start) << halfShift) + (ch2 - Character::kUNICODESurrogate_Low_Start) + halfBase;
449 ++source;
450 }
451 else if (missingCharacterReplacement == nullopt) { /* it's an unpaired high surrogate */
452 --source; /* return to the illegal value itself */
453 result = sourceIllegal;
454 break;
455 }
456 }
457 else { /* We don't have the 16 bits following the high surrogate. */
458 --source; /* return to the high surrogate */
459 result = sourceExhausted;
460 break;
461 }
462 }
463 else if (missingCharacterReplacement == nullopt) {
464 /* UTF-16 surrogate values are illegal in UTF-32 */
465 if (ch >= Character::kUNICODESurrogate_Low_Start && ch <= Character::kUNICODESurrogate_Low_End) {
466 --source; /* return to the illegal value itself */
467 result = sourceIllegal;
468 break;
469 }
470 }
471 if (target >= targetEnd) {
472 source = oldSource; /* Back up source pointer! */
473 result = targetExhausted;
474 break;
475 }
476 *target++ = ch;
477 }
478 *sourceStart = source;
479 *targetStart = target;
480 if (result == sourceIllegal) {
481 using namespace Characters::Literals;
482 DbgTrace ("ConvertUTF16toUTF32 illegal seq 0x{:x},0x{:x}"_f, static_cast<int> (ch), static_cast<int> (ch2));
483 }
484 return result;
485 }
486 DISABLE_COMPILER_MSC_WARNING_END (4701)
487 inline ConversionResult ConvertUTF32toUTF16_ (const char32_t** sourceStart, const char32_t* sourceEnd, char16_t** targetStart,
488 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement)
489 {
490 ConversionResult result = conversionOK;
491 const char32_t* source = *sourceStart;
492 char16_t* target = *targetStart;
493 auto addMissing = [&] () {
494 if (Character{*missingCharacterReplacement}.IsSurrogatePair ()) {
495 auto p = Character{*missingCharacterReplacement}.GetSurrogatePair ();
496 *target++ = p.first;
497 *target++ = p.second;
498 }
499 else {
500 *target++ = static_cast<char16_t> (*missingCharacterReplacement);
501 }
502 };
503 while (source < sourceEnd) {
504 char32_t ch;
505 if (target >= targetEnd) {
506 result = targetExhausted;
507 break;
508 }
509 ch = *source++;
510 if (ch <= UNI_MAX_BMP) [[likely]] { /* Target is a character <= 0xFFFF */
511 /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
512 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) [[unlikely]] {
513 if (missingCharacterReplacement == nullopt) {
514 --source; /* return to the illegal value itself */
515 result = sourceIllegal;
516 break;
517 }
518 else {
519 addMissing ();
520 }
521 }
522 else {
523 *target++ = (char16_t)ch; /* normal case */
524 }
525 }
526 else if (ch > UNI_MAX_LEGAL_UTF32) {
527 if (missingCharacterReplacement) {
528 addMissing ();
529 }
530 else {
531 result = sourceIllegal;
532 }
533 }
534 else {
535 /* target is a character in range 0xFFFF - 0x10FFFF. */
536 if (target + 1 >= targetEnd) {
537 --source; /* Back up source pointer! */
538 result = targetExhausted;
539 break;
540 }
541 ch -= halfBase;
542 *target++ = (char16_t)((ch >> halfShift) + Character::kUNICODESurrogate_High_Start);
543 *target++ = (char16_t)((ch & halfMask) + Character::kUNICODESurrogate_Low_Start);
544 }
545 }
546 *sourceStart = source;
547 *targetStart = target;
548 return result;
549 }
550 inline ConversionResult ConvertUTF8toUTF32_ (const char8_t** sourceStart, const char8_t* sourceEnd, char32_t** targetStart,
551 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement)
552 {
553 ConversionResult result = conversionOK;
554 const char8_t* source = *sourceStart;
555 char32_t* target = *targetStart;
556 while (source < sourceEnd) {
557 char32_t ch = 0;
558 unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
559 bool cutShort = source + extraBytesToRead >= sourceEnd;
560 if (cutShort ? not isLegalUTF8Prefix_ (source, sourceEnd) : not isLegalUTF8_ (source, extraBytesToRead + 1)) {
561 if (not missingCharacterReplacement) {
562 result = sourceIllegal;
563 break;
564 }
565 if (target >= targetEnd) [[unlikely]] {
566 result = targetExhausted;
567 break;
568 }
569 *target++ = *missingCharacterReplacement;
570 ++source; // resynchronize at the next byte
571 continue;
572 }
573 if (cutShort) [[unlikely]] {
574 result = sourceExhausted;
575 break;
576 }
577 /*
578 * The cases all fall through. See "Note A" below.
579 */
580 switch (extraBytesToRead) {
581 case 5:
582 ch += *source++;
583 ch <<= 6;
584 case 4:
585 ch += *source++;
586 ch <<= 6;
587 case 3:
588 ch += *source++;
589 ch <<= 6;
590 case 2:
591 ch += *source++;
592 ch <<= 6;
593 case 1:
594 ch += *source++;
595 ch <<= 6;
596 case 0:
597 ch += *source++;
598 }
599 ch -= offsetsFromUTF8[extraBytesToRead];
600
601 if (target >= targetEnd) [[unlikely]] {
602 source -= (extraBytesToRead + 1); /* Back up the source pointer! */
603 result = targetExhausted;
604 break;
605 }
606 if (ch <= UNI_MAX_LEGAL_UTF32) [[likely]] {
607 /*
608 * UTF-16 surrogate values are illegal in UTF-32, and anything
609 * over Plane 17 (> 0x10FFFF) is illegal.
610 */
611 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) {
612 if (missingCharacterReplacement) {
613 *target++ = *missingCharacterReplacement;
614 }
615 else {
616 source -= (extraBytesToRead + 1); /* return to the illegal value itself */
617 result = sourceIllegal;
618 break;
619 }
620 }
621 else {
622 *target++ = ch;
623 }
624 }
625 else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
626 if (missingCharacterReplacement) {
627 *target++ = *missingCharacterReplacement;
628 }
629 else {
630 result = sourceIllegal;
631 break;
632 }
633 }
634 }
635 *sourceStart = source;
636 *targetStart = target;
637 return result;
638 }
639 inline ConversionResult ConvertUTF32toUTF8_ (const char32_t** sourceStart, const char32_t* sourceEnd, char8_t** targetStart,
640 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement)
641 {
642 ConversionResult result = conversionOK;
643 const char32_t* source = *sourceStart;
644 char8_t* target = *targetStart;
645 while (source < sourceEnd) {
646 char32_t ch;
647 unsigned short bytesToWrite = 0;
648 const char32_t byteMask = 0xBF;
649 const char32_t byteMark = 0x80;
650 ch = *source++;
651 if (missingCharacterReplacement == nullopt) {
652 /* UTF-16 surrogate values are illegal in UTF-32 */
653 if (ch >= Character::kUNICODESurrogate_High_Start && ch <= Character::kUNICODESurrogate_Low_End) [[unlikely]] {
654 --source; /* return to the illegal value itself */
655 result = sourceIllegal;
656 break;
657 }
658 }
659 /*
660 * Figure out how many bytes the result will require. Turn any
661 * illegally large UTF32 things (> Plane 17) into replacement chars.
662 */
663 if (ch < (char32_t)0x80) {
664 bytesToWrite = 1;
665 }
666 else if (ch < (char32_t)0x800) {
667 bytesToWrite = 2;
668 }
669 else if (ch < (char32_t)0x10000) {
670 bytesToWrite = 3;
671 }
672 else if (ch <= UNI_MAX_LEGAL_UTF32) {
673 bytesToWrite = 4;
674 }
675 else {
676 if (missingCharacterReplacement) {
677 ch = *missingCharacterReplacement;
678 bytesToWrite = 3; // @todo WRONG - must get right number for this character
679 }
680 else {
681 result = sourceIllegal;
682 break;
683 }
684 }
685
686 target += bytesToWrite;
687 if (target > targetEnd) {
688 --source; /* Back up source pointer! */
689 target -= bytesToWrite;
690 result = targetExhausted;
691 break;
692 }
693 switch (bytesToWrite) { /* note: everything falls through. */
694 case 4:
695 *--target = (char8_t)((ch | byteMark) & byteMask);
696 ch >>= 6;
697 case 3:
698 *--target = (char8_t)((ch | byteMark) & byteMask);
699 ch >>= 6;
700 case 2:
701 *--target = (char8_t)((ch | byteMark) & byteMask);
702 ch >>= 6;
703 case 1:
704 *--target = (char8_t)(ch | firstByteMark[bytesToWrite]);
705 }
706 target += bytesToWrite;
707 }
708 *sourceStart = source;
709 *targetStart = target;
710 return result;
711 }
712
713#if defined(__clang_major__) and (20 < __clang_major__)
714 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wcharacter-conversion\"")
715#endif
716 }
717}
718
719namespace {
720 namespace UTFConvert_codecvtSupport_ {
721 inline UTFConvert::ConversionStatusFlag cvt_stdcodecvt_results_ (int i)
722 {
723 switch (i) {
724 case codecvt_base::ok:
725 return UTFConvert::ConversionStatusFlag::ok;
726 case codecvt_base::error:
727 return UTFConvert::ConversionStatusFlag::sourceIllegal;
728 case codecvt_base::partial:
729 return UTFConvert::ConversionStatusFlag::sourceExhausted; // not quite - could be target exhausted?
730 case codecvt_base::noconv:
731 return UTFConvert::ConversionStatusFlag::sourceIllegal; // not quite
732 default:
733 Assert (false);
734 return UTFConvert::ConversionStatusFlag::sourceIllegal;
735 }
736 }
737 inline UTFConvert::ConversionStatusFlag ConvertUTF8toUTF16_codecvt_ (const char8_t** sourceStart, const char8_t* sourceEnd,
738 char16_t** targetStart, char16_t* targetEnd)
739 {
740 DISABLE_COMPILER_MSC_WARNING_START (4996)
741 static const deletable_facet_<codecvt<char16_t, char8_t, mbstate_t>> cvt;
742 mbstate_t ignoredMBState{};
743 const char8_t* sourceCursor = *sourceStart;
744 char16_t* outCursor = *targetStart;
745 codecvt_base::result rr = cvt.in (ignoredMBState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
746 *sourceStart = reinterpret_cast<const char8_t*> (sourceCursor);
747 *targetStart = outCursor;
748 return cvt_stdcodecvt_results_ (rr);
749 DISABLE_COMPILER_MSC_WARNING_END (4996)
750 }
751 inline UTFConvert::ConversionStatusFlag ConvertUTF16toUTF8_codecvt_ (const char16_t** sourceStart, const char16_t* sourceEnd,
752 char8_t** targetStart, char8_t* targetEnd)
753 {
754 DISABLE_COMPILER_MSC_WARNING_START (4996)
755 static const deletable_facet_<codecvt<char16_t, char8_t, mbstate_t>> cvt;
756 mbstate_t ignoredMBState{};
757 const char16_t* sourceCursor = *sourceStart;
758 char8_t* outCursor = *targetStart;
759 codecvt_base::result rr = cvt.out (ignoredMBState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
760 *sourceStart = reinterpret_cast<const char16_t*> (sourceCursor);
761 *targetStart = reinterpret_cast<char8_t*> (outCursor);
762 return cvt_stdcodecvt_results_ (rr);
763 DISABLE_COMPILER_MSC_WARNING_END (4996)
764 }
765 inline UTFConvert::ConversionStatusFlag ConvertUTF8toUTF32_codecvt_ (const char8_t** sourceStart, const char8_t* sourceEnd,
766 char32_t** targetStart, char32_t* targetEnd)
767 {
768 DISABLE_COMPILER_MSC_WARNING_START (4996)
769 static const deletable_facet_<codecvt<char32_t, char8_t, mbstate_t>> cvt;
770 mbstate_t ignoredState{};
771
772 const char8_t* sourceCursor = *sourceStart;
773 char32_t* outCursor = *targetStart;
774 codecvt_base::result rr = cvt.in (ignoredState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
775 *sourceStart = reinterpret_cast<const char8_t*> (sourceCursor);
776 *targetStart = outCursor;
777 return cvt_stdcodecvt_results_ (rr);
778 DISABLE_COMPILER_MSC_WARNING_END (4996)
779 }
780 inline UTFConvert::ConversionStatusFlag ConvertUTF32toUTF8_codecvt_ (const char32_t** sourceStart, const char32_t* sourceEnd,
781 char8_t** targetStart, char8_t* targetEnd)
782 {
783 DISABLE_COMPILER_MSC_WARNING_START (4996)
784 static const deletable_facet_<codecvt<char32_t, char8_t, mbstate_t>> cvt;
785 mbstate_t ignoredState{};
786 const char32_t* sourceCursor = *sourceStart;
787 char8_t* outCursor = *targetStart;
788 codecvt_base::result rr = cvt.out (ignoredState, *sourceStart, sourceEnd, sourceCursor, *targetStart, targetEnd, outCursor);
789 *sourceStart = reinterpret_cast<const char32_t*> (sourceCursor);
790 *targetStart = reinterpret_cast<char8_t*> (outCursor);
791 return cvt_stdcodecvt_results_ (rr);
792 DISABLE_COMPILER_MSC_WARNING_END (4996)
793 }
794 }
795}
796
797/*
798 ********************************************************************************
799 ***************************** Characters::UTFConvert ***************************
800 ********************************************************************************
801 */
802namespace {
803 using ConversionResultWithStatus = Characters::UTFConvert::ConversionResultWithStatus;
804 using ConversionStatusFlag = Characters::UTFConvert::ConversionStatusFlag;
805
806 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>
807 inline auto ConvertQuietly_StroikaPortable_helper_ (optional<Character> invalidCharacterReplacement, span<const IN_T> source,
808 span<OUT_T> target, FUN2DO_REAL_WORK&& realWork) -> ConversionResultWithStatus
809 {
810 using namespace UTFConvert_libutfxx_;
811 const IN_T* sourceStart = source.data ();
812 const IN_T* sourceEnd = sourceStart + source.size ();
813 OUT_T* targetStart = target.data ();
814 OUT_T* targetEnd = targetStart + target.size ();
815 // convert replacement character to target character set, and then pass that
816 ConversionResult r =
817 realWork (&sourceStart, sourceEnd, &targetStart, targetEnd,
818 invalidCharacterReplacement.has_value () ? invalidCharacterReplacement->As<char32_t> () : optional<char32_t>{});
819 return ConversionResultWithStatus{
820 {static_cast<size_t> (sourceStart - source.data ()), static_cast<size_t> (targetStart - target.data ())}, cvt_ (r)};
821 }
822}
823auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char8_t> source, span<char16_t> target)
824 -> ConversionResultWithStatus
825{
826#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
827 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
828 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
829 [] (const char8_t** sourceStart, const char8_t* sourceEnd, char16_t** targetStart,
830 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
831 return UTFConvert_libutfxx_::ConvertUTF8toUTF16_ (
832 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
833 });
834 }
835#endif
836 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF8toUTF16_);
837}
838auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char8_t> source, span<char32_t> target)
839 -> ConversionResultWithStatus
840{
841#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
842 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
843 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
844 [] (const char8_t** sourceStart, const char8_t* sourceEnd, char32_t** targetStart,
845 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
846 return UTFConvert_libutfxx_::ConvertUTF8toUTF32_ (
847 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
848 });
849 }
850#endif
851 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF8toUTF32_);
852}
853auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char16_t> source,
854 span<char32_t> target) -> ConversionResultWithStatus
855{
856#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
857 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
858 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
859 [] (const char16_t** sourceStart, const char16_t* sourceEnd, char32_t** targetStart,
860 char32_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
861 return UTFConvert_libutfxx_::ConvertUTF16toUTF32_ (
862 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
863 });
864 }
865#endif
866 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF16toUTF32_);
867}
868auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char32_t> source,
869 span<char16_t> target) -> ConversionResultWithStatus
870{
871#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
872 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
873 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
874 [] (const char32_t** sourceStart, const char32_t* sourceEnd, char16_t** targetStart,
875 char16_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
876 return UTFConvert_libutfxx_::ConvertUTF32toUTF16_ (
877 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
878 });
879 }
880#endif
881 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF32toUTF16_);
882}
883auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char32_t> source, span<char8_t> target)
884 -> ConversionResultWithStatus
885{
886#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
887 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
888 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
889 [] (const char32_t** sourceStart, const char32_t* sourceEnd, char8_t** targetStart,
890 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
891 return UTFConvert_libutfxx_::ConvertUTF32toUTF8_ (
892 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
893 });
894 }
895#endif
896 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF32toUTF8_);
897}
898auto UTFConvert::ConvertQuietly_StroikaPortable_ (optional<Character> invalidCharacterReplacement, span<const char16_t> source, span<char8_t> target)
899 -> ConversionResultWithStatus
900{
901#if qCompilerAndStdLib_arm_ubsan_callDirectFunInsteadOfThruLamdba_Buggy
902 if (Debug::kBuiltWithUndefinedBehaviorSanitizer) {
903 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target,
904 [] (const char16_t** sourceStart, const char16_t* sourceEnd, char8_t** targetStart,
905 char8_t* targetEnd, optional<char32_t> missingCharacterReplacement) {
906 return UTFConvert_libutfxx_::ConvertUTF16toUTF8_ (
907 sourceStart, sourceEnd, targetStart, targetEnd, missingCharacterReplacement);
908 });
909 }
910#endif
911 return ConvertQuietly_StroikaPortable_helper_ (invalidCharacterReplacement, source, target, UTFConvert_libutfxx_::ConvertUTF16toUTF8_);
912}
913
914namespace {
915 template <typename IN_T, typename OUT_T, typename FUN2DO_REAL_WORK>
916 inline auto ConvertQuietly_codeCvt_helper_ (span<const IN_T> source, const span<OUT_T> target, FUN2DO_REAL_WORK&& realWork) -> ConversionResultWithStatus
917 {
918 using namespace UTFConvert_codecvtSupport_;
919 const IN_T* sourceStart = reinterpret_cast<const IN_T*> (source.data ());
920 const IN_T* sourceEnd = sourceStart + source.size ();
921 OUT_T* targetStart = reinterpret_cast<OUT_T*> (target.data ());
922 OUT_T* targetEnd = targetStart + target.size ();
923 ConversionStatusFlag r = realWork (&sourceStart, sourceEnd, &targetStart, targetEnd);
924 if (r == ConversionStatusFlag::ok) {
925 return ConversionResultWithStatus{{static_cast<size_t> (sourceStart - reinterpret_cast<const IN_T*> (source.data ())),
926 static_cast<size_t> (targetStart - reinterpret_cast<const OUT_T*> (target.data ()))},
927 ConversionStatusFlag::ok};
928 }
929 else {
930 return ConversionResultWithStatus{{0, 0}, r};
931 }
932 }
933}
934auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char8_t> source, span<char16_t> target) -> ConversionResultWithStatus
935{
936 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF8toUTF16_codecvt_);
937}
938auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char16_t> source, span<char8_t> target) -> ConversionResultWithStatus
939{
940 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF16toUTF8_codecvt_);
941}
942auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char8_t> source, span<char32_t> target) -> ConversionResultWithStatus
943{
944 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF8toUTF32_codecvt_);
945}
946auto UTFConvert::ConvertQuietly_codeCvt_ (span<const char32_t> source, span<char8_t> target) -> ConversionResultWithStatus
947{
948 return ConvertQuietly_codeCvt_helper_ (source, target, UTFConvert_codecvtSupport_::ConvertUTF32toUTF8_codecvt_);
949}
950
951void UTFConvert::Throw (ConversionStatusFlag cr, size_t errorAtSourceOffset)
952{
953 switch (cr) {
955 static const auto kException_ = Execution::Exception<runtime_error>{"Invalid UNICODE source string (incomplete UTF character)"sv};
956 Execution::Throw (kException_);
957 }
960 }
961 default:
963 }
964}
#define RequireNotReached()
Definition Assertions.h:388
#define AssertNotReached()
Definition Assertions.h:358
#define DbgTrace
Definition Trace.h:327
An error occurred encoding or decoding a character
constexpr pair< char16_t, char16_t > GetSurrogatePair() const
ConversionStatusFlag
used for ConvertQuietly
Definition UTFConvert.h:279
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:219
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54