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