Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
CodeCvt.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <algorithm>
5#include <bit>
6
13
15
16 namespace Private_ {
17 // crazy - from https://en.cppreference.com/w/cpp/locale/codecvt
18 DISABLE_COMPILER_MSC_WARNING_START (4996)
19 template <typename FACET>
20 struct deletable_facet_ : FACET {
21 template <typename... Args>
22 deletable_facet_ (Args&&... args)
23 : FACET{forward<Args> (args)...}
24 {
25 }
26 ~deletable_facet_ () = default;
27 };
28 DISABLE_COMPILER_MSC_WARNING_END (4996)
29 void ThrowErrorConvertingBytes2Characters_ (size_t nSrcCharsWhereError);
30 void ThrowErrorConvertingCharacters2Bytes_ (size_t nSrcCharsWhereError);
31 void ThrowCodePageNotSupportedException_ (CodePage cp);
32 void ThrowCharsetNotSupportedException_ (const Charset& charset);
33#if qStroika_Platform_Windows
34 CodePage GetANSICodePage_ ();
35#endif
36 }
37
38 /*
39 ********************************************************************************
40 ***************************** CodeCvt<CHAR_T>::IRep ****************************
41 ********************************************************************************
42 */
43 template <IUNICODECanAlwaysConvertTo CHAR_T>
44 size_t CodeCvt<CHAR_T>::IRep::_Bytes2Characters (span<const byte> from) const
45 {
46 Memory::StackBuffer<CHAR_T> to{this->ComputeTargetCharacterBufferSize (from)};
47 return this->Bytes2Characters (&from, span{to}).size ();
48 }
49 template <IUNICODECanAlwaysConvertTo CHAR_T>
50 size_t CodeCvt<CHAR_T>::IRep::_Characters2Bytes (span<const CHAR_T> from) const
51 {
52 Memory::StackBuffer<byte> to{this->ComputeTargetByteBufferSize (from)};
53 return this->Characters2Bytes (from, span{to}).size ();
54 }
55
56 /*
57 ********************************************************************************
58 *********************** CodeCvt<CHAR_T>::UTFConvertRep_ ************************
59 ********************************************************************************
60 */
61 template <IUNICODECanAlwaysConvertTo CHAR_T>
62#if qCompilerAndStdLib_template_second_concept_Buggy
63 template <typename SERIALIZED_CHAR_T>
64#else
65 template <IUNICODECanAlwaysConvertTo SERIALIZED_CHAR_T>
66#endif
67 struct CodeCvt<CHAR_T>::UTFConvertRep_ : CodeCvt<CHAR_T>::IRep {
68 using ConversionResult = UTFConvert::ConversionResult;
69 using ConversionResultWithStatus = UTFConvert::ConversionResultWithStatus;
70 using ConversionStatusFlag = UTFConvert::ConversionStatusFlag;
71 UTFConvertRep_ (const Options& o)
72 : fCodeConverter_{o.fInvalidCharacterReplacement
73 ? UTFConvert{UTFConvert::Options{.fInvalidCharacterReplacement = *o.fInvalidCharacterReplacement}}
74 : UTFConvert::kThe}
75 {
76 }
77 virtual Options GetOptions () const override
78 {
79 return Options{.fInvalidCharacterReplacement = fCodeConverter_.GetOptions ().fInvalidCharacterReplacement};
80 }
81 virtual span<CHAR_T> Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const override
82 {
83 RequireNotNull (from);
84 Require (to.size () >= ComputeTargetCharacterBufferSize (*from));
85 span<const SERIALIZED_CHAR_T> serializedFrom = ReinterpretBytes_ (*from);
86 Assert (serializedFrom.size_bytes () <= from->size ()); // note - serializedFrom could be smaller than from in byte-span
87 ConversionResultWithStatus r = fCodeConverter_.ConvertQuietly (serializedFrom, to);
88 if (r.fStatus == ConversionStatusFlag::sourceIllegal) {
89 UTFConvert::Throw (r.fStatus, r.fSourceConsumed);
90 }
91 *from = from->subspan (r.fSourceConsumed * sizeof (SERIALIZED_CHAR_T)); // from updated to remaining data, if any
92 return to.subspan (0, r.fTargetProduced); // point ACTUAL copied data
93 }
94 virtual span<byte> Characters2Bytes (span<const CHAR_T> from, span<byte> to) const override
95 {
96 Require (to.size () >= ComputeTargetByteBufferSize (from));
97 span<SERIALIZED_CHAR_T> serializedTo = ReinterpretBytes_ (to);
98 ConversionResult r = fCodeConverter_.Convert (from, serializedTo); // cannot have sourceExhausted here so no need to call ConvertQuietly
99 Require (r.fSourceConsumed == from.size ()); // always use all input characters
100 return to.subspan (0, r.fTargetProduced * sizeof (SERIALIZED_CHAR_T)); // point ACTUAL copied data
101 }
102 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override
103 {
104 if (const size_t* i = get_if<size_t> (&src)) {
105 return UTFConvert::ComputeTargetBufferSize<CHAR_T, SERIALIZED_CHAR_T> (*i / sizeof (SERIALIZED_CHAR_T));
106 }
107 else {
108 return UTFConvert::ComputeTargetBufferSize<CHAR_T> (ReinterpretBytes_ (get<span<const byte>> (src)));
109 }
110 }
111 virtual size_t ComputeTargetByteBufferSize (variant<span<const CHAR_T>, size_t> src) const override
112 {
113 if (const size_t* i = get_if<size_t> (&src)) {
114 return UTFConvert::ComputeTargetBufferSize<SERIALIZED_CHAR_T, CHAR_T> (*i) * sizeof (SERIALIZED_CHAR_T);
115 }
116 else {
117 return UTFConvert::ComputeTargetBufferSize<SERIALIZED_CHAR_T> (get<span<const CHAR_T>> (src)) * sizeof (SERIALIZED_CHAR_T);
118 }
119 }
120 /*
121 * essentially 'cast' from bytes to from SERIALIZED_CHAR_T (could be char8_t, char16_t or whatever works with UTFConvert)
122 */
123 static span<const SERIALIZED_CHAR_T> ReinterpretBytes_ (span<const byte> s)
124 {
125 return span<const SERIALIZED_CHAR_T>{reinterpret_cast<const SERIALIZED_CHAR_T*> (s.data ()), s.size () / sizeof (SERIALIZED_CHAR_T)};
126 }
127 static span<SERIALIZED_CHAR_T> ReinterpretBytes_ (span<byte> s)
128 {
129 return span<SERIALIZED_CHAR_T>{reinterpret_cast<SERIALIZED_CHAR_T*> (s.data ()), s.size () / sizeof (SERIALIZED_CHAR_T)};
130 }
131 UTFConvert fCodeConverter_;
132 };
133
134 /*
135 ********************************************************************************
136 ********************* CodeCvt<CHAR_T>::Latin1ConvertRep_ ***********************
137 ********************************************************************************
138 */
139 template <IUNICODECanAlwaysConvertTo CHAR_T>
140 struct CodeCvt<CHAR_T>::Latin1ConvertRep_ : CodeCvt<CHAR_T>::IRep {
141 using ConversionResult = UTFConvert::ConversionResult;
142 using ConversionResultWithStatus = UTFConvert::ConversionResultWithStatus;
143 using ConversionStatusFlag = UTFConvert::ConversionStatusFlag;
144 Latin1ConvertRep_ (const Options& o)
145 : fCodeConverter_{o.fInvalidCharacterReplacement
146 ? UTFConvert{UTFConvert::Options{.fInvalidCharacterReplacement = *o.fInvalidCharacterReplacement}}
147 : UTFConvert::kThe}
148 {
149 }
150 virtual Options GetOptions () const override
151 {
152 return Options{.fInvalidCharacterReplacement = fCodeConverter_.GetOptions ().fInvalidCharacterReplacement};
153 }
154 virtual span<CHAR_T> Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const override
155 {
156 RequireNotNull (from);
157 Require (to.size () >= ComputeTargetCharacterBufferSize (*from));
158 span<const Latin1> serializedFrom = ReinterpretBytes_ (*from);
159 Assert (serializedFrom.size_bytes () <= from->size ()); // note - serializedFrom could be smaller than from in bytespan
160 ConversionResultWithStatus r = fCodeConverter_.ConvertQuietly (serializedFrom, to);
161 if (r.fStatus == ConversionStatusFlag::sourceIllegal) {
162 UTFConvert::Throw (r.fStatus, r.fSourceConsumed);
163 }
164 *from = from->subspan (r.fSourceConsumed); // from updated to remaining data, if any
165 return to.subspan (0, r.fTargetProduced); // point ACTUAL copied data
166 }
167 virtual span<byte> Characters2Bytes ([[maybe_unused]] span<const CHAR_T> from, [[maybe_unused]] span<byte> to) const override
168 {
169 RequireNotReached (); // doesn't work in general, so disallow
170 return span<byte>{};
171 }
172 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override
173 {
174 if (const size_t* i = get_if<size_t> (&src)) {
175 return UTFConvert::ComputeTargetBufferSize<CHAR_T, Latin1> (*i / sizeof (Latin1));
176 }
177 else {
178 return UTFConvert::ComputeTargetBufferSize<CHAR_T> (ReinterpretBytes_ (get<span<const byte>> (src)));
179 }
180 }
181 virtual size_t ComputeTargetByteBufferSize ([[maybe_unused]] variant<span<const CHAR_T>, size_t> src) const override
182 {
183 RequireNotReached (); // doesn't work in general, so disallow
184 return 0;
185 }
186 /*
187 * essentially 'cast' from bytes to from Latin1 (could be char8_t, char16_t or whatever works with UTFConvert)
188 */
189 static span<const Latin1> ReinterpretBytes_ (span<const byte> s)
190 {
191 return span<const Latin1>{reinterpret_cast<const Latin1*> (s.data ()), s.size () / sizeof (Latin1)};
192 }
193 static span<Latin1> ReinterpretBytes_ (span<byte> s)
194 {
195 return span<Latin1>{reinterpret_cast<Latin1*> (s.data ()), s.size () / sizeof (Latin1)};
196 }
197 UTFConvert fCodeConverter_;
198 };
199
200 /*
201 ********************************************************************************
202 ****************** CodeCvt<CHAR_T>::UTFConvertSwappedRep_ **********************
203 ********************************************************************************
204 */
205 template <IUNICODECanAlwaysConvertTo CHAR_T>
206#if qCompilerAndStdLib_template_second_concept_Buggy
207 template <typename SERIALIZED_CHAR_T>
208#else
209 template <IUNICODECanAlwaysConvertTo SERIALIZED_CHAR_T>
210#endif
211 struct CodeCvt<CHAR_T>::UTFConvertSwappedRep_ : UTFConvertRep_<SERIALIZED_CHAR_T> {
212 using inherited = UTFConvertRep_<SERIALIZED_CHAR_T>;
213 UTFConvertSwappedRep_ (const Options& o)
214 : inherited{o}
215 {
216 }
217 virtual span<CHAR_T> Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const override
218 {
219 RequireNotNull (from);
220 Require (to.size () >= this->ComputeTargetCharacterBufferSize (*from));
221 Memory::StackBuffer<byte> nativeOrderBytes{*from};
222 /*
223 * What is in the other byte order is each SERIALIZED_CHAR_T code unit - so swap the bytes,
224 * and leave decoding to inherited.
225 */
226 SwapEachCodeUnit_ (span<byte>{nativeOrderBytes.data (), nativeOrderBytes.size ()});
227 span<const byte> remaining{nativeOrderBytes.data (), nativeOrderBytes.size ()};
228 span<CHAR_T> r = inherited::Bytes2Characters (&remaining, to);
229 *from = from->subspan (from->size () - remaining.size ()); // from updated to remaining data, if any
230 return r;
231 }
232 virtual span<byte> Characters2Bytes (span<const CHAR_T> from, span<byte> to) const override
233 {
234 Require (to.size () >= this->ComputeTargetByteBufferSize (from));
235 span<byte> r = inherited::Characters2Bytes (from, to);
236 SwapEachCodeUnit_ (r);
237 return r;
238 }
239 static void SwapEachCodeUnit_ (span<byte> bytes)
240 {
241 // a trailing partial code unit is left alone - Bytes2Characters leaves it unconsumed, for the next call
242 for (size_t i = 0; i + sizeof (SERIALIZED_CHAR_T) <= bytes.size (); i += sizeof (SERIALIZED_CHAR_T)) {
243 reverse (bytes.begin () + i, bytes.begin () + i + sizeof (SERIALIZED_CHAR_T));
244 }
245 }
246 };
247
248 /*
249 ********************************************************************************
250 *********************** CodeCvt<CHAR_T>::UTF2UTFRep_ ***************************
251 ********************************************************************************
252 */
253 /*
254 * Utility rep to wrap some kind of rep along with (optional) UTFConvert, to complete
255 * conversion from bytes to/from desired rep generally through some intermediary rep.
256 *
257 * NOTE - this code allows INTERMEDIATE_CHAR_T == CHAR_T special case, and is optimized to do
258 * nothing for that case (or should be - maybe needs a bit more tweaking of implementation for that to be fully true).
259 */
260 template <IUNICODECanAlwaysConvertTo CHAR_T>
261#if qCompilerAndStdLib_template_second_concept_Buggy
262 template <typename INTERMEDIATE_CHAR_T>
263#else
264 template <IUNICODECanAlwaysConvertTo INTERMEDIATE_CHAR_T>
265#endif
266 struct CodeCvt<CHAR_T>::UTF2UTFRep_ : CodeCvt<CHAR_T>::IRep {
267 using ConversionResultWithStatus = UTFConvert::ConversionResultWithStatus;
268 using ConversionStatusFlag = UTFConvert::ConversionStatusFlag;
269 UTF2UTFRep_ (const CodeCvt<INTERMEDIATE_CHAR_T>& origCodeCvt)
270 requires (sizeof (CHAR_T) == sizeof (INTERMEDIATE_CHAR_T))
271 : fBytesVSIntermediateCvt_{origCodeCvt}
272 {
273 }
274 UTF2UTFRep_ (const CodeCvt<INTERMEDIATE_CHAR_T>& origCodeCvt, const UTFConvert& secondStep = {})
275 requires (sizeof (CHAR_T) != sizeof (INTERMEDIATE_CHAR_T))
276 : fBytesVSIntermediateCvt_{origCodeCvt}
277 , fIntermediateVSFinalCHARCvt_{secondStep}
278 {
279 }
280 virtual Options GetOptions () const override
281 {
282 // Not 100% right cuz ignoring - fIntermediateVSFinalCHARCvt_ - LGP - 2023-08-07
283 return Options{.fInvalidCharacterReplacement = fBytesVSIntermediateCvt_.GetOptions ().fInvalidCharacterReplacement};
284 }
285 virtual span<CHAR_T> Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const override
286 {
287 RequireNotNull (from);
288 Require (to.size () >= ComputeTargetCharacterBufferSize (*from) or to.size () >= this->_Bytes2Characters (*from));
289 if constexpr (sizeof (CHAR_T) == sizeof (INTERMEDIATE_CHAR_T)) {
290 return span<CHAR_T>{to.begin (),
291 fBytesVSIntermediateCvt_.Bytes2Characters (from, Memory::SpanBytesCast<span<INTERMEDIATE_CHAR_T>> (to)).size ()};
292 }
293 else {
294 /*
295 * Big picture: fBytesVSIntermediateCvt_ goes bytes -> INTERMEDIATE_CHAR_T, so we use it first.
296 *
297 * BUT - trick - even if we successfully do first conversion (bytes -> INTERMEDIATE_CHAR_T) - we might still get a split
298 * char on the second conversion (RARE). If so - we need to backup in 'from' - to avoid this. Just allege we consumed less. This MIGHT -
299 * in extreme cases - go all the way back to zero.
300 */
301 while (true) {
302 // Because we KNOW everything will fit (disallow target exhausted), we can allocate a temporary buffer for the intermediate state, and be done with
303 // it by the end of this routine (stay stateless)
304 Memory::StackBuffer<INTERMEDIATE_CHAR_T> intermediateBuf{fBytesVSIntermediateCvt_.ComputeTargetCharacterBufferSize (*from)};
305 span<const INTERMEDIATE_CHAR_T> intermediateSpan = fBytesVSIntermediateCvt_.Bytes2Characters (from, intermediateBuf); // shortens 'from' if needed
306
307 // then use fIntermediateVSFinalCHARCvt_ to perform final mapping INTERMEDIATE_CHAR_T -> CHAR_T
308 ConversionResultWithStatus cr = fIntermediateVSFinalCHARCvt_.ConvertQuietly (intermediateSpan, to);
309 switch (cr.fStatus) {
310 case ConversionStatusFlag::sourceIllegal:
311 UTFConvert::Throw (cr.fStatus, cr.fSourceConsumed);
312 case ConversionStatusFlag::sourceExhausted:
313 // TRICKY - if we have at least one character output, then we need to back out bytes 'from' - til this doesn't happen
314 if (not from->empty ()) {
315 *from = from->subspan (0, from->size () - 1);
316 continue; // 'goto try again'
317 }
318 else {
319 return span<CHAR_T>{}; // no update to 'from' since we consumed no characters
320 }
321 case ConversionStatusFlag::ok:
322 return to.subspan (0, cr.fTargetProduced);
323 default:
325 return span<CHAR_T>{};
326 }
327 }
328 }
329 }
330 virtual span<byte> Characters2Bytes (span<const CHAR_T> from, span<byte> to) const override
331 {
332 Require (to.size () >= ComputeTargetByteBufferSize (from) or to.size () >= this->_Characters2Bytes (from));
333 if constexpr (sizeof (CHAR_T) == sizeof (INTERMEDIATE_CHAR_T)) {
334 return fBytesVSIntermediateCvt_.Characters2Bytes (Memory::SpanBytesCast<span<const INTERMEDIATE_CHAR_T>> (from), to);
335 }
336 else {
337 /*
338 * Because we KNOW everything will fit, we can allocate a temporary buffer for the intermediate state, and be done with
339 * it by the end of this routine (stay stateless)
340 */
341 Memory::StackBuffer<INTERMEDIATE_CHAR_T> intermediateBuf{
342 fIntermediateVSFinalCHARCvt_.template ComputeTargetBufferSize<INTERMEDIATE_CHAR_T> (from)};
343
344 /*
345 * first translate to something usable by fBytesVSIntermediateCvt_
346 */
347 span<INTERMEDIATE_CHAR_T> intermediateSpan =
348 fIntermediateVSFinalCHARCvt_.ConvertSpan (from, span<INTERMEDIATE_CHAR_T>{intermediateBuf.data (), intermediateBuf.size ()});
349
350 // Then use fBytesVSIntermediateCvt_, no need to track anything in intermediateBuf, we require all used, no partials etc.
351 return fBytesVSIntermediateCvt_.Characters2Bytes (intermediateSpan, to);
352 }
353 }
354 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override
355 {
356 size_t intermediateCharCntMax = [&] () {
357 if (const size_t* i = get_if<size_t> (&src)) {
358 return fBytesVSIntermediateCvt_.ComputeTargetCharacterBufferSize (*i);
359 }
360 else {
361 return fBytesVSIntermediateCvt_.ComputeTargetCharacterBufferSize (get<span<const byte>> (src));
362 }
363 }();
364 if constexpr (sizeof (CHAR_T) == sizeof (INTERMEDIATE_CHAR_T)) {
365 return intermediateCharCntMax;
366 }
367 else {
368 return fIntermediateVSFinalCHARCvt_.template ComputeTargetBufferSize<CHAR_T, INTERMEDIATE_CHAR_T> (intermediateCharCntMax);
369 }
370 }
371 virtual size_t ComputeTargetByteBufferSize (variant<span<const CHAR_T>, size_t> src) const override
372 {
373 size_t intermediateCharCntMax = [&] () {
374 if constexpr (sizeof (CHAR_T) == sizeof (INTERMEDIATE_CHAR_T)) {
375 if (const size_t* i = get_if<size_t> (&src)) {
376 return *i;
377 }
378 else {
379 return get<span<const CHAR_T>> (src).size ();
380 }
381 }
382 else {
383 if (const size_t* i = get_if<size_t> (&src)) {
384 return fIntermediateVSFinalCHARCvt_.template ComputeTargetBufferSize<INTERMEDIATE_CHAR_T, CHAR_T> (*i);
385 }
386 else {
387 return fIntermediateVSFinalCHARCvt_.template ComputeTargetBufferSize<INTERMEDIATE_CHAR_T> (get<span<const CHAR_T>> (src));
388 }
389 }
390 }();
391 return fBytesVSIntermediateCvt_.ComputeTargetByteBufferSize (intermediateCharCntMax);
392 }
393 CodeCvt<INTERMEDIATE_CHAR_T> fBytesVSIntermediateCvt_;
394 conditional_t<sizeof (CHAR_T) != sizeof (INTERMEDIATE_CHAR_T), UTFConvert, byte> fIntermediateVSFinalCHARCvt_; // would like to remove field if sizeof ==, but not sure how (void doesnt work)
395 };
396
397 /*
398 * This is crazy complicated because codecvt goes out of its way to be hard to copy, hard to move, but with
399 * a little care, can be made to work with unique_ptr.
400 *
401 * Also, std::codecvt doesn't natively support fInvalidCharacterReplacement, so we have to support manually.
402 */
403 template <IUNICODECanAlwaysConvertTo CHAR_T>
404 template <typename STD_CODE_CVT_T>
405 struct CodeCvt<CHAR_T>::CodeCvt_WrapStdCodeCvt_ : CodeCvt<CHAR_T>::IRep {
406 unique_ptr<STD_CODE_CVT_T> fCodeCvt_;
407 optional<Character> fInvalidCharacterReplacement_;
408 optional<span<byte>> fInvalidCharacterReplacementBytes_;
409 using extern_type = typename STD_CODE_CVT_T::extern_type;
410 extern_type fInvalidCharacterReplacementBytesBuf[8]; // WAG at sufficient size, but sb enuf
411 static_assert (same_as<CHAR_T, typename STD_CODE_CVT_T::intern_type>);
412#if qCompilerAndStdLib_arm_asan_FaultStackUseAfterScope_Buggy
413 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_ADDRESS
414#endif
415 CodeCvt_WrapStdCodeCvt_ (const Options& options, unique_ptr<STD_CODE_CVT_T>&& codeCvt)
416 : fCodeCvt_{move (codeCvt)}
417 , fInvalidCharacterReplacement_{options.fInvalidCharacterReplacement}
418 {
419 if (fInvalidCharacterReplacement_) {
420 mbstate_t ignoredMBState{};
421 Memory::StackBuffer<CHAR_T> tmpBuf;
422 span<const CHAR_T> invalCharPartlyEncode = fInvalidCharacterReplacement_->As<CHAR_T> (&tmpBuf);
423 const CHAR_T* ignoreCharsConsumed = nullptr;
424 extern_type* bytesInvalChar = fInvalidCharacterReplacementBytesBuf;
426 auto r = fCodeCvt_->out (ignoredMBState, invalCharPartlyEncode.data (),
427 invalCharPartlyEncode.data () + invalCharPartlyEncode.size (), ignoreCharsConsumed,
428 fInvalidCharacterReplacementBytesBuf,
429 fInvalidCharacterReplacementBytesBuf + size (fInvalidCharacterReplacementBytesBuf), bytesInvalChar);
430 DISABLE_COMPILER_MSC_WARNING_END (4996)
431 if (r == STD_CODE_CVT_T::ok) [[likely]] {
432 fInvalidCharacterReplacementBytes_ = as_writable_bytes (
433 span{fInvalidCharacterReplacementBytesBuf}.subspan (0, bytesInvalChar - fInvalidCharacterReplacementBytesBuf));
434 }
435 // else this encoding cannot represent the replacement - so Bytes2Characters still uses it, and
436 // Characters2Bytes throws on an invalid character, as if there were none (see Options::fInvalidCharacterReplacement)
437 }
438 }
439 virtual Options GetOptions () const override
440 {
441 return Options{.fInvalidCharacterReplacement = fInvalidCharacterReplacement_};
442 }
443 virtual span<CHAR_T> Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const override
444 {
445 RequireNotNull (from);
446 Require (to.size () >= ComputeTargetCharacterBufferSize (*from));
447 const extern_type* _First1 = reinterpret_cast<const extern_type*> (from->data ());
448 const extern_type* _Last1 = _First1 + from->size ();
449 const extern_type* _Mid1 = _First1; // DOUBLE CHECK SPEC - NOT SURE IF THIS IS USED ON INPUT
450 CHAR_T* _First2 = to.data ();
451 CHAR_T* _Last2 = _First2 + to.size ();
452 CHAR_T* _Mid2 = _First2; // DOUBLE CHECK SPEC - NOT SURE IF THIS IS USED ON INPUT
453 mbstate_t ignoredMBState{};
454 size_t bytesDone = 0;
455 size_t charsDone = 0;
456 continueWith:
457 auto r = fCodeCvt_->in (ignoredMBState, _First1 + bytesDone, _Last1, _Mid1, _First2 + charsDone, _Last2, _Mid2);
458 // An invalid byte is 'error' to most codecvts, but 'partial' to some (libc++) - and only fewer than max_length ()
459 // bytes can be a character that the input ends inside of
460 if (r == STD_CODE_CVT_T::partial and _Last1 - _Mid1 >= fCodeCvt_->max_length ()) {
461 r = STD_CODE_CVT_T::error;
462 }
463 if (r == STD_CODE_CVT_T::partial) {
464 *from = from->subspan (static_cast<size_t> (_Mid1 - _First1)); // the remaining bytes - a character the input ends inside of
465 Assert (from->size () != 0);
466 }
467 else if (r != STD_CODE_CVT_T::ok) {
468 if (fInvalidCharacterReplacement_) {
469 bytesDone = _Mid1 - _First1 + 1; // skip one byte and try again (no idea how many bytes would have been best to skip)
470 charsDone = _Mid2 - _First2;
471 ignoredMBState = mbstate_t{}; // unspecified after an error
472
473 Memory::StackBuffer<CHAR_T> badCharTmpBuf;
474 span<const CHAR_T> badCharReplaceSpan = fInvalidCharacterReplacement_->As<CHAR_T> (&badCharTmpBuf);
475 span<CHAR_T> copied = Memory::CopyBytes (badCharReplaceSpan, span{&_First2[charsDone], _Last2});
476 Assert (copied.size () >= 0);
477 charsDone += copied.size ();
478 Assert (charsDone <= to.size ());
479 goto continueWith;
480 }
481 else {
482 Private_::ThrowErrorConvertingBytes2Characters_ (_Mid1 - _First1);
483 }
484 }
485 else {
486 Require (_Mid1 == _Last1);
487 *from = span<const byte>{}; // used all input
488 }
489 return to.subspan (0, _Mid2 - _First2); // point ACTUAL copied data
490 }
491 virtual span<byte> Characters2Bytes (span<const CHAR_T> from, span<byte> to) const override
492 {
493 Require (to.size () >= ComputeTargetByteBufferSize (from));
494 const CHAR_T* _First1 = from.data ();
495 const CHAR_T* _Last1 = _First1 + from.size ();
496 const CHAR_T* _Mid1 = _First1; // DOUBLE CHECK SPEC - NOT SURE IF THIS IS USED ON INPUT
497 extern_type* _First2 = reinterpret_cast<extern_type*> (to.data ());
498 extern_type* _Last2 = _First2 + to.size ();
499 extern_type* _Mid2 = _First2; // DOUBLE CHECK SPEC - NOT SURE IF THIS IS USED ON INPUT
500 mbstate_t ignoredMBState{};
501 size_t charsDone = 0;
502 size_t bytesDone = 0;
503 continueWith:
504 auto r = fCodeCvt_->out (ignoredMBState, _First1 + charsDone, _Last1, _Mid1, _First2 + bytesDone, _Last2, _Mid2);
505 if (r != STD_CODE_CVT_T::ok) {
506 if (fInvalidCharacterReplacementBytes_) {
507 charsDone = _Mid1 - _First1 + 1; // skip one character and try again
508 bytesDone = _Mid2 - _First2;
509 memcpy (_First2 + bytesDone, fInvalidCharacterReplacementBytes_->data (), fInvalidCharacterReplacementBytes_->size ());
510 bytesDone += fInvalidCharacterReplacementBytes_->size ();
511 goto continueWith;
512 }
513 else {
514 Private_::ThrowErrorConvertingCharacters2Bytes_ (_Mid1 - _First1);
515 }
516 }
517 Require (_Mid1 == _Last1); // used all input
518 return to.subspan (0, _Mid2 - _First2); // point ACTUAL copied data
519 }
520 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override
521 {
522 // at most one character per byte, and std::codecvt doesn't appear to offer API to compute better
523 if (const size_t* i = get_if<size_t> (&src)) {
524 return *i;
525 }
526 else {
527 return get<span<const byte>> (src).size ();
528 }
529 }
530 virtual size_t ComputeTargetByteBufferSize (variant<span<const CHAR_T>, size_t> src) const override
531 {
532 if (const size_t* i = get_if<size_t> (&src)) {
533 return (*i) * fCodeCvt_->max_length ();
534 }
535 else {
536 // std::codecvt doesn't appear to provide an API to compute needed buffer length (just the reverse -
537 // for a buffer length, how many bytes consumed).
538 return get<span<const CHAR_T>> (src).size () * fCodeCvt_->max_length ();
539 }
540 }
541 };
542
543 namespace Private_ {
544 // a lot of old, important character sets can be represented this way (like old PC character sets for non-asian languages)
545 struct BuiltinSingleByteTableCodePageRep_ final : CodeCvt<char16_t>::IRep {
546 BuiltinSingleByteTableCodePageRep_ (CodePage cp, optional<Character> invalidCharacterReplacement);
547 virtual ~BuiltinSingleByteTableCodePageRep_ () = default;
548 virtual CodeCvt<char16_t>::Options GetOptions () const override
549 {
550 optional<char16_t> invalRepChar;
551 if (fInvalidCharacterReplacementByte_ != nullopt) {
552 char16_t x;
553 auto byteSpan = span{&*fInvalidCharacterReplacementByte_, 1};
554 (void)this->Bytes2Characters (&byteSpan, span{&x, 1});
555 invalRepChar = x;
556 }
557 return CodeCvt<char16_t>::Options{.fInvalidCharacterReplacement = invalRepChar};
558 }
559 virtual span<char16_t> Bytes2Characters (span<const byte>* from, span<char16_t> to) const override;
560 virtual span<byte> Characters2Bytes (span<const char16_t> from, span<byte> to) const override;
561 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override;
562 virtual size_t ComputeTargetByteBufferSize (variant<span<const char16_t>, size_t> src) const override;
563 const char16_t* fMap_;
564 optional<byte> fInvalidCharacterReplacementByte_;
565 };
566#if qStroika_Platform_Windows
567 struct WindowsNative_ final : CodeCvt<char16_t>::IRep {
568 constexpr WindowsNative_ (CodePage cp)
569 : fCodePage_{cp}
570 {
571 }
572 virtual ~WindowsNative_ () = default;
573 virtual CodeCvt<char16_t>::Options GetOptions () const override
574 {
575 return {};
576 }
577 virtual span<char16_t> Bytes2Characters (span<const byte>* from, span<char16_t> to) const override;
578 virtual span<byte> Characters2Bytes (span<const char16_t> from, span<byte> to) const override;
579 virtual size_t ComputeTargetCharacterBufferSize (variant<span<const byte>, size_t> src) const override;
580 virtual size_t ComputeTargetByteBufferSize (variant<span<const char16_t>, size_t> src) const override;
581 CodePage fCodePage_;
582 };
583#endif
584 }
585
586 /*
587 ********************************************************************************
588 ************************* CodeCvt<CHAR_T>::Options *****************************
589 ********************************************************************************
590 */
591 template <IUNICODECanAlwaysConvertTo CHAR_T>
592 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (IUNICODECanAlwaysConvertTo) FROM_CHAR_T_OPTIONS>
593 constexpr inline auto CodeCvt<CHAR_T>::Options::New (typename CodeCvt<FROM_CHAR_T_OPTIONS>::Options o) -> Options
594 {
595 return Options{.fInvalidCharacterReplacement = o.fInvalidCharacterReplacement};
596 }
597
598 /*
599 ********************************************************************************
600 ******************************* CodeCvt<CHAR_T> ********************************
601 ********************************************************************************
602 */
603 template <IUNICODECanAlwaysConvertTo CHAR_T>
604 inline CodeCvt<CHAR_T>::CodeCvt (const Options& options)
605 : fRep_{Memory::MakeSharedPtr<UTFConvertRep_<char8_t>> (options)} // default, is to serialize to UTF-8
606 {
607 }
608 template <IUNICODECanAlwaysConvertTo CHAR_T>
609 inline CodeCvt<CHAR_T>::CodeCvt (const locale& l, const Options& options)
610 {
612 if constexpr (same_as<CHAR_T, wchar_t>) {
613 *this = mkFromStdCodeCvt<codecvt_byname<wchar_t, char, mbstate_t>> (options, l.name ());
614 }
615 else if constexpr (same_as<CHAR_T, char16_t> or same_as<CHAR_T, char32_t>) {
616 *this = mkFromStdCodeCvt<codecvt_byname<CHAR_T, char8_t, mbstate_t>> (options, l.name ());
617 }
618 else if constexpr (same_as<CHAR_T, Character>) {
619 fRep_ = Memory::MakeSharedPtr<UTF2UTFRep_<char32_t>> (CodeCvt<char32_t>::mkFromStdCodeCvt<codecvt_byname<char32_t, char8_t, mbstate_t>> (
620 CodeCvt<char32_t>::Options::New<CHAR_T> (options), l.name ()));
621 }
622 else {
623 // CHAR_T COULD be UTF-8, but not clear if/why that would be useful.
625 }
626 DISABLE_COMPILER_MSC_WARNING_END (4996)
627 }
628 template <IUNICODECanAlwaysConvertTo CHAR_T>
629 CodeCvt<CHAR_T>::CodeCvt (const Charset& charset, const Options& options)
630 {
631 if (charset == WellKnownCharsets::kISO_8859_1) {
632 fRep_ = Memory::MakeSharedPtr<Latin1ConvertRep_> (options);
633 }
634 else if (charset == WellKnownCharsets::kUTF8) {
635 *this = CodeCvt<CHAR_T>{UnicodeExternalEncodings::eUTF8};
636 }
637 else if (same_as<CHAR_T, Character>) {
639 // best hope is to treat it as a locale name, and hope its found
640 fRep_ = Memory::MakeSharedPtr<UTF2UTFRep_<char32_t>> (CodeCvt<char32_t>::mkFromStdCodeCvt<codecvt_byname<char32_t, char8_t, mbstate_t>> (
641 CodeCvt<char32_t>::Options::New<CHAR_T> (options), charset.AsNarrowSDKString ()));
642 DISABLE_COMPILER_MSC_WARNING_END (4996)
643 }
644 else {
645 Private_::ThrowCharsetNotSupportedException_ (charset);
646 }
647 }
648 template <IUNICODECanAlwaysConvertTo CHAR_T>
649 CodeCvt<CHAR_T>::CodeCvt (UnicodeExternalEncodings e, const Options& options)
650 : fRep_{}
651 {
652 switch (e) {
653 case UnicodeExternalEncodings::eUTF8:
654 fRep_ = Memory::MakeSharedPtr<UTFConvertRep_<char8_t>> (options);
655 break;
656 case UnicodeExternalEncodings::eUTF16_BE:
657 case UnicodeExternalEncodings::eUTF16_LE:
658 if (e == UnicodeExternalEncodings::eUTF16) {
659 fRep_ = Memory::MakeSharedPtr<UTFConvertRep_<char16_t>> (options);
660 }
661 else {
662 fRep_ = Memory::MakeSharedPtr<UTFConvertSwappedRep_<char16_t>> (options);
663 }
664 break;
665 case UnicodeExternalEncodings::eUTF32_BE:
666 case UnicodeExternalEncodings::eUTF32_LE:
667 if (e == UnicodeExternalEncodings::eUTF32) {
668 fRep_ = Memory::MakeSharedPtr<UTFConvertRep_<char32_t>> (options);
669 }
670 else {
671 fRep_ = Memory::MakeSharedPtr<UTFConvertSwappedRep_<char32_t>> (options);
672 }
673 break;
674 default:
676 }
677 }
678 template <IUNICODECanAlwaysConvertTo CHAR_T>
679 CodeCvt<CHAR_T>::CodeCvt (span<const byte>* guessFormatFrom, const optional<CodeCvt>& useElse, const Options& options)
680 : fRep_{}
681 {
682 RequireNotNull (guessFormatFrom);
683 Require (useElse == nullopt or useElse->GetOptions ().fInvalidCharacterReplacement == options.fInvalidCharacterReplacement);
684 if (optional<tuple<UnicodeExternalEncodings, size_t>> r = ReadByteOrderMark (*guessFormatFrom)) {
685 *guessFormatFrom = guessFormatFrom->subspan (get<size_t> (*r));
686 fRep_ = CodeCvt{get<UnicodeExternalEncodings> (*r), options}.fRep_;
687 }
688 else {
689 fRep_ = useElse ? useElse->fRep_ : CodeCvt{options}.fRep_;
690 }
691 }
692 template <IUNICODECanAlwaysConvertTo CHAR_T>
693 CodeCvt<CHAR_T>::CodeCvt (CodePage cp, const Options& options)
694 : fRep_{}
695 {
696 // A few we have builtin table converters for (BuiltinSingleByteTableCodePageRep_);
697 // a few are just UTF, and we can convert those.
698 // On windows, we can delegate to WindowsNative_
699 // else give up and throw not supported code page.
700 switch (cp) {
701 case WellKnownCodePages::kANSI:
702 case WellKnownCodePages::kMAC:
703 case WellKnownCodePages::kPC:
704 case WellKnownCodePages::kPCA:
705 case WellKnownCodePages::kGreek:
706 case WellKnownCodePages::kTurkish:
707 case WellKnownCodePages::kHebrew:
708 case WellKnownCodePages::kArabic:
709 fRep_ = Memory::MakeSharedPtr<UTF2UTFRep_<char16_t>> (CodeCvt<char16_t> (
710 Memory::MakeSharedPtr<Private_::BuiltinSingleByteTableCodePageRep_> (cp, options.fInvalidCharacterReplacement)));
711 break;
712 case WellKnownCodePages::kUTF8:
713 fRep_ = Memory::MakeSharedPtr<UTFConvertRep_<char8_t>> (options);
714 break;
715 case WellKnownCodePages::kUNICODE_WIDE:
716 fRep_ = Memory::MakeSharedPtr<UTFConvertRep_<char16_t>> (options);
717 break;
718 case WellKnownCodePages::kUNICODE_WIDE_BIGENDIAN:
719 fRep_ = Memory::MakeSharedPtr<UTFConvertSwappedRep_<char16_t>> (options);
720 break;
721 default:
722#if qStroika_Platform_Windows
723 if (options.fInvalidCharacterReplacement) {
724 Private_::ThrowCodePageNotSupportedException_ (cp); // WindowsNative_ doesn't support fInvalidCharacterReplacement
725 }
726 fRep_ = Memory::MakeSharedPtr<UTF2UTFRep_<char16_t>> (CodeCvt<char16_t> (Memory::MakeSharedPtr<Private_::WindowsNative_> (cp)));
727 break;
728#else
729 Private_::ThrowCodePageNotSupportedException_ (cp);
730#endif
731 }
732 }
733 template <IUNICODECanAlwaysConvertTo CHAR_T>
734 inline CodeCvt<CHAR_T> CodeCvt<CHAR_T>::PlatformDefault (const Options& options)
735 {
736#if qStroika_Platform_Windows
737 // the number, not CP_ACP, so a code page with a table built in uses it
738 return CodeCvt{Private_::GetANSICodePage_ (), options};
739#else
740 locale l = locale::classic ();
741 try {
742 l = locale{""};
743 }
744 catch (...) {
745 // an environment naming a locale this system lacks: as if it named none
746 }
747 const string name = l.name ();
748 if (name == "C" or name == "POSIX" or name.find ("UTF-8") != string::npos or name.find ("utf8") != string::npos) {
749 return CodeCvt{UnicodeExternalEncodings::eUTF8, options};
750 }
751 return CodeCvt{l, options};
752#endif
753 }
754 template <IUNICODECanAlwaysConvertTo CHAR_T>
755 template <IUNICODECanAlwaysConvertTo INTERMEDIATE_CHAR_T>
757 : fRep_{Memory::MakeSharedPtr<UTF2UTFRep_<INTERMEDIATE_CHAR_T>> (basedOn)}
758 {
759 }
760 template <IUNICODECanAlwaysConvertTo CHAR_T>
761 inline CodeCvt<CHAR_T>::CodeCvt (const shared_ptr<IRep>& rep)
762 : fRep_{(RequireExpression (rep != nullptr), rep)}
763 {
764 }
765 template <IUNICODECanAlwaysConvertTo CHAR_T>
766 template <IStdCodeCVT STD_CODECVT, typename... ARGS>
767 inline CodeCvt<CHAR_T> CodeCvt<CHAR_T>::mkFromStdCodeCvt (const Options& options, ARGS... args)
768 requires (same_as<CHAR_T, typename STD_CODECVT::intern_type>)
769 {
770 auto u = make_unique<Private_::deletable_facet_<STD_CODECVT>> (forward<ARGS> (args)...);
771 return CodeCvt<CHAR_T>{Memory::MakeSharedPtr<CodeCvt_WrapStdCodeCvt_<Private_::deletable_facet_<STD_CODECVT>>> (options, move (u))};
772 }
773 template <IUNICODECanAlwaysConvertTo CHAR_T>
774 inline auto CodeCvt<CHAR_T>::GetOptions () const -> Options
775 {
776 return fRep_->GetOptions ();
777 }
778 template <IUNICODECanAlwaysConvertTo CHAR_T>
779 inline auto CodeCvt<CHAR_T>::Bytes2Characters (span<const byte> from) const -> size_t
780 {
781 Memory::StackBuffer<CHAR_T> to{ComputeTargetCharacterBufferSize (from)};
782 return fRep_->Bytes2Characters (&from, span{to}).size ();
783 }
784 template <IUNICODECanAlwaysConvertTo CHAR_T>
785 inline auto CodeCvt<CHAR_T>::Bytes2Characters (span<const byte>* from, span<CHAR_T> to) const -> span<CHAR_T>
786 {
787 RequireNotNull (from);
788 AssertNotNull (fRep_);
789 Require (to.size () >= ComputeTargetCharacterBufferSize (*from) or to.size () >= Bytes2Characters (*from)); // ComputeTargetCharacterBufferSize cheaper to compute
790 auto r = fRep_->Bytes2Characters (from, to);
791 Ensure (from->size () < 10); // can only contain bytes for a partial character so must be small, typically one or two or zero
792 WeakAssert (from->size () <= 3); // UTF-8: The maximum is 4 bytes per character, but could be other code sequence even worse
793 return r;
794 }
795 template <IUNICODECanAlwaysConvertTo CHAR_T>
796 inline auto CodeCvt<CHAR_T>::Bytes2Characters (span<const byte> from, span<CHAR_T> to) const -> span<CHAR_T>
797 {
798 AssertNotNull (fRep_);
799 Require (to.size () >= ComputeTargetCharacterBufferSize (from) or to.size () >= Bytes2Characters (from)); // ComputeTargetCharacterBufferSize cheaper to compute
800 size_t origSize = from.size ();
801 auto result = fRep_->Bytes2Characters (&from, to);
802 if (not from.empty ()) {
803 // the input ends inside a character: with a replacement, that is one more invalid character
804 if (optional<Character> replacement = GetOptions ().fInvalidCharacterReplacement) {
805 if constexpr (same_as<CHAR_T, Character>) {
806 Require (result.size () < to.size ());
807 to[result.size ()] = *replacement;
808 return to.subspan (0, result.size () + 1);
809 }
810 else {
811 Memory::StackBuffer<CHAR_T> replacementBuf;
812 span<const CHAR_T> replacementChars = replacement->As<CHAR_T> (&replacementBuf);
813 Require (result.size () + replacementChars.size () <= to.size ());
814 span<CHAR_T> copied = Memory::CopyBytes (replacementChars, to.subspan (result.size ()));
815 return to.subspan (0, result.size () + copied.size ());
816 }
817 }
818 Private_::ThrowErrorConvertingBytes2Characters_ (origSize - from.size ());
819 }
820 return result;
821 }
822 template <IUNICODECanAlwaysConvertTo CHAR_T>
823 inline auto CodeCvt<CHAR_T>::Characters2Bytes (span<const CHAR_T> from) const -> size_t
824 {
825 Memory::StackBuffer<byte> to{ComputeTargetByteBufferSize (from)};
826 return fRep_->Characters2Bytes (from, span{to}).size ();
827 }
828 template <IUNICODECanAlwaysConvertTo CHAR_T>
829 inline auto CodeCvt<CHAR_T>::Characters2Bytes (span<const CHAR_T> from, span<byte> to) const -> span<byte>
830 {
831 AssertNotNull (fRep_);
832 Require (to.size () >= ComputeTargetByteBufferSize (from) or to.size () >= Characters2Bytes (from)); // ComputeTargetByteBufferSize cheaper to compute
833 return fRep_->Characters2Bytes (from, to);
834 }
835 template <IUNICODECanAlwaysConvertTo CHAR_T>
836 inline size_t CodeCvt<CHAR_T>::ComputeTargetCharacterBufferSize (span<const byte> src) const
837 {
838 return fRep_->ComputeTargetCharacterBufferSize (src);
839 }
840 template <IUNICODECanAlwaysConvertTo CHAR_T>
841 inline size_t CodeCvt<CHAR_T>::ComputeTargetCharacterBufferSize (size_t srcSize) const
842 {
843 return fRep_->ComputeTargetCharacterBufferSize (srcSize);
844 }
845 template <IUNICODECanAlwaysConvertTo CHAR_T>
846 inline size_t CodeCvt<CHAR_T>::ComputeTargetByteBufferSize (span<const CHAR_T> src) const
847 {
848 return fRep_->ComputeTargetByteBufferSize (src);
849 }
850 template <IUNICODECanAlwaysConvertTo CHAR_T>
851 inline size_t CodeCvt<CHAR_T>::ComputeTargetByteBufferSize (size_t srcSize) const
852 {
853 return fRep_->ComputeTargetByteBufferSize (srcSize);
854 }
855 template <IUNICODECanAlwaysConvertTo CHAR_T>
856 template <constructible_from<const CHAR_T*, const CHAR_T*> STRINGISH>
857 STRINGISH CodeCvt<CHAR_T>::Bytes2String (span<const byte> from) const
858 {
859 // + 2: room for a replacement (even a surrogate pair) after a character the input ends inside of
860 Memory::StackBuffer<CHAR_T> buf{this->ComputeTargetCharacterBufferSize (from) + 2};
861 span<CHAR_T> r = this->Bytes2Characters (from, span{buf});
862 return STRINGISH{r.data (), r.data () + r.size ()};
863 }
864 template <IUNICODECanAlwaysConvertTo CHAR_T>
865 template <constructible_from<const byte*, const byte*> BLOBISH>
866 BLOBISH CodeCvt<CHAR_T>::String2Bytes (span<const CHAR_T> from) const
867 {
868 Memory::StackBuffer<byte> buf{Memory::eUninitialized, this->ComputeTargetByteBufferSize (from)};
869 const span<const byte> r = this->Characters2Bytes (from, span{buf});
870 if constexpr (same_as<BLOBISH, string>) {
871 return string{reinterpret_cast<const char*> (r.data ()), reinterpret_cast<const char*> (r.data ()) + r.size ()};
872 }
873 else {
874 return BLOBISH{r.data (), r.data () + r.size ()};
875 }
876 }
877
878}
#define AssertNotNull(p)
Definition Assertions.h:336
#define AssertNotImplemented()
Definition Assertions.h:404
#define RequireNotReached()
Definition Assertions.h:388
#define RequireNotNull(p)
Definition Assertions.h:350
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:441
#define RequireExpression(c)
Definition Assertions.h:270
#define AssertNotReached()
Definition Assertions.h:358
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
static CodeCvt mkFromStdCodeCvt(const Options &options={}, ARGS... args)
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual size_t Bytes2Characters(span< const byte > from) const
convert span byte (external serialized format) parameters to characters (like std::codecvt<>::in () -...
Definition CodeCvt.inl:779
CodeCvt(const Options &options=Options{})
Definition CodeCvt.inl:604
static CodeCvt PlatformDefault(const Options &options=Options{})
The encoding the platform takes text to be in where nothing says otherwise: on Windows,...
Definition CodeCvt.inl:734
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
UTFConvert is designed to provide mappings between various UTF encodings of UNICODE characters.
Definition UTFConvert.h:83
ConversionStatusFlag
used for ConvertQuietly
Definition UTFConvert.h:279
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
constexpr optional< tuple< UnicodeExternalEncodings, size_t > > ReadByteOrderMark(span< const byte > d) noexcept
UnicodeExternalEncodings
list of external UNICODE character encodings, for file IO (eDEFAULT = eUTF8)
Definition UTFConvert.h:31