Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SDKString.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <climits>
7
9
10#include "SDKString.h"
11
12using namespace Stroika::Foundation;
14
15#if !qStroika_Foundation_Characters_SDKUseswchar_t
16namespace {
17 constexpr CodeCvt<wchar_t>::Options kAllowMissing_{.fInvalidCharacterReplacement = UTFConvert::Options::kDefaultMissingReplacementCharacter};
18}
19#endif
20
21#if not qStroika_Foundation_Characters_SDKUseswchar_t && not qStroika_Platform_MacOS
22namespace {
23 // CodeCvt cannot say whether a replacement is encodable - encoding throws when one is needed and it is not - so ask the locale
24 bool LocaleCanEncode_ (const locale& l, Character c)
25 {
26 static_assert (sizeof (wchar_t) == sizeof (char32_t)); // so one Character is one wchar_t
27 using CVT = codecvt<wchar_t, char, mbstate_t>;
28 wchar_t w = static_cast<wchar_t> (c.As<char32_t> ());
29 mbstate_t state{};
30 const wchar_t* fromNext{};
31 char to[MB_LEN_MAX];
32 char* toNext{};
33 return use_facet<CVT> (l).out (state, &w, &w + 1, fromNext, begin (to), end (to), toNext) == CVT::ok;
34 }
35
36 // The converters for one locale
37 struct LocaleCodeCvts_ {
38 locale fLocale;
39 CodeCvt<wchar_t> fStrict; // throws on a missing character
40 CodeCvt<wchar_t> fAllowMissingToWide; // U+FFFD - decoding can always produce it
41 CodeCvt<wchar_t> fAllowMissingToSDK; // U+FFFD if the locale can encode it (the "C" locale cannot), else '?', which every locale can
42 };
43 LocaleCodeCvts_ MakeLocaleCodeCvts_ (const locale& l)
44 {
45 auto toSDK = kAllowMissing_;
46 if (not LocaleCanEncode_ (l, *toSDK.fInvalidCharacterReplacement)) {
47 toSDK.fInvalidCharacterReplacement = '?';
48 }
49 return LocaleCodeCvts_{l, CodeCvt<wchar_t>{l}, CodeCvt<wchar_t>{l, kAllowMissing_}, CodeCvt<wchar_t>{l, toSDK}};
50 }
51
52 // Set when this thread's cache (below) is destroyed. Trivially destructible, so still readable afterwards - while the
53 // thread's other thread_locals are destroyed, and on the main thread while static destructors run.
54 thread_local bool tLocaleCodeCvtsCacheGone_{false};
55
56 // Constructing a CodeCvt from a locale is costly (it builds a codecvt_byname), so each thread keeps the ones for the
57 // global locale until that changes. So a locale::global () takes effect at the next conversion, just as with no cache:
58 // CodeCvt depends only on the locale's name, and that is what locale's operator== compares. Once the cache is gone,
59 // each call builds its own.
60 CodeCvt<wchar_t> GetCodeCvt_ (CodeCvt<wchar_t> LocaleCodeCvts_::* which)
61 {
62 locale l{};
63 if (tLocaleCodeCvtsCacheGone_) [[unlikely]] {
64 return MakeLocaleCodeCvts_ (l).*which;
65 }
66 struct Cache_ {
67 optional<LocaleCodeCvts_> fEntry;
68 ~Cache_ ()
69 {
70 tLocaleCodeCvtsCacheGone_ = true;
71 }
72 };
73 static thread_local Cache_ tCache_;
74 if (not tCache_.fEntry or tCache_.fEntry->fLocale != l) [[unlikely]] {
75 tCache_.fEntry.emplace (MakeLocaleCodeCvts_ (l));
76 }
77 // a copy (it shares the rep), so it stays valid even if a nested call - a Throw () traced mid-conversion - finds
78 // the global locale changed, and rebuilds the cache
79 return (*tCache_.fEntry).*which;
80 }
81}
82#endif
83
84/*
85 ********************************************************************************
86 ******************************* Characters::SDK2Wide ***************************
87 ********************************************************************************
88 */
89#if !qStroika_Foundation_Characters_SDKUseswchar_t
90wstring Characters::SDK2Wide (span<const SDKChar> s)
91{
92#if qStroika_Platform_MacOS
93 static const CodeCvt<wchar_t> kCvt_{UnicodeExternalEncodings::eUTF8};
94 return kCvt_.Bytes2String<wstring> (as_bytes (s));
95#else
96 return GetCodeCvt_ (&LocaleCodeCvts_::fStrict).Bytes2String<wstring> (as_bytes (s));
97#endif
98}
99wstring Characters::SDK2Wide (span<const SDKChar> s, AllowMissingCharacterErrorsFlag)
100{
101#if qStroika_Platform_MacOS
102 static const CodeCvt<wchar_t> kCvt_{UnicodeExternalEncodings::eUTF8, kAllowMissing_};
103 return kCvt_.Bytes2String<wstring> (as_bytes (s));
104#else
105 return GetCodeCvt_ (&LocaleCodeCvts_::fAllowMissingToWide).Bytes2String<wstring> (as_bytes (s));
106#endif
107}
108#endif
109
110/*
111 ********************************************************************************
112 ******************************* Characters::Wide2SDK ***************************
113 ********************************************************************************
114 */
115#if !qStroika_Foundation_Characters_SDKUseswchar_t
116SDKString Characters::Wide2SDK (span<const wchar_t> s)
117{
118#if qStroika_Platform_MacOS
119 static const CodeCvt<wchar_t> kCvt_{UnicodeExternalEncodings::eUTF8};
120 return kCvt_.String2Bytes<SDKString> (s);
121#else
122 return GetCodeCvt_ (&LocaleCodeCvts_::fStrict).String2Bytes<SDKString> (s);
123#endif
124}
125SDKString Characters::Wide2SDK (span<const wchar_t> s, AllowMissingCharacterErrorsFlag)
126{
127#if qStroika_Platform_MacOS
128 static const CodeCvt<wchar_t> kCvt_{UnicodeExternalEncodings::eUTF8, kAllowMissing_};
129 return kCvt_.String2Bytes<SDKString> (s);
130#else
131 return GetCodeCvt_ (&LocaleCodeCvts_::fAllowMissingToSDK).String2Bytes<SDKString> (s);
132#endif
133}
134#endif
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
basic_string< SDKChar > SDKString
Definition SDKString.h:38