Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
String.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <regex>
5
8#include "Stroika/Foundation/Execution/Throw.h"
9#include "Stroika/Foundation/Memory/Common.h"
10
12
13 [[deprecated ("Since v3.0d1 - use String{s}.AsNarrowSDKString ()")]] inline std::string WideStringToNarrowSDKString (std::wstring s)
14 {
15 return String{s}.AsNarrowSDKString ();
16 }
17
18 /*
19 ********************************************************************************
20 *************************** Characters::Private_ *******************************
21 ********************************************************************************
22 */
23 namespace Private_ {
24 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
25 static size_t StrLen_ (const CHAR_T* s)
26 {
27 if constexpr (same_as<CHAR_T, Latin1>) {
28 return StrLen_ (reinterpret_cast<const char*> (s));
29 }
30 else {
31 return CString::Length (s);
32 }
33 }
34 template <IUNICODECanUnambiguouslyConvertFrom SRC_T>
35 inline void CopyAsASCIICharacters_ (span<const SRC_T> src, span<ASCII> trg)
36 {
37 Require (trg.size () >= src.size ());
38 ASCII* outI = trg.data ();
39 for (auto ii = src.begin (); ii != src.end (); ++ii) {
40 if constexpr (same_as<SRC_T, Character>) {
41 *outI++ = ii->GetAsciiCode ();
42 }
43 else {
44 *outI++ = static_cast<ASCII> (*ii);
45 }
46 }
47 }
48 template <IUNICODECanUnambiguouslyConvertFrom SRC_T>
49 inline void CopyAsLatin1Characters_ (span<const SRC_T> src, span<Latin1> trg)
50 {
51 Require (trg.size () >= src.size ());
52 Latin1* outI = trg.data ();
53 for (auto ii = src.begin (); ii != src.end (); ++ii) {
54 if constexpr (same_as<SRC_T, Character>) {
55 *outI++ = Latin1{static_cast<unsigned char> (ii->GetCharacterCode ())};
56 }
57 else {
58 *outI++ = Latin1{static_cast<unsigned char> (*ii)};
59 }
60 }
61 }
62 template <ICanBeTreatedAsSpanOfCharacter_ USTRING, size_t STACK_BUFFER_SZ>
63 inline span<const Character> AsSpanOfCharacters_ (USTRING&& s, Memory::StackBuffer<Character, STACK_BUFFER_SZ>* mostlyIgnoredBuf)
64 {
65 /*
66 * Genericly convert the argument to a span<const Character> object; for a string, complex and requires
67 * a function call (GetData) and ESSENTIALLY optional mostlyIgnoredBuf argument. For most other types
68 * mostlyIgnoredBuf is ignored.
69 *
70 * This must be highly optimized as its used in critical locations, to quickly access argument data and
71 * convert it into a usable form.
72 */
73 if constexpr (derived_from<remove_cvref_t<USTRING>, String>) {
74 return s.GetData (mostlyIgnoredBuf);
75 }
76 else if constexpr (same_as<remove_cvref_t<USTRING>, const char32_t*> or
77 (sizeof (wchar_t) == sizeof (Character) and same_as<remove_cvref_t<USTRING>, const wchar_t*>)) {
78 return span{reinterpret_cast<const Character*> (s), CString::Length (s)};
79 }
80 else if constexpr (same_as<remove_cvref_t<USTRING>, u32string> or
81 (sizeof (wchar_t) == sizeof (Character) and same_as<remove_cvref_t<USTRING>, wstring>)) {
82 return span{reinterpret_cast<const Character*> (s.c_str ()), s.length ()};
83 }
84 else if constexpr (same_as<remove_cvref_t<USTRING>, u32string_view> or
85 (sizeof (wchar_t) == sizeof (Character) and same_as<remove_cvref_t<USTRING>, wstring_view>)) {
86 return span{reinterpret_cast<const Character*> (s.data ()), s.length ()};
87 }
88 else if constexpr (same_as<remove_cvref_t<USTRING>, const char8_t*> or same_as<remove_cvref_t<USTRING>, const char16_t*> or
89 same_as<remove_cvref_t<USTRING>, const wchar_t*>) {
90 span spn{s, CString::Length (s)};
91 mostlyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<Character> (spn));
92 return UTFConvert::kThe.ConvertSpan (spn, span{*mostlyIgnoredBuf});
93 }
94 else if constexpr (same_as<remove_cvref_t<USTRING>, u8string> or same_as<remove_cvref_t<USTRING>, u16string> or
95 same_as<remove_cvref_t<USTRING>, wstring>) {
96 span spn{s.data (), s.size ()};
97 mostlyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<Character> (spn));
98 return UTFConvert::kThe.ConvertSpan (spn, span{*mostlyIgnoredBuf});
99 }
100 else if constexpr (same_as<remove_cvref_t<USTRING>, u8string_view> or same_as<remove_cvref_t<USTRING>, u16string_view> or
101 same_as<remove_cvref_t<USTRING>, wstring_view>) {
102 span spn{s.data (), s.size ()};
103 mostlyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<Character> (spn));
104 return UTFConvert::kThe.ConvertSpan (spn, span{*mostlyIgnoredBuf});
105 }
106 else {
107 // else must copy data to mostlyIgnoredBuf and use that, so just need a span
108 span spn{s}; // tricky part
109 mostlyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<Character> (spn));
110 return UTFConvert::kThe.ConvertSpan (spn);
111 }
112 }
113 }
114
115 /*
116 ********************************************************************************
117 ************************************* String ***********************************
118 ********************************************************************************
119 */
120
121 // Since we don't mix spans of single/2-3-4 byte chars in a single rep (would make char indexing too expensive)
122 // just specialize 3 cases - ASCII (char), utf-16, and utf-32 (others - like char8_t, wchar_t mappeed appropriately)
123 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
124 auto String::mk_ (span<const CHAR_T> s) -> shared_ptr<_IRep>
125 {
126 if (s.empty ()) {
127 return mkEmpty_ ();
128 }
129 if constexpr (same_as<CHAR_T, ASCII>) {
131 return mk_nocheck_ (s);
132 }
133 else if constexpr (same_as<CHAR_T, Latin1>) {
135 return mk_nocheck_ (s);
136 }
137 // NOTE: StackBuffer <,SIZE> parameters heuristic - to avoid _chkstk calls and performance impact - for most common string cases.
138 switch (Character::IsASCIIOrLatin1 (s)) {
139 case Character::ASCIIOrLatin1Result::eASCII: {
140 if constexpr (sizeof (CHAR_T) == 1) {
141 return mk_nocheck_ (span<const ASCII>{reinterpret_cast<const ASCII*> (s.data ()), s.size ()});
142 }
143 else {
144 // Copy to smaller buffer (e.g. utf16_t to char)
145 Memory::StackBuffer<ASCII, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 4 - 20> buf{Memory::eUninitialized,
146 s.size ()};
147 Private_::CopyAsASCIICharacters_ (s, span{buf});
148 return mk_nocheck_ (span<const ASCII>{buf});
149 }
150 }
151 case Character::ASCIIOrLatin1Result::eLatin1: {
152 if constexpr (sizeof (CHAR_T) == 1) {
153 if constexpr (same_as<remove_cv_t<CHAR_T>, ASCII>) {
154 RequireNotReached (); // if marked as ASCII, better not contain non-ascii characters!
155 }
156 else if constexpr (same_as<remove_cv_t<CHAR_T>, Latin1>) {
157 return mk_nocheck_ (s);
158 }
159 else if constexpr (same_as<remove_cv_t<CHAR_T>, char8_t>) {
160 // Latin1 CAN fit in a single byte, but when encoded as UTF-8, it generally does NOT. So we must map to its one byte
161 // representation, by doing UTF decoding.
162 //
163 // EXAMPLE:
164 // https://www.utf8-chartable.de/
165 // U+00C2 � c3 82 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
166 //
167 // However, a quirk (reasonable) of UTFConvert::kThe.ConvertSpan is that it CANNOT convert to Latin1 becuase
168 // the concept IUNICODECanUnambiguouslyConvertTo<Latin1> evaluated to false.
169 //
170 // COULD lift that restriction, (@todo consider), and just check/assert/require input is indeeded all Latin1. Or could
171 // do what I do here, and do a two step copy.
172 //
173 // OR could fix CopyAsLatin1Characters_ () to handle input of char8_t differently/correctly;
174 //
175 // Suspect this case is rare enuf to be good enuf for now ]--LGP 2023-12-02
176 Memory::StackBuffer<char16_t, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 16 - 20> c16buf{
177 Memory::eUninitialized, s.size ()};
178 span<const char16_t> s16 = UTFConvert::kThe.ConvertSpan (s, span{c16buf});
179 Memory::StackBuffer<Latin1, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 16 - 20> buf{
180 Memory::eUninitialized, s16.size ()};
181 Private_::CopyAsLatin1Characters_ (s16, span{buf});
182 return mk_nocheck_ (span<const Latin1>{buf});
183 }
184 else {
185 AssertNotReached (); // no other 1-byte case
186 }
187 }
188 else {
189 // Copy to smaller buffer (e.g. utf32_t to Latin1)
190 Memory::StackBuffer<Latin1, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 8 - 20> buf{Memory::eUninitialized,
191 s.size ()};
192 Private_::CopyAsLatin1Characters_ (s, span{buf});
193 return mk_nocheck_ (span<const Latin1>{buf});
194 }
195 }
196 }
197 // at this point, we know the text must be encoded as utf16 or utf32 (but source code still be single byte, like utf8)
199 if constexpr (sizeof (CHAR_T) == 2) {
200 // no transcode needed UTF16->UTF16
201 return mk_nocheck_ (span<const char16_t>{reinterpret_cast<const char16_t*> (s.data ()), s.size ()});
202 }
203 else {
204 // complex case - could be utf8 src, utf16, or utf32, so must transcode to char16_t
205 Memory::StackBuffer<char16_t, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 16> wideUnicodeBuf{
206 Memory::eUninitialized, UTFConvert::ComputeTargetBufferSize<char16_t> (s)};
207 return mk_nocheck_ (Memory::ConstSpan (UTFConvert::kThe.ConvertSpan (s, span{wideUnicodeBuf})));
208 }
209 }
210 // So at this point - definitely converting to UTF-32
211 if constexpr (sizeof (CHAR_T) == 4) {
212 // Easy, just cast
213 return mk_nocheck_ (span<const char32_t>{reinterpret_cast<const char32_t*> (s.data ()), s.size ()});
214 }
215 else {
216 // converting utf8 or utf16 with surrogates to utf32
217 Memory::StackBuffer<char32_t, Memory::Support::StackBuffer::kSizeIfLargerStackGuardCalled / 32> wideUnicodeBuf{
218 Memory::eUninitialized, UTFConvert::ComputeTargetBufferSize<char32_t> (s)};
219 return mk_nocheck_ (Memory::ConstSpan (UTFConvert::kThe.ConvertSpan (s, span{wideUnicodeBuf})));
220 }
221 }
222 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
223 auto String::mk_ (span<CHAR_T> s) -> shared_ptr<_IRep>
224 {
225 // weird and unfortunate overload needed for non-const spans, not automatically promoted to const
226 return mk_ (Memory::ConstSpan (s));
227 }
228 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
229 auto String::mk_ (Iterable<CHAR_T> it) -> shared_ptr<_IRep>
230 {
231 // redo with small stackbuffer (character and dont do iterable<Characer> do Iterable<CHAR_T> where t is Characer_Compiabple)
232 // then unoicode covert and use other mk_ existing overloads
233 Memory::StackBuffer<char32_t> r;
234 it.Apply ([&r] (CHAR_T c) {
235 if constexpr (same_as<CHAR_T, Character>) {
236 r.push_back (static_cast<char32_t> (c)); // explicit operator char32_t to avoid ambiguities elsewhere
237 }
238 else {
239 r.push_back (c);
241 });
242 return mk_ (span{r.data (), r.size ()});
243 }
244 template <>
245 auto String::mk_ (basic_string<char>&& s) -> shared_ptr<_IRep>;
246 template <>
247 auto String::mk_ (basic_string<char16_t>&& s) -> shared_ptr<_IRep>;
248 template <>
249 auto String::mk_ (basic_string<char32_t>&& s) -> shared_ptr<_IRep>;
250 template <>
251 auto String::mk_ (basic_string<wchar_t>&& s) -> shared_ptr<_IRep>;
252 template <IStdBasicStringCompatibleCharacter CHAR_T>
253 inline auto String::mk_ (basic_string<CHAR_T>&& s) -> shared_ptr<_IRep>
254 {
255 // by default, except for maybe a few special cases, just copy the data - don't move
256 return mk_ (span{s.begin (), s.size ()});
257 }
258 inline String::String (const shared_ptr<_IRep>& rep) noexcept
259 : inherited{rep}
260 {
261 _AssertRepValidType ();
262 }
263 inline String::String (shared_ptr<_IRep>&& rep) noexcept
264 : inherited{(RequireExpression (rep != nullptr), move (rep))}
265 {
266 _AssertRepValidType ();
267 }
269 : inherited{mkEmpty_ ()}
270 {
271 _AssertRepValidType ();
272 }
273 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
274 inline String::String (const CHAR_T* cString)
275 : inherited{mk_ (span{cString, CString::Length (cString)})}
276 {
278 _AssertRepValidType ();
279 }
280 template <Memory::ISpan SPAN_OF_CHAR_T>
281 inline String::String (SPAN_OF_CHAR_T s)
282 requires (IUNICODECanUnambiguouslyConvertFrom<typename SPAN_OF_CHAR_T::value_type>)
283 : inherited{mk_ (span<const typename SPAN_OF_CHAR_T::value_type>{s})}
284 {
285 _AssertRepValidType ();
286 }
287 template <IStdBasicStringCompatibleCharacter CHAR_T>
288 inline String::String (const basic_string<CHAR_T>& s)
289 : inherited{mk_ (span<const CHAR_T>{s.data (), s.size ()})}
290 {
291 }
292 template <IStdBasicStringCompatibleCharacter CHAR_T>
293 inline String::String (const basic_string_view<CHAR_T>& s)
294 : inherited{CTORFromBasicStringView_ (s)}
295 {
296 }
297 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
298 inline String::String (const Iterable<CHAR_T>& src)
299 requires (not Memory::ISpan<CHAR_T>)
300 : inherited{mk_ (src)}
301 {
302 }
303 inline String::String (Character c)
304 : String{span{&c, 1}}
305 {
306 }
307 template <IStdBasicStringCompatibleCharacter CHAR_T>
308 inline String::String (basic_string<CHAR_T>&& s)
309 : inherited{mk_ (forward<basic_string<CHAR_T>> (s))}
310 {
311 }
312 template <IStdPathLike2UNICODEString PATHLIKE_TOSTRINGABLE>
313 inline String::String (PATHLIKE_TOSTRINGABLE&& s)
314
315 : String{mkSTR_ (forward<PATHLIKE_TOSTRINGABLE> (s))}
316 {
317 }
318 template <IStdPathLike2UNICODEString PATHLIKE_TOSTRINGABLE>
319 String String::mkSTR_ (PATHLIKE_TOSTRINGABLE&& s)
320 {
321 if constexpr (requires (PATHLIKE_TOSTRINGABLE t) {
322 { t.wstring () } -> same_as<wstring>;
323 }) {
324 return String{forward<PATHLIKE_TOSTRINGABLE> (s).wstring ()};
325 }
326 if constexpr (requires (PATHLIKE_TOSTRINGABLE t) {
327 { t.u8string () } -> same_as<u8string>;
328 }) {
329 return String{forward<PATHLIKE_TOSTRINGABLE> (s).u8string ()};
330 }
331 if constexpr (requires (PATHLIKE_TOSTRINGABLE t) {
332 { t.u16string () } -> same_as<u16string>;
333 }) {
334 return String{forward<PATHLIKE_TOSTRINGABLE> (s).u16string ()};
335 }
336 if constexpr (requires (PATHLIKE_TOSTRINGABLE t) {
337 { t.u32string () } -> same_as<u32string>;
338 }) {
339 return String{forward<PATHLIKE_TOSTRINGABLE> (s).u32string ()};
340 }
341 }
342 inline String String::FromNarrowString (const char* from, const locale& l)
343 {
344 RequireNotNull (from);
345 return FromNarrowString (span{from, ::strlen (from)}, l);
346 }
347 inline String String::FromNarrowString (const string& from, const locale& l)
348 {
349 return FromNarrowString (span{from.c_str (), from.length ()}, l);
350 }
351 template <IStdBasicStringCompatibleCharacter CHAR_T>
352 inline String String::FromLatin1 (const basic_string<CHAR_T>& s)
353 {
354 return FromLatin1 (span{s.data (), s.size ()});
355 }
356 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
357 inline String String::FromLatin1 (const CHAR_T* cString)
358 {
359 RequireNotNull (cString);
360 return FromLatin1 (span{cString, Private_::StrLen_ (cString)});
361 }
362 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
363 inline String String::FromLatin1 (span<const CHAR_T> s)
364 {
365 /*
366 * From http://unicodebook.readthedocs.io/encodings.html
367 * "For example, ISO-8859-1 are the first 256 UNICODE code points (U+0000-U+00FF)."
368 */
369 if constexpr (sizeof (CHAR_T) == 1) {
370 return mk_ (span<const Latin1>{reinterpret_cast<const Latin1*> (s.data ()), s.size ()});
371 }
372 else {
373 const CHAR_T* b = reinterpret_cast<const CHAR_T*> (s.data ());
374 const CHAR_T* e = b + s.size ();
375 Memory::StackBuffer<Latin1> buf{Memory::eUninitialized, static_cast<size_t> (e - b)};
376 Latin1* pOut = buf.begin ();
377 for (const CHAR_T* i = b; i != e; ++i, ++pOut) {
378 if (*i >= 256) {
379 static const auto kException_ = out_of_range{"Error converting non-iso-latin-1 text to String"};
380 Execution::Throw (kException_);
381 }
382 *pOut = *i;
383 }
384 return mk_ (span<const Latin1>{buf.begin (), pOut});
385 }
386 }
387 template <size_t SIZE, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
388 inline String String::FromStringConstant (const CHAR_T (&cString)[SIZE])
389 {
390 return FromStringConstant (span<const CHAR_T>{cString, SIZE - 1}); // -1 because a literal array SIZE includes the NUL-character at the end
391 }
392 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
393 inline String String::FromStringConstant (const basic_string_view<CHAR_T>& str)
394 {
395 return FromStringConstant (span<const CHAR_T>{str.data (), str.size ()});
396 }
397 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
398 inline String String::FromStringConstant (span<const CHAR_T> str)
399 {
400 // todo add test cases..
401 // todo add low pri jira ticket - at least for UTF-8 case - could do native utf8 rep (multibyte indexing)
402
403 if constexpr (same_as<CHAR_T, char8_t>) {
404 if (Character::IsASCII (str)) {
405 return FromStringConstant (Memory::SpanBytesCast<span<const ASCII>> (str));
406 }
407 }
408 // other cases - just copy
409 return String{str}; // fallback implementation - not any quicker, but allows saying intent of string-constant and later impl faster version
410 }
411 inline String String::FromStringConstant (span<const wchar_t> s)
412 {
413 if constexpr (sizeof (wchar_t) == 2) {
414 return FromStringConstant (Memory::SpanBytesCast<span<const char16_t>> (s));
415 }
416 else {
417 Assert (sizeof (wchar_t) == 4);
418 return FromStringConstant (Memory::SpanBytesCast<span<const char32_t>> (s));
419 }
420 }
421 template <typename CHAR_T>
422 inline String String::FromUTF8 (span<CHAR_T> s)
423 requires (same_as<remove_cv_t<CHAR_T>, char8_t> or same_as<remove_cv_t<CHAR_T>, char>)
424 {
425 if (Character::IsASCII (s)) [[likely]] {
426 return mk_ (span<const char>{reinterpret_cast<const char*> (s.data ()), s.size ()});
427 }
428 else if (UTFConvert::AllFitsInTwoByteEncoding (s)) [[likely]] {
429 Memory::StackBuffer<char16_t> buf{Memory::eUninitialized, UTFConvert::kThe.ComputeTargetBufferSize<char16_t> (s)};
430 return String{UTFConvert::kThe.ConvertSpan (s, span{buf})};
431 }
432 else {
433 Memory::StackBuffer<char32_t> buf{Memory::eUninitialized, UTFConvert::kThe.ComputeTargetBufferSize<char32_t> (s)};
434 return String{UTFConvert::kThe.ConvertSpan (s, span{buf})};
435 }
436 }
437 template <typename CHAR_T>
438 inline String String::FromUTF8 (const basic_string<CHAR_T>& from)
439 requires (same_as<remove_cv_t<CHAR_T>, char8_t> or same_as<remove_cv_t<CHAR_T>, char>)
440 {
441 return FromUTF8 (span{from.data (), from.length ()});
442 }
443 template <typename CHAR_T>
444 inline String String::FromUTF8 (const CHAR_T* from)
445 requires (same_as<remove_cv_t<CHAR_T>, char8_t> or same_as<remove_cv_t<CHAR_T>, char>)
446 {
447 return FromUTF8 (span{from, ::strlen (reinterpret_cast<const char*> (from))});
448 }
450 {
451 RequireNotNull (from);
452 return FromSDKString (span{from, CString::Length (from)});
453 }
454 inline String String::FromSDKString (span<const SDKChar> s)
455 {
456 if constexpr (same_as<SDKChar, wchar_t>) {
457 return String{s};
458 }
459 else {
460 return String{SDK2Wide (s)};
461 }
462 }
463 inline String String::FromSDKString (const SDKString& from)
464 {
465 if constexpr (same_as<SDKString, wstring>) {
466 return String{from};
467 }
468 else {
469 return FromSDKString (span{from.c_str (), from.length ()});
470 }
471 }
472 inline String String::FromNarrowSDKString (const char* from)
473 {
474 RequireNotNull (from);
475 return FromNarrowSDKString (span{from, ::strlen (from)});
476 }
477 inline String String::FromNarrowSDKString (span<const char> s)
478 {
479 return String{NarrowSDK2Wide (s)};
480 }
481 inline String String::FromNarrowSDKString (const string& from)
482 {
483 return FromNarrowSDKString (span{from.c_str (), from.length ()});
484 }
485 template <typename T>
486 inline String String::Concatenate (T&& rhs) const
487 requires (is_convertible_v<T, String>)
488 {
489 // @todo more work needed optimizing this - esp for other arguments like const char*, and string_view etc...
490 // and even can optimize for case where storing char16_t or LATIN1 types...
491 PeekSpanData lhsPSD = GetPeekSpanData<ASCII> ();
492 // OPTIMIZED PATHS: Common case(s) and should be fast
493 if (lhsPSD.fInCP == PeekSpanData::StorageCodePointType::eAscii) {
494 if constexpr (derived_from<remove_cvref_t<T>, String>) {
495 PeekSpanData rhsPSD = rhs.template GetPeekSpanData<ASCII> ();
496 if (rhsPSD.fInCP == PeekSpanData::StorageCodePointType::eAscii) {
497 Memory::StackBuffer<ASCII, 512> buf{Memory::eUninitialized, lhsPSD.fAscii.size () + rhsPSD.fAscii.size ()};
498 copy (lhsPSD.fAscii.begin (), lhsPSD.fAscii.end (), buf.data ());
499 copy (rhsPSD.fAscii.begin (), rhsPSD.fAscii.end (), buf.data () + lhsPSD.fAscii.size ());
500 return this->mk_nocheck_ (span<const ASCII>{buf}); // no check needed cuz combining all ASCII sources
501 }
502 }
503 // @todo lots of other easy cases to optimize, but this came up first...
504 }
505 // simple default fallthru implementation
506 return Concatenate_ (forward<T> (rhs));
507 }
508 inline void String::_AssertRepValidType () const
509 {
510 EnsureMember (&_SafeReadRepAccessor{this}._ConstGetRep (), String::_IRep);
511 }
512 template <IUNICODECanAlwaysConvertTo CHAR_T>
513 inline span<CHAR_T> String::CopyTo (span<CHAR_T> s) const
514 requires (not is_const_v<CHAR_T>)
515 {
516 PeekSpanData psd = GetPeekSpanData<CHAR_T> ();
517 if (auto p = PeekData<CHAR_T> (psd)) {
518 return Memory::CopySpanData (*p, s);
519 }
520 else {
521 // OK, we need to UTF convert from the actual size we have to what the caller asked for
522 switch (psd.fInCP) {
523 case PeekSpanData::StorageCodePointType::eAscii: // maybe could optimize this case too
524 case PeekSpanData::StorageCodePointType::eSingleByteLatin1:
525 return UTFConvert::kThe.ConvertSpan (psd.fSingleByteLatin1, s);
526 case PeekSpanData::StorageCodePointType::eChar16:
527 return UTFConvert::kThe.ConvertSpan (psd.fChar16, s);
528 case PeekSpanData::StorageCodePointType::eChar32:
529 return UTFConvert::kThe.ConvertSpan (psd.fChar32, s);
530 default:
532 return span<CHAR_T>{};
533 }
534 }
535 }
536 inline size_t String::size () const noexcept
537 {
538 _SafeReadRepAccessor accessor{this};
539 return accessor._ConstGetRep ().size ();
540 }
541 template <unsigned_integral T>
542 inline size_t String::SubString_adjust_ (T fromOrTo, [[maybe_unused]] size_t myLength) const
543 {
544 Require (fromOrTo <= numeric_limits<size_t>::max ());
545 return static_cast<size_t> (fromOrTo);
546 }
547 template <signed_integral T>
548 inline size_t String::SubString_adjust_ (T fromOrTo, size_t myLength) const
549 {
550 if (fromOrTo >= 0) [[likely]] {
551 Require (fromOrTo <= numeric_limits<ptrdiff_t>::max ());
552 return static_cast<size_t> (fromOrTo);
553 }
554 else {
555 Require (fromOrTo >= numeric_limits<ptrdiff_t>::min ());
556 return static_cast<size_t> (myLength + static_cast<ptrdiff_t> (fromOrTo));
557 }
558 }
559 template <typename SZ>
560 inline String String::SubString (SZ from) const
561 {
562 _SafeReadRepAccessor accessor{this};
563 size_t myLength{accessor._ConstGetRep ().size ()};
564 size_t f = SubString_adjust_ (from, myLength);
565 size_t t = myLength;
566 Require (f <= myLength);
567 return SubString_ (accessor, f, t);
568 }
569 template <typename SZ1, typename SZ2>
570 inline String String::SubString (SZ1 from, SZ2 to) const
571 {
572 _SafeReadRepAccessor accessor{this};
573 size_t myLength{accessor._ConstGetRep ().size ()};
574 size_t f = SubString_adjust_ (from, myLength);
575 size_t t = SubString_adjust_ (to, myLength);
576 Require (f <= t);
577 Require (t <= myLength);
578 return SubString_ (accessor, f, t);
579 }
580 template <typename SZ>
581 inline String String::SafeSubString (SZ from) const
582 {
583 _SafeReadRepAccessor accessor{this};
584 size_t myLength{accessor._ConstGetRep ().size ()};
585 size_t f = SubString_adjust_ (from, myLength);
586 f = min (f, myLength);
587 Assert (f <= myLength);
588 size_t useLength{myLength - f};
589 return SubString_ (accessor, f, f + useLength);
590 }
591 template <typename SZ1, typename SZ2>
592 inline String String::SafeSubString (SZ1 from, SZ2 to) const
593 {
594 _SafeReadRepAccessor accessor{this};
595 size_t myLength{accessor._ConstGetRep ().size ()};
596 size_t f = SubString_adjust_ (from, myLength);
597 size_t t = SubString_adjust_ (to, myLength);
598 f = min (f, myLength);
599 t = min (t, myLength);
600 t = max (t, f);
601 Assert (f <= t);
602 Assert (t <= myLength);
603 size_t useLength = (t - f);
604 return SubString_ (accessor, f, f + useLength);
605 }
606 inline String String::Skip (size_t n) const
607 {
608 return SafeSubString (n);
609 }
610 inline String String::RemoveAt (size_t charAt) const
611 {
612 return RemoveAt (charAt, charAt + 1);
613 }
614 inline String String::RemoveAt (pair<size_t, size_t> fromTo) const
615 {
616 return RemoveAt (fromTo.first, fromTo.second);
617 }
618 inline bool String::empty () const noexcept
619 {
620 _SafeReadRepAccessor accessor{this};
621 return accessor._ConstGetRep ().size () == 0;
622 }
623 namespace Private_ {
624 // match index starts with 1 (and requires match.size () >=2)
625 inline void ExtractMatches_ ([[maybe_unused]] const wsmatch& base_match, [[maybe_unused]] size_t currentUnpackIndex)
626 {
627 }
628 template <Common::IAnyOf<optional<String>*, String*, nullptr_t> SUBMATCH, typename... OPTIONAL_STRINGS>
629 void ExtractMatches_ (const wsmatch& base_match, size_t currentUnpackIndex, SUBMATCH subMatchI, OPTIONAL_STRINGS&&... remainingSubmatches)
630 {
631 if (currentUnpackIndex < base_match.size ()) [[likely]] {
632 if constexpr (not same_as<SUBMATCH, nullptr_t>) {
633 if (subMatchI != nullptr) {
634 *subMatchI = base_match[currentUnpackIndex].str ();
635 }
636 }
637 ExtractMatches_ (base_match, currentUnpackIndex + 1, forward<OPTIONAL_STRINGS> (remainingSubmatches)...);
638 }
639 }
640 const wregex& RegularExpression_GetCompiled (const RegularExpression& regExp);
641 }
642 template <Common::IAnyOf<optional<String>*, String*, nullptr_t>... OPTIONAL_STRINGS>
643 bool String::Matches (const RegularExpression& regEx, OPTIONAL_STRINGS&&... subMatches) const
644 {
645 wstring tmp{As<wstring> ()};
646 wsmatch baseMatch;
647 if (regex_match (tmp, baseMatch, Private_::RegularExpression_GetCompiled (regEx))) {
648 Private_::ExtractMatches_ (baseMatch, 1, forward<OPTIONAL_STRINGS> (subMatches)...);
649 return true;
650 }
651 return false;
652 }
653 template <size_t I>
654 optional<Common::RepeatedTuple_t<I, String>> String::Matches (const RegularExpression& regEx) const
655 {
656 wstring tmp{As<wstring> ()};
657 wsmatch baseMatch;
658 if (regex_match (tmp, baseMatch, Private_::RegularExpression_GetCompiled (regEx))) {
659 //tmphack impl - cuz my template skills suck --LGP 2025-01-24
660 if constexpr (I == 0) {
661 return make_tuple ();
662 }
663 else if constexpr (I == 1) {
664 return make_tuple (String{baseMatch[1].str ()});
665 }
666 else if constexpr (I == 2) {
667 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()});
668 }
669 else if constexpr (I == 3) {
670 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()});
671 }
672 else if constexpr (I == 4) {
673 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()},
674 String{baseMatch[4].str ()});
675 }
676 else if constexpr (I == 5) {
677 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()},
678 String{baseMatch[4].str ()}, String{baseMatch[5].str ()});
679 }
680 else {
682 return nullopt;
683 }
684 }
685 return nullopt;
686 }
687 inline optional<size_t> String::Find (Character c, CompareOptions co) const
688 {
689 return Find (c, 0, co);
690 }
691 inline optional<size_t> String::Find (const String& subString, CompareOptions co) const
692 {
693 return Find (subString, 0, co);
694 }
695 inline Traversal::Iterator<Character> String::Find (const function<bool (Character item)>& that) const
696 {
697 return inherited::Find (that);
698 }
699 inline bool String::Contains (Character c, CompareOptions co) const
700 {
701 return static_cast<bool> (Find (c, co));
702 }
703 inline bool String::Contains (const String& subString, CompareOptions co) const
704 {
705 return static_cast<bool> (Find (subString, co));
706 }
707 inline bool String::ContainsAny (Iterable<Character> cs, CompareOptions co) const
708 {
709 auto comparer = Character::EqualsComparer{co};
710 auto checkEachCharacter = [&] (Character c) -> bool { return cs.Any ([&] (const Character c2) { return comparer (c, c2); }); };
711 return Find (checkEachCharacter) != nullptr;
712 }
713 inline String String::Replace (pair<size_t, size_t> fromTo, const String& replacement) const
714 {
715 return Replace (fromTo.first, fromTo.second, replacement);
716 }
717 inline String String::ColValue (size_t i, const String& valueIfMissing) const
718 {
719 return Col (i).value_or (valueIfMissing);
720 }
721 inline String String::InsertAt (Character c, size_t at) const
722 {
723 return InsertAt (span<const Character>{&c, 1}, at);
724 }
725 inline String String::InsertAt (const String& s, size_t at) const
726 {
728 return InsertAt (s.GetData (&ignored1), at);
729 }
730 inline String String::InsertAt (span<Character> s, size_t at) const
731 {
732 return InsertAt (Memory::ConstSpan (s), at);
733 }
734 inline const Character String::GetCharAt (size_t i) const noexcept
735 {
736 _SafeReadRepAccessor accessor{this};
737 Require (i >= 0);
738 Require (i < accessor._ConstGetRep ().size ());
739 return accessor._ConstGetRep ().GetAt (i);
740 }
741 inline const Character String::operator[] (size_t i) const noexcept
742 {
743 Require (i >= 0);
744 Require (i < size ());
745 return GetCharAt (i);
746 }
747 inline String String::LimitLength (size_t maxLen, StringShorteningPreference keepPref) const
748 {
749#if qCompiler_vswprintf_on_elispisStr_Buggy
750 static const String kELIPSIS_{"..."_k};
751#else
752 static const String kELIPSIS_{u"\u2026"sv}; // OR "..."
753#endif
754 return LimitLength (maxLen, keepPref, kELIPSIS_);
755 }
756 template <typename T>
757 inline T String::As () const
758 requires (IBasicUNICODEStdString<T> or same_as<T, String> or constructible_from<T, wstring>)
759 {
760 if constexpr (same_as<T, u8string>) {
761 return AsUTF8<T> ();
762 }
763 else if constexpr (same_as<T, u16string>) {
764 return AsUTF16<T> ();
765 }
766 else if constexpr (same_as<T, u32string>) {
767 return AsUTF32<T> ();
768 }
769 else if constexpr (same_as<T, wstring>) {
770 if constexpr (sizeof (wchar_t) == 2) {
771 return AsUTF16<T> ();
772 }
773 else {
774 return AsUTF32<T> ();
775 }
776 }
777 else if constexpr (same_as<T, String>) {
778 return *this;
779 }
780 else if constexpr (constructible_from<T, wstring>) {
781 return T{As<wstring> ()};
782 }
783 }
784 template <typename T>
785 inline T String::AsUTF8 () const
786 requires (same_as<T, string> or same_as<T, u8string>)
787 {
788 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
789 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1);
790 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
791 }
792 template <typename T>
793 inline T String::AsUTF16 () const
794 requires (same_as<T, u16string> or (sizeof (wchar_t) == sizeof (char16_t) and same_as<T, wstring>))
795 {
796 Memory::StackBuffer<char16_t> maybeIgnoreBuf1;
797 span<const char16_t> thisData = GetData (&maybeIgnoreBuf1);
798 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
799 }
800 template <typename T>
801 inline T String::AsUTF32 () const
802 requires (same_as<T, u32string> or (sizeof (wchar_t) == sizeof (char32_t) and same_as<T, wstring>))
803 {
804 Memory::StackBuffer<char32_t> maybeIgnoreBuf1;
805 span<const char32_t> thisData = GetData (&maybeIgnoreBuf1);
806 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
807 }
809 {
810#if qTargetPlatformSDKUseswchar_t
811 Memory::StackBuffer<wchar_t> maybeIgnoreBuf1;
812 span<const wchar_t> thisData = GetData (&maybeIgnoreBuf1);
813 return SDKString{thisData.begin (), thisData.end ()};
814#elif qStroika_Foundation_Common_Platform_MacOS
815 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
816 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1);
817 return SDKString{thisData.begin (), thisData.end ()}; // @todo DOCUMENT THAT MACOS USES UTF8 - SRC - LOGIC/RATIONALE
818#else
819 return AsNarrowString (locale{}); // @todo document why - linux one rationale - default - similar
820#endif
821 }
823 {
824#if qTargetPlatformSDKUseswchar_t
825 Memory::StackBuffer<wchar_t> maybeIgnoreBuf1;
826 span<const wchar_t> thisData = GetData (&maybeIgnoreBuf1);
827 return SDKString{thisData.begin (), thisData.end ()};
828#elif qStroika_Foundation_Common_Platform_MacOS
829 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
830 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1); // Note this always works, since we can always map to UTF-8 any Stroika string
831 return SDKString{thisData.begin (), thisData.end ()}; // @todo DOCUMENT THAT MACOS USES UTF8 - SRC - LOGIC/RATIONALE
832#else
833 return AsNarrowString (locale{}, eIgnoreErrors); // @todo document why - linux one rationale - default - similar
834#endif
835 }
836 inline string String::AsNarrowSDKString () const
837 {
838 return SDK2Narrow (AsSDKString ());
839 }
841 {
842 return SDK2Narrow (AsSDKString (eIgnoreErrors), AllowMissingCharacterErrorsFlag::eIgnoreErrors);
843 }
844 template <typename T>
845 inline T String::AsASCII () const
846 requires requires (T* into) {
847 { into->empty () } -> same_as<bool>;
848 { into->push_back (ASCII{0}) };
849 }
850 {
851 // @todo possibly rewrite/inline impl to avoid map to optional<T> which involves copying
852 if (auto p = AsASCIIQuietly<T> ()) {
853 return *p;
854 }
855 else {
856 ThrowInvalidAsciiException_ ();
857 }
858 }
859 template <typename T>
860 inline optional<T> String::AsASCIIQuietly () const
861 requires requires (T* into) {
862 { into->empty () } -> same_as<bool>;
863 { into->push_back (ASCII{0}) };
864 }
865 {
866 // @todo OPTIMIZE - PeekSpanData - may already be ASCII - OPTIMIZE THAT CASE!!!
867 Memory::StackBuffer<wchar_t> ignored1;
868 auto thisSpan = GetData (&ignored1);
869 T s;
870 return Character::AsASCIIQuietly<T> (thisSpan, &s) ? s : optional<T>{};
871 }
872 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
873 inline String::PeekSpanData String::GetPeekSpanData () const
874 {
875 using StorageCodePointType = PeekSpanData::StorageCodePointType;
876 StorageCodePointType preferredSCP{};
877 if constexpr (same_as<remove_cv_t<CHAR_TYPE>, ASCII>) {
878 preferredSCP = StorageCodePointType::eAscii;
879 }
880 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, Latin1>) {
881 preferredSCP = StorageCodePointType::eSingleByteLatin1;
882 }
883 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char8_t>) {
884 preferredSCP = StorageCodePointType::eAscii; // not clear what's best in this case but probably doesn't matter
885 }
886 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char16_t>) {
887 preferredSCP = StorageCodePointType::eChar16;
888 }
889 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char32_t> or same_as<remove_cv_t<CHAR_TYPE>, Character>) {
890 preferredSCP = StorageCodePointType::eChar32;
891 }
892 if constexpr (same_as<remove_cv_t<CHAR_TYPE>, wchar_t>) {
893 if constexpr (sizeof (wchar_t) == 2) {
894 preferredSCP = StorageCodePointType::eChar16;
895 }
896 else if constexpr (sizeof (wchar_t) == 4) {
897 preferredSCP = StorageCodePointType::eChar32;
898 }
899 }
900 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, Character>) {
901 // later will map to char32_t, but for now same as wchar_t
902 if constexpr (sizeof (wchar_t) == 2) {
903 preferredSCP = StorageCodePointType::eChar16;
904 }
905 else if constexpr (sizeof (wchar_t) == 4) {
906 preferredSCP = StorageCodePointType::eChar32;
907 }
908 }
909 return _SafeReadRepAccessor{this}._ConstGetRep ().PeekData (preferredSCP);
910 }
911 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
912 inline optional<span<const CHAR_TYPE>> String::PeekData (const PeekSpanData& pds)
913 {
914 using StorageCodePointType = PeekSpanData::StorageCodePointType;
915 if constexpr (same_as<CHAR_TYPE, ASCII>) {
916 if (pds.fInCP == StorageCodePointType::eAscii) {
917 return pds.fAscii;
918 }
919 }
920 else if constexpr (same_as<CHAR_TYPE, Latin1>) {
921 if (pds.fInCP == StorageCodePointType::eSingleByteLatin1) {
922 return pds.fSingleByteLatin1;
923 }
924 }
925 else if constexpr (same_as<CHAR_TYPE, char8_t>) {
926 if (pds.fInCP == StorageCodePointType::eAscii) { // single-byte-latin1 not legal char8_t format
927 return pds.fAscii;
928 }
929 }
930 else if constexpr (same_as<CHAR_TYPE, char16_t>) {
931 if (pds.fInCP == StorageCodePointType::eChar16) {
932 return pds.fChar16;
933 }
934 }
935 else if constexpr (same_as<CHAR_TYPE, char32_t>) {
936 if (pds.fInCP == StorageCodePointType::eChar32) {
937 return pds.fChar32;
938 }
939 }
940 else if constexpr (same_as<CHAR_TYPE, wchar_t>) {
941 if constexpr (sizeof (wchar_t) == 2) {
942 if (pds.fInCP == StorageCodePointType::eChar16) {
943 return span<const wchar_t>{reinterpret_cast<const wchar_t*> (pds.fChar16.data ()), pds.fChar16.size ()};
944 }
945 }
946 else if constexpr (sizeof (wchar_t) == 4) {
947 if (pds.fInCP == StorageCodePointType::eChar32) {
948 return span<const wchar_t>{reinterpret_cast<const wchar_t*> (pds.fChar32.data ()), pds.fChar32.size ()};
949 }
950 }
951 return span<const wchar_t>{};
952 }
953 else if constexpr (same_as<CHAR_TYPE, Character>) {
954 if (pds.fInCP == StorageCodePointType::eChar32) {
955 return span<const Character>{reinterpret_cast<const Character*> (pds.fChar32.data ()), pds.fChar32.size ()};
956 }
957 return span<const Character>{};
958 }
959 return nullopt; // can easily happen if you request a type that is not stored in the rep
960 }
961 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
962 inline optional<span<const CHAR_TYPE>> String::PeekData () const
963 {
964 return PeekData<CHAR_TYPE> (GetPeekSpanData<CHAR_TYPE> ());
965 }
966 // even thought this looks complex, nearly all of it is if constexpr, and most important cases vanish into practically nothing,
967 // so inline
968 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
969 inline span<const CHAR_TYPE> String::GetData (const PeekSpanData& pds, Memory::StackBuffer<CHAR_TYPE, STACK_BUFFER_SZ>* possiblyUsedBuffer)
970 {
971 RequireNotNull (possiblyUsedBuffer);
972 using StorageCodePointType = PeekSpanData::StorageCodePointType;
973 if constexpr (same_as<CHAR_TYPE, wchar_t>) {
974 if constexpr (sizeof (CHAR_TYPE) == 2) {
975 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char16_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
976 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
977 }
978 else if constexpr (sizeof (wchar_t) == 4) {
979 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char32_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
980 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
981 }
982 }
983 else if constexpr (same_as<CHAR_TYPE, Character>) {
984 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char32_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
985 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
986 }
987 if constexpr (same_as<CHAR_TYPE, char8_t>) {
988 switch (pds.fInCP) {
989 case StorageCodePointType::eAscii:
990 // ASCII chars are subset of char8_t so any span of ascii is legit span of char8_t
991 return span{reinterpret_cast<const char8_t*> (pds.fAscii.data ()), pds.fAscii.size ()};
992 case StorageCodePointType::eSingleByteLatin1: {
993 // Convert ISO-Latin to UTF8 requires a little work sadly
994 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
995 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
996 }
997 case StorageCodePointType::eChar16: {
998 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar16));
999 return UTFConvert::kThe.ConvertSpan (pds.fChar16, span{*possiblyUsedBuffer});
1000 }
1001 case StorageCodePointType::eChar32: {
1002 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar32));
1003 return UTFConvert::kThe.ConvertSpan (pds.fChar32, span{*possiblyUsedBuffer});
1004 }
1005 default:
1007 return span<const CHAR_TYPE>{};
1008 }
1009 }
1010 else if constexpr (same_as<CHAR_TYPE, char16_t>) {
1011 switch (pds.fInCP) {
1012 case StorageCodePointType::eAscii:
1013 case StorageCodePointType::eSingleByteLatin1: {
1014 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
1015 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
1016 }
1017 case StorageCodePointType::eChar16:
1018 return pds.fChar16;
1019 case StorageCodePointType::eChar32: {
1020 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar32));
1021 return UTFConvert::kThe.ConvertSpan (pds.fChar32, span{*possiblyUsedBuffer});
1022 }
1023 default:
1025 return span<const CHAR_TYPE>{};
1026 }
1027 }
1028 else if constexpr (same_as<CHAR_TYPE, char32_t>) {
1029 switch (pds.fInCP) {
1030 case StorageCodePointType::eAscii:
1031 case StorageCodePointType::eSingleByteLatin1: {
1032 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
1033 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
1034 }
1035 case StorageCodePointType::eChar16: {
1036 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar16));
1037 return UTFConvert::kThe.ConvertSpan (pds.fChar16, span{*possiblyUsedBuffer});
1038 }
1039 case StorageCodePointType::eChar32:
1040 return pds.fChar32;
1041 default:
1043 return span<const CHAR_TYPE>{};
1044 }
1045 }
1046 }
1047 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
1048 inline span<const CHAR_TYPE> String::GetData (Memory::StackBuffer<CHAR_TYPE, STACK_BUFFER_SZ>* possiblyUsedBuffer) const
1049 {
1050 RequireNotNull (possiblyUsedBuffer);
1051 return GetData (GetPeekSpanData<CHAR_TYPE> (), possiblyUsedBuffer);
1052 }
1053 inline size_t String::length () const noexcept
1054 {
1055 return size ();
1056 }
1057 inline tuple<const wchar_t*, wstring_view> String::c_str (Memory::StackBuffer<wchar_t>* possibleBackingStore) const
1058 {
1059 // @todo FIRST check if default impl already returns c_str () and just use it if we can. ONLY if that fails, do we
1060 // convert, and write to possibleBackingStore
1061 RequireNotNull (possibleBackingStore);
1062 // quickie weak implementation
1063 wstring tmp{As<wstring> ()};
1064 possibleBackingStore->resize_uninitialized (tmp.size () + 1);
1065 copy (tmp.begin (), tmp.end (), possibleBackingStore->begin ());
1066 (*possibleBackingStore)[tmp.length ()] = '\0'; // assure NUL-terminated
1067 return make_tuple (possibleBackingStore->begin (), wstring_view{possibleBackingStore->begin (), tmp.length ()});
1068 }
1069 inline size_t String::find (Character c, size_t startAt) const
1070 {
1071 return Find (c, startAt, eWithCase).value_or (npos);
1072 }
1073 inline size_t String::find (const String& s, size_t startAt) const
1074 {
1075 return Find (s, startAt, eWithCase).value_or (npos);
1076 }
1077 inline size_t String::rfind (Character c) const
1078 {
1079 return RFind (c).value_or (npos);
1080 }
1081 inline Character String::back () const
1082 {
1083 Require (not empty ());
1084 _SafeReadRepAccessor accessor{this};
1085 size_t thisLen = accessor._ConstGetRep ().size ();
1086 return accessor._ConstGetRep ().GetAt (thisLen - 1);
1087 }
1088 inline Character String::front () const
1089 {
1090 Require (not empty ());
1091 _SafeReadRepAccessor accessor{this};
1092 return accessor._ConstGetRep ().GetAt (0);
1093 }
1094 inline String String::substr (size_t from, size_t count) const
1095 {
1096 _SafeReadRepAccessor accessor{this};
1097 size_t thisLen = accessor._ConstGetRep ().size ();
1098 if (from > thisLen) [[unlikely]] {
1099 static auto kException_ = out_of_range{"string index out of range"};
1100 Execution::Throw (kException_);
1101 }
1102 // @todo
1103 // Not QUITE correct - due to overflow issues, but pragmatically this is probably close enough
1104 size_t to = (count == npos) ? thisLen : (from + min (thisLen, count));
1105 return SubString_ (accessor, from, to);
1106 }
1107 inline strong_ordering String::operator<=> (const String& rhs) const
1108 {
1109 return ThreeWayComparer{}(*this, rhs);
1110 }
1111 template <IConvertibleToString T>
1112 inline strong_ordering String::operator<=> (T&& rhs) const
1113 requires (not same_as<remove_cvref_t<T>, String>)
1114 {
1115 return ThreeWayComparer{}(*this, forward<T> (rhs));
1116 }
1117 inline bool String::operator== (const String& rhs) const
1118 {
1119 return EqualsComparer{}(*this, rhs);
1120 }
1121 template <IConvertibleToString T>
1122 inline bool String::operator== (T&& rhs) const
1123 requires (not same_as<remove_cvref_t<T>, String>)
1124 {
1125 return EqualsComparer{}(*this, rhs);
1126 }
1127
1128 /*
1129 ********************************************************************************
1130 *************************** Literals::operator"" _k ****************************
1131 ********************************************************************************
1132 */
1133 inline namespace Literals {
1134 inline String operator""_k (const ASCII* s, size_t len)
1135 {
1136 return String::FromStringConstant (span<const char>{s, len});
1137 }
1138 inline String operator""_k (const char8_t* s, size_t len)
1139 {
1140 return String::FromStringConstant (span<const char8_t>{s, len});
1141 }
1142 inline String operator""_k (const wchar_t* s, size_t len)
1143 {
1144 return String::FromStringConstant (span<const wchar_t>{s, len});
1145 }
1146 inline String operator""_k (const char16_t* s, size_t len)
1147 {
1148 return String::FromStringConstant (span<const char16_t>{s, len});
1149 }
1150 inline String operator""_k (const char32_t* s, size_t len)
1151 {
1152 return String::FromStringConstant (span<const char32_t>{s, len});
1153 }
1154 }
1155
1156 /*
1157 ********************************************************************************
1158 **************************** String::EqualsComparer ****************************
1159 ********************************************************************************
1160 */
1161 constexpr String::EqualsComparer::EqualsComparer (CompareOptions co)
1162 : fCompareOptions{co}
1163 {
1164 }
1165 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1166 inline bool String::EqualsComparer::Cmp_ (LT&& lhs, RT&& rhs) const
1167 {
1168 // optimize very common case of ASCII String vs ASCII String
1170 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1171 if (auto rhsAsciiSpan = rhs.template PeekData<ASCII> ()) {
1172 if (fCompareOptions == eWithCase) {
1173 if (lhsAsciiSpan->size () != rhsAsciiSpan->size ()) {
1174 return false;
1175 }
1176 return Memory::CompareBytes (lhsAsciiSpan->data (), rhsAsciiSpan->data (), lhsAsciiSpan->size ()) == 0;
1177 }
1178 else {
1179 return Character::Compare (*lhsAsciiSpan, *rhsAsciiSpan, eCaseInsensitive) == 0;
1180 }
1181 }
1182 }
1183 }
1184 // And optimize case of String vs string_view (basic_string_view<ASCII>
1186 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1187 auto rhsAsciiSpan = span<const ASCII>{rhs};
1188 Require (Character::IsASCII (rhsAsciiSpan)); // in debug builds double check sv only used on ASCII strings with Stroika string library
1189 if (fCompareOptions == eWithCase) {
1190 if (lhsAsciiSpan->size () != rhsAsciiSpan.size ()) {
1191 return false;
1192 }
1193 return Memory::CompareBytes (lhsAsciiSpan->data (), rhsAsciiSpan.data (), lhsAsciiSpan->size ()) == 0;
1194 }
1195 else {
1196 return Character::Compare (*lhsAsciiSpan, rhsAsciiSpan, eCaseInsensitive) == 0;
1197 }
1198 }
1199 }
1200 return Cmp_Generic_ (forward<LT> (lhs), forward<RT> (rhs));
1201 }
1202 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1203 bool String::EqualsComparer::Cmp_Generic_ (LT&& lhs, RT&& rhs) const
1204 {
1205 // separate function - cuz large stackframe and on windows generates chkstk calls, so dont have in
1206 // same frame where we do optimizations
1207 // and use smaller 'stackbuffer' size to avoid invoking _chkstk on VisualStudio (could do patform specific, but not clear there is a need) --LGP 2023-09-15
1208 Memory::StackBuffer<Character, 256> ignore1;
1209 Memory::StackBuffer<Character, 256> ignore2;
1210 return Character::Compare (Private_::AsSpanOfCharacters_ (forward<LT> (lhs), &ignore1),
1211 Private_::AsSpanOfCharacters_ (forward<RT> (rhs), &ignore2), fCompareOptions) == 0;
1212 }
1213 template <IConvertibleToString LT, IConvertibleToString RT>
1214 inline bool String::EqualsComparer::operator() (LT&& lhs, RT&& rhs) const
1215 {
1216 if constexpr (requires { lhs.size (); } and requires { rhs.size (); }) {
1217 if (lhs.size () != rhs.size ()) {
1218 return false; // performance tweak
1219 }
1220 }
1221 if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LT> and Private_::ICanBeTreatedAsSpanOfCharacter_<RT>) {
1222 return Cmp_ (forward<LT> (lhs), forward<RT> (rhs));
1223 }
1224 else {
1225 // should almost never happen, but if it does, fall back on using String
1226 return operator() (String{forward<LT> (lhs)}, String{forward<RT> (rhs)});
1227 }
1228 }
1229
1230 /*
1231 ********************************************************************************
1232 **************************** String::ThreeWayComparer **************************
1233 ********************************************************************************
1234 */
1235 constexpr String::ThreeWayComparer::ThreeWayComparer (CompareOptions co)
1236 : fCompareOptions{co}
1237 {
1238 }
1239 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1240 inline strong_ordering String::ThreeWayComparer::Cmp_ (LT&& lhs, RT&& rhs) const
1241 {
1242 // optimize very common case of ASCII String vs ASCII String
1244 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1245 if (auto rhsAsciiSpan = rhs.template PeekData<ASCII> ()) {
1246 return Character::Compare (*lhsAsciiSpan, *rhsAsciiSpan, fCompareOptions);
1247 }
1248 }
1249 }
1250 return Cmp_Generic_ (forward<LT> (lhs), forward<RT> (rhs));
1251 }
1252 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1253 strong_ordering String::ThreeWayComparer::Cmp_Generic_ (LT&& lhs, RT&& rhs) const
1254 {
1255 // separate function - cuz large stackframe and on windows generates chkstk calls, so dont have in
1256 // same frame where we do optimizations
1257 // and use smaller 'stackbuffer' size to avoid invoking _chkstk on VisualStudio (could do patform specific, but not clear there is a need) --LGP 2023-09-15
1258 Memory::StackBuffer<Character, 256> ignore1;
1259 Memory::StackBuffer<Character, 256> ignore2;
1260 return Character::Compare (Private_::AsSpanOfCharacters_ (forward<LT> (lhs), &ignore1),
1261 Private_::AsSpanOfCharacters_ (forward<RT> (rhs), &ignore2), fCompareOptions);
1262 }
1263 template <IConvertibleToString LT, IConvertibleToString RT>
1264 inline strong_ordering String::ThreeWayComparer::operator() (LT&& lhs, RT&& rhs) const
1265 {
1266 if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LT> and Private_::ICanBeTreatedAsSpanOfCharacter_<RT>) {
1267 return Cmp_ (forward<LT> (lhs), forward<RT> (rhs));
1268 }
1269 else {
1270 // should almost never happen, but if it does, fall back on using String
1271 return operator() (String{forward<LT> (lhs)}, String{forward<RT> (rhs)});
1272 }
1273 }
1274
1275 /*
1276 ********************************************************************************
1277 **************************** String::LessComparer ******************************
1278 ********************************************************************************
1279 */
1280 constexpr String::LessComparer::LessComparer (CompareOptions co)
1281 : fComparer_{co}
1282 {
1283 }
1284 template <typename T1, typename T2>
1285 inline bool String::LessComparer::operator() (T1 lhs, T2 rhs) const
1286 {
1287 return fComparer_ (lhs, rhs) < 0;
1288 }
1289
1290 /*
1291 ********************************************************************************
1292 *********************************** operator+ **********************************
1293 ********************************************************************************
1294 */
1295 template <IConvertibleToString LHS_T, IConvertibleToString RHS_T>
1298 {
1300 return lhs.Concatenate (forward<RHS_T> (rhs));
1301 }
1302#if 0
1303 else if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LHS_T> and Private_::ICanBeTreatedAsSpanOfCharacter_<RHS_T>) {
1304 // maybe always true?
1306 span<const Character> lSpan = Private_::AsSpanOfCharacters_ (forward<LHS_T> (lhs), &ignored1);
1308 span<const Character> rSpan = Private_::AsSpanOfCharacters_ (forward<RHS_T> (rhs), &ignored2);
1309 Memory::StackBuffer<Character, 512> buf{Memory::eUninitialized, lSpan.size () + rSpan.size ()};
1310 span bufSpan{buf};
1311 Memory::CopySpanData (lSpan, bufSpan);
1312 Memory::CopySpanData (rSpan, bufSpan.subspan (lSpan.size ()));
1313 return String{bufSpan};
1314 }
1315#endif
1316 else {
1318 }
1319 }
1320
1321 inline const function<String (String, String, bool)> kDefaultStringCombiner = StringCombiner<String>{.fSeparator = ", "_k};
1322
1323#if qStroika_HasComponent_googletest
1324 inline void PrintTo (const String& s, std::ostream* os)
1325 {
1326 *os << s;
1327 }
1328#endif
1329
1330}
1331
1333
1334 // DEPRECATED
1335 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
1336 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
1337 DISABLE_COMPILER_MSC_WARNING_START (4996)
1338 template <typename CHAR_T>
1339 inline void String::Append (span<const CHAR_T> s)
1340 requires (same_as<CHAR_T, Character> or same_as<CHAR_T, char32_t>)
1341 {
1342 if (not s.empty ()) {
1343 Memory::StackBuffer<char32_t> ignored1;
1344 span<const char32_t> thisSpan = this->GetData (&ignored1);
1345 Memory::StackBuffer<char32_t> combinedBuf{Memory::eUninitialized, thisSpan.size () + s.size ()};
1346 Memory::CopySpanData (thisSpan, span{combinedBuf});
1347 char32_t* write2Buf = combinedBuf.data () + thisSpan.size ();
1348 for (auto i : s) {
1349 if constexpr (same_as<CHAR_T, Character>) {
1350 *write2Buf = i.template As<char32_t> ();
1351 }
1352 else {
1353 *write2Buf = i;
1354 }
1355 ++write2Buf;
1356 }
1357 *this = mk_ (span{combinedBuf});
1358 }
1359 }
1360 inline void String::Append (const wchar_t* from, const wchar_t* to)
1361 {
1362 Require (from <= to);
1363 if (from != to) {
1364 Memory::StackBuffer<wchar_t> ignored1;
1365 span<const wchar_t> thisSpan = this->GetData (&ignored1);
1366 Memory::StackBuffer<wchar_t> buf{Memory::eUninitialized, thisSpan.size () + (to - from)};
1367 span<wchar_t> bufSpan{buf};
1368 Memory::CopySpanData (thisSpan, bufSpan);
1369 Memory::CopySpanData (span{from, to}, bufSpan.subspan (thisSpan.size ()));
1370 *this = mk_ (bufSpan);
1371 }
1372 }
1373 inline void String::Append (Character c)
1374 {
1375 Append (&c, &c + 1);
1376 }
1377 inline void String::Append (const String& s)
1378 {
1379 Memory::StackBuffer<char32_t> ignored1;
1380 auto rhsSpan = s.GetData (&ignored1);
1381 Append (rhsSpan);
1382 }
1383 inline void String::Append (const wchar_t* s)
1384 {
1385 Append (s, s + ::wcslen (s));
1386 }
1387 inline void String::Append (const Character* from, const Character* to)
1388 {
1389 Append (span{from, to});
1390 }
1391 inline String& String::operator+= (Character appendage)
1392 {
1393 Append (appendage);
1394 return *this;
1395 }
1396 inline String& String::operator+= (const String& appendage)
1397 {
1398 Append (appendage);
1399 return *this;
1400 }
1401 inline String& String::operator+= (const wchar_t* appendageCStr)
1402 {
1403 Append (appendageCStr);
1404 return *this;
1405 }
1406
1407 inline void String::push_back (wchar_t c)
1408 {
1409 Append (Character (c));
1410 }
1411 inline void String::push_back (Character c)
1412 {
1413 Append (c);
1414 }
1415
1416 DISABLE_COMPILER_MSC_WARNING_END (4996)
1417 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
1418 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
1419
1420 [[deprecated ("Since Stroika v3.0d1 - just use _k, sv, or nothing")]] inline String operator""_ASCII (const char* str, size_t len)
1421 {
1422 return String{span{str, len}};
1423 }
1424 class [[deprecated ("Since Stroika v3.0 - just use String::FromStringConstant")]] String_Constant : public String {
1425 public:
1426 template <size_t SIZE>
1427 explicit String_Constant (const wchar_t (&cString)[SIZE])
1428 : String{String::FromStringConstant (std::basic_string_view<wchar_t>{cString, SIZE - 1})}
1429 {
1430 }
1431
1432 String_Constant (const wchar_t* start, const wchar_t* end)
1433 : String{String::FromStringConstant (std::basic_string_view<wchar_t>{start, static_cast<size_t> (end - start)})}
1434 {
1435 }
1436
1437 String_Constant (const std::basic_string_view<wchar_t>& str)
1439 {
1440 }
1441 };
1442}
1443namespace Stroika::Foundation::Characters::Concrete {
1444 class [[deprecated ("Since Stroika v3.0 - just use String::FromStringConstant")]] String_ExternalMemoryOwnership_ApplicationLifetime : public String {
1445 public:
1446 template <size_t SIZE>
1447 explicit String_ExternalMemoryOwnership_ApplicationLifetime (const wchar_t (&cString)[SIZE - 1])
1448 : String{String::FromStringConstant (basic_string_view<wchar_t>{cString, SIZE})}
1449 {
1450 }
1451
1452 String_ExternalMemoryOwnership_ApplicationLifetime (const wchar_t* start, const wchar_t* end)
1453 : String{String::FromStringConstant (basic_string_view<wchar_t>{start, static_cast<size_t> (end - start)})}
1454 {
1455 }
1456
1457 String_ExternalMemoryOwnership_ApplicationLifetime (const basic_string_view<wchar_t>& str)
1459 {
1460 }
1461 };
1462}
1463
1465
1466 template <>
1467 String StringCombiner<String>::operator() (const String& lhs, const String& rhs, bool isLast) const;
1468
1469 template <typename STRING>
1470 STRING StringCombiner<STRING>::operator() (const STRING& lhs, const STRING& rhs, bool isLast) const
1471 {
1472 STRING sb{lhs};
1473 if (isLast and fSpecialSeparatorForLastPair) [[unlikely]] {
1474 sb = sb + *fSpecialSeparatorForLastPair;
1475 }
1476 else {
1477 sb = sb + fSeparator;
1478 }
1479 sb = sb + rhs;
1480 return sb;
1481 }
1482}
#define AssertNotImplemented()
Definition Assertions.h:402
#define RequireNotReached()
Definition Assertions.h:386
#define RequireNotNull(p)
Definition Assertions.h:348
#define RequireExpression(c)
Definition Assertions.h:268
#define AssertNotReached()
Definition Assertions.h:356
#define EnsureMember(p, c)
Definition Assertions.h:320
constexpr bool IsASCII() const noexcept
Return true iff the given character (or all in span) is (are) in the ascii range [0....
static void CheckLatin1(span< const CHAR_T > s)
if not IsLatin1 (arg) throw RuntimeException...
static constexpr void CheckASCII(span< const CHAR_T > s)
if not IsASCII (arg) throw RuntimeException...
static constexpr ASCIIOrLatin1Result IsASCIIOrLatin1(span< const CHAR_T > s) noexcept
static constexpr strong_ordering Compare(span< const CHAR_T, E1 > lhs, span< const CHAR_T, E2 > rhs, CompareOptions co) noexcept
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual bool Contains(Character c, CompareOptions co=eWithCase) const
Definition String.inl:699
nonvirtual size_t length() const noexcept
Definition String.inl:1053
static String FromNarrowString(const char *from, const locale &l)
Definition String.inl:342
nonvirtual bool Matches(const RegularExpression &regEx) const
Definition String.cpp:1145
static String FromStringConstant(const CHAR_T(&cString)[SIZE])
Take the given argument data (constant span) - which must remain unchanged - constant - for the appli...
Definition String.inl:388
nonvirtual String ColValue(size_t i, const String &valueIfMissing={}) const
see Col(i) - but with default value of empty string
Definition String.inl:717
nonvirtual bool operator==(const String &rhs) const
Definition String.inl:1117
static String FromSDKString(const SDKChar *from)
Definition String.inl:449
nonvirtual String LimitLength(size_t maxLen, StringShorteningPreference keepPref=StringShorteningPreference::ePreferKeepLeft) const
return the first maxLen (or fewer if string shorter) characters of this string (adding ellipsis if tr...
Definition String.inl:747
nonvirtual string AsNarrowSDKString() const
Definition String.inl:836
nonvirtual String InsertAt(Character c, size_t at) const
Definition String.inl:721
nonvirtual size_t rfind(Character c) const
Definition String.inl:1077
static String FromNarrowSDKString(const char *from)
Definition String.inl:472
nonvirtual String Concatenate(T &&rhs) const
appends 'rhs' string to this string (without modifying this string) and returns the combined string
nonvirtual SDKString AsSDKString() const
Definition String.inl:808
nonvirtual size_t size() const noexcept
Definition String.inl:536
nonvirtual String Replace(size_t from, size_t to, const String &replacement) const
Definition String.cpp:1057
nonvirtual String SubString(SZ from) const
nonvirtual strong_ordering operator<=>(const String &rhs) const
Definition String.inl:1107
nonvirtual Character back() const
Definition String.inl:1081
nonvirtual span< CHAR_T > CopyTo(span< CHAR_T > s) const
nonvirtual PeekSpanData GetPeekSpanData() const
return the constant character data inside the string in the form of a case variant union of different...
nonvirtual String SafeSubString(SZ from) const
nonvirtual Character front() const
Definition String.inl:1088
nonvirtual String Skip(size_t n) const
Return a substring of this string, starting at 'argument' n. If n > size(), return empty string.
Definition String.inl:606
static span< const CHAR_TYPE > GetData(const PeekSpanData &pds, Memory::StackBuffer< CHAR_TYPE, STACK_BUFFER_SZ > *possiblyUsedBuffer)
return the constant character data inside the string (rep) in the form of a span, possibly quickly an...
Definition String.inl:969
nonvirtual String RemoveAt(size_t charAt) const
Definition String.inl:610
nonvirtual optional< T > AsASCIIQuietly() const
static String FromLatin1(const CHAR_T *cString)
Definition String.inl:357
nonvirtual const Character operator[](size_t i) const noexcept
return (read-only) Character object
Definition String.inl:741
static String FromUTF8(span< CHAR_T > from)
Definition String.inl:422
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
Definition String.inl:687
nonvirtual String substr(size_t from, size_t count=npos) const
Definition String.inl:1094
nonvirtual size_t find(Character c, size_t startAt=0) const
Definition String.inl:1069
static const UTFConvert kThe
Nearly always use this default UTFConvert.
Definition UTFConvert.h:369
static constexpr bool AllFitsInTwoByteEncoding(span< const CHAR_T > s) noexcept
nonvirtual span< TRG_T > ConvertSpan(span< const SRC_T > source, span< TRG_T > target) const
Convert between UTF-N encoded (including the special case of ASCII, and Latin1) character spans (e....
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual size_t size() const noexcept
nonvirtual void resize_uninitialized(size_t nElements)
same as resize (), except leaves newly created elements uninitialized (requires is_trivially_copyable...
An Iterator<T> is a copyable object which allows traversing the contents of some container.
Definition Iterator.h:253
returns true iff T == u8string, u16string, u32string, or wstring - which std::string types can be una...
Definition String.h:116
wstring NarrowSDK2Wide(span< const char > s)
char ASCII
Stroika's string/character classes treat 'char' as being an ASCII character.
Definition Character.h:59
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar
Definition SDKChar.h:71
basic_string< SDKChar > SDKString
Definition SDKString.h:38
String operator+(LHS_T &&lhs, RHS_T &&rhs)
Definition String.inl:1296
const function< String(String, String, bool)> kDefaultStringCombiner
Definition String.inl:1321
string SDK2Narrow(span< const SDKChar > s)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
STL namespace.
constexpr EqualsComparer(CompareOptions co=eWithCase)
Definition String.inl:1161
nonvirtual bool operator()(LT &&lhs, RT &&rhs) const
Summary data for raw contents of rep - each rep will support at least one of these span forms.
Definition String.h:1280
StringCombiner is a simple function object used to combine two strings visually - used in Iterable<>:...
Definition String.h:1933