Stroika Library 3.0d16
 
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 (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.c_str (), 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::RemoveAt (size_t charAt) const
605 {
606 return RemoveAt (charAt, charAt + 1);
607 }
608 inline String String::RemoveAt (pair<size_t, size_t> fromTo) const
609 {
610 return RemoveAt (fromTo.first, fromTo.second);
611 }
612 inline bool String::empty () const noexcept
613 {
614 _SafeReadRepAccessor accessor{this};
615 return accessor._ConstGetRep ().size () == 0;
616 }
617 namespace Private_ {
618 // match index starts with 1 (and requires match.size () >=2)
619 inline void ExtractMatches_ ([[maybe_unused]] const wsmatch& base_match, [[maybe_unused]] size_t currentUnpackIndex)
620 {
621 }
622 template <Common::IAnyOf<optional<String>*, String*, nullptr_t> SUBMATCH, typename... OPTIONAL_STRINGS>
623 void ExtractMatches_ (const wsmatch& base_match, size_t currentUnpackIndex, SUBMATCH subMatchI, OPTIONAL_STRINGS&&... remainingSubmatches)
624 {
625 if (currentUnpackIndex < base_match.size ()) [[likely]] {
626 if constexpr (not same_as<SUBMATCH, nullptr_t>) {
627 if (subMatchI != nullptr) {
628 *subMatchI = base_match[currentUnpackIndex].str ();
629 }
630 }
631 ExtractMatches_ (base_match, currentUnpackIndex + 1, forward<OPTIONAL_STRINGS> (remainingSubmatches)...);
632 }
633 }
634 const wregex& RegularExpression_GetCompiled (const RegularExpression& regExp);
635 }
636 template <Common::IAnyOf<optional<String>*, String*, nullptr_t>... OPTIONAL_STRINGS>
637 bool String::Matches (const RegularExpression& regEx, OPTIONAL_STRINGS&&... subMatches) const
638 {
639 wstring tmp{As<wstring> ()};
640 wsmatch baseMatch;
641 if (regex_match (tmp, baseMatch, Private_::RegularExpression_GetCompiled (regEx))) {
642 Private_::ExtractMatches_ (baseMatch, 1, forward<OPTIONAL_STRINGS> (subMatches)...);
643 return true;
644 }
645 return false;
646 }
647 template <size_t I>
648 optional<Common::RepeatedTuple_t<I, String>> String::Matches (const RegularExpression& regEx) const
649 {
650 wstring tmp{As<wstring> ()};
651 wsmatch baseMatch;
652 if (regex_match (tmp, baseMatch, Private_::RegularExpression_GetCompiled (regEx))) {
653 //tmphack impl - cuz my template skills suck --LGP 2025-01-24
654 if constexpr (I == 0) {
655 return make_tuple ();
656 }
657 else if constexpr (I == 1) {
658 return make_tuple (String{baseMatch[1].str ()});
659 }
660 else if constexpr (I == 2) {
661 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()});
662 }
663 else if constexpr (I == 3) {
664 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()});
665 }
666 else if constexpr (I == 4) {
667 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()},
668 String{baseMatch[4].str ()});
669 }
670 else if constexpr (I == 5) {
671 return make_tuple (String{baseMatch[1].str ()}, String{baseMatch[2].str ()}, String{baseMatch[3].str ()},
672 String{baseMatch[4].str ()}, String{baseMatch[5].str ()});
673 }
674 else {
676 return nullopt;
677 }
678 }
679 return nullopt;
680 }
681 inline optional<size_t> String::Find (Character c, CompareOptions co) const
682 {
683 return Find (c, 0, co);
684 }
685 inline optional<size_t> String::Find (const String& subString, CompareOptions co) const
686 {
687 return Find (subString, 0, co);
688 }
689 inline Traversal::Iterator<Character> String::Find (const function<bool (Character item)>& that) const
690 {
691 return inherited::Find (that);
692 }
693 inline bool String::Contains (Character c, CompareOptions co) const
694 {
695 return static_cast<bool> (Find (c, co));
696 }
697 inline bool String::Contains (const String& subString, CompareOptions co) const
698 {
699 return static_cast<bool> (Find (subString, co));
700 }
701 inline bool String::ContainsAny (Iterable<Character> cs, CompareOptions co) const
702 {
703 auto comparer = Character::EqualsComparer{co};
704 auto checkEachCharacter = [&] (Character c) -> bool { return cs.Any ([&] (const Character c2) { return comparer (c, c2); }); };
705 return Find (checkEachCharacter) != nullptr;
706 }
707 inline String String::Replace (pair<size_t, size_t> fromTo, const String& replacement) const
708 {
709 return Replace (fromTo.first, fromTo.second, replacement);
710 }
711 inline String String::ColValue (size_t i, const String& valueIfMissing) const
712 {
713 return Col (i).value_or (valueIfMissing);
714 }
715 inline String String::InsertAt (Character c, size_t at) const
716 {
717 return InsertAt (span<const Character>{&c, 1}, at);
718 }
719 inline String String::InsertAt (const String& s, size_t at) const
720 {
722 return InsertAt (s.GetData (&ignored1), at);
723 }
724 inline String String::InsertAt (span<Character> s, size_t at) const
725 {
726 return InsertAt (Memory::ConstSpan (s), at);
727 }
728 inline const Character String::GetCharAt (size_t i) const noexcept
729 {
730 _SafeReadRepAccessor accessor{this};
731 Require (i >= 0);
732 Require (i < accessor._ConstGetRep ().size ());
733 return accessor._ConstGetRep ().GetAt (i);
734 }
735 inline const Character String::operator[] (size_t i) const noexcept
736 {
737 Require (i >= 0);
738 Require (i < size ());
739 return GetCharAt (i);
740 }
741 inline String String::LimitLength (size_t maxLen, StringShorteningPreference keepPref) const
742 {
743#if qCompiler_vswprintf_on_elispisStr_Buggy
744 static const String kELIPSIS_{"..."_k};
745#else
746 static const String kELIPSIS_{u"\u2026"sv}; // OR "..."
747#endif
748 return LimitLength (maxLen, keepPref, kELIPSIS_);
749 }
750 template <typename T>
751 inline T String::As () const
752 requires (IBasicUNICODEStdString<T> or same_as<T, String> or constructible_from<T, wstring>)
753 {
754 if constexpr (same_as<T, u8string>) {
755 return AsUTF8<T> ();
756 }
757 else if constexpr (same_as<T, u16string>) {
758 return AsUTF16<T> ();
759 }
760 else if constexpr (same_as<T, u32string>) {
761 return AsUTF32<T> ();
762 }
763 else if constexpr (same_as<T, wstring>) {
764 if constexpr (sizeof (wchar_t) == 2) {
765 return AsUTF16<T> ();
766 }
767 else {
768 return AsUTF32<T> ();
769 }
770 }
771 else if constexpr (same_as<T, String>) {
772 return *this;
773 }
774 else if constexpr (constructible_from<T, wstring>) {
775 return T{As<wstring> ()};
776 }
777 }
778 template <typename T>
779 inline T String::AsUTF8 () const
780 requires (same_as<T, string> or same_as<T, u8string>)
781 {
782 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
783 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1);
784 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
785 }
786 template <typename T>
787 inline T String::AsUTF16 () const
788 requires (same_as<T, u16string> or (sizeof (wchar_t) == sizeof (char16_t) and same_as<T, wstring>))
789 {
790 Memory::StackBuffer<char16_t> maybeIgnoreBuf1;
791 span<const char16_t> thisData = GetData (&maybeIgnoreBuf1);
792 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
793 }
794 template <typename T>
795 inline T String::AsUTF32 () const
796 requires (same_as<T, u32string> or (sizeof (wchar_t) == sizeof (char32_t) and same_as<T, wstring>))
797 {
798 Memory::StackBuffer<char32_t> maybeIgnoreBuf1;
799 span<const char32_t> thisData = GetData (&maybeIgnoreBuf1);
800 return T{reinterpret_cast<const typename T::value_type*> (thisData.data ()), thisData.size ()};
801 }
803 {
804#if qTargetPlatformSDKUseswchar_t
805 Memory::StackBuffer<wchar_t> maybeIgnoreBuf1;
806 span<const wchar_t> thisData = GetData (&maybeIgnoreBuf1);
807 return SDKString{thisData.begin (), thisData.end ()};
808#elif qStroika_Foundation_Common_Platform_MacOS
809 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
810 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1);
811 return SDKString{thisData.begin (), thisData.end ()}; // @todo DOCUMENT THAT MACOS USES UTF8 - SRC - LOGIC/RATIONALE
812#else
813 return AsNarrowString (locale{}); // @todo document why - linux one rationale - default - similar
814#endif
815 }
817 {
818#if qTargetPlatformSDKUseswchar_t
819 Memory::StackBuffer<wchar_t> maybeIgnoreBuf1;
820 span<const wchar_t> thisData = GetData (&maybeIgnoreBuf1);
821 return SDKString{thisData.begin (), thisData.end ()};
822#elif qStroika_Foundation_Common_Platform_MacOS
823 Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
824 span<const char8_t> thisData = GetData (&maybeIgnoreBuf1); // Note this always works, since we can always map to UTF-8 any Stroika string
825 return SDKString{thisData.begin (), thisData.end ()}; // @todo DOCUMENT THAT MACOS USES UTF8 - SRC - LOGIC/RATIONALE
826#else
827 return AsNarrowString (locale{}, eIgnoreErrors); // @todo document why - linux one rationale - default - similar
828#endif
829 }
830 inline string String::AsNarrowSDKString () const
831 {
832 return SDK2Narrow (AsSDKString ());
833 }
835 {
836 return SDK2Narrow (AsSDKString (eIgnoreErrors), AllowMissingCharacterErrorsFlag::eIgnoreErrors);
837 }
838 template <typename T>
839 inline T String::AsASCII () const
840 requires requires (T* into) {
841 { into->empty () } -> same_as<bool>;
842 { into->push_back (ASCII{0}) };
843 }
844 {
845 // @todo possibly rewrite/inline impl to avoid map to optional<T> which involves copying
846 if (auto p = AsASCIIQuietly<T> ()) {
847 return *p;
848 }
849 else {
850 ThrowInvalidAsciiException_ ();
851 }
852 }
853 template <typename T>
854 inline optional<T> String::AsASCIIQuietly () const
855 requires requires (T* into) {
856 { into->empty () } -> same_as<bool>;
857 { into->push_back (ASCII{0}) };
858 }
859 {
860 // @todo OPTIMIZE - PeekSpanData - may already be ASCII - OPTIMIZE THAT CASE!!!
861 Memory::StackBuffer<wchar_t> ignored1;
862 auto thisSpan = GetData (&ignored1);
863 T s;
864 return Character::AsASCIIQuietly<T> (thisSpan, &s) ? s : optional<T>{};
865 }
866 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
867 inline String::PeekSpanData String::GetPeekSpanData () const
868 {
869 using StorageCodePointType = PeekSpanData::StorageCodePointType;
870 StorageCodePointType preferredSCP{};
871 if constexpr (same_as<remove_cv_t<CHAR_TYPE>, ASCII>) {
872 preferredSCP = StorageCodePointType::eAscii;
873 }
874 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, Latin1>) {
875 preferredSCP = StorageCodePointType::eSingleByteLatin1;
876 }
877 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char8_t>) {
878 preferredSCP = StorageCodePointType::eAscii; // not clear what's best in this case but probably doesn't matter
879 }
880 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char16_t>) {
881 preferredSCP = StorageCodePointType::eChar16;
882 }
883 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, char32_t> or same_as<remove_cv_t<CHAR_TYPE>, Character>) {
884 preferredSCP = StorageCodePointType::eChar32;
885 }
886 if constexpr (same_as<remove_cv_t<CHAR_TYPE>, wchar_t>) {
887 if constexpr (sizeof (wchar_t) == 2) {
888 preferredSCP = StorageCodePointType::eChar16;
889 }
890 else if constexpr (sizeof (wchar_t) == 4) {
891 preferredSCP = StorageCodePointType::eChar32;
892 }
893 }
894 else if constexpr (same_as<remove_cv_t<CHAR_TYPE>, Character>) {
895 // later will map to char32_t, but for now same as wchar_t
896 if constexpr (sizeof (wchar_t) == 2) {
897 preferredSCP = StorageCodePointType::eChar16;
898 }
899 else if constexpr (sizeof (wchar_t) == 4) {
900 preferredSCP = StorageCodePointType::eChar32;
901 }
902 }
903 return _SafeReadRepAccessor{this}._ConstGetRep ().PeekData (preferredSCP);
904 }
905 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
906 inline optional<span<const CHAR_TYPE>> String::PeekData (const PeekSpanData& pds)
907 {
908 using StorageCodePointType = PeekSpanData::StorageCodePointType;
909 if constexpr (same_as<CHAR_TYPE, ASCII>) {
910 if (pds.fInCP == StorageCodePointType::eAscii) {
911 return pds.fAscii;
912 }
913 }
914 else if constexpr (same_as<CHAR_TYPE, Latin1>) {
915 if (pds.fInCP == StorageCodePointType::eSingleByteLatin1) {
916 return pds.fSingleByteLatin1;
917 }
918 }
919 else if constexpr (same_as<CHAR_TYPE, char8_t>) {
920 if (pds.fInCP == StorageCodePointType::eAscii) { // single-byte-latin1 not legal char8_t format
921 return pds.fAscii;
922 }
923 }
924 else if constexpr (same_as<CHAR_TYPE, char16_t>) {
925 if (pds.fInCP == StorageCodePointType::eChar16) {
926 return pds.fChar16;
927 }
928 }
929 else if constexpr (same_as<CHAR_TYPE, char32_t>) {
930 if (pds.fInCP == StorageCodePointType::eChar32) {
931 return pds.fChar32;
932 }
933 }
934 else if constexpr (same_as<CHAR_TYPE, wchar_t>) {
935 if constexpr (sizeof (wchar_t) == 2) {
936 if (pds.fInCP == StorageCodePointType::eChar16) {
937 return span<const wchar_t>{reinterpret_cast<const wchar_t*> (pds.fChar16.data ()), pds.fChar16.size ()};
938 }
939 }
940 else if constexpr (sizeof (wchar_t) == 4) {
941 if (pds.fInCP == StorageCodePointType::eChar32) {
942 return span<const wchar_t>{reinterpret_cast<const wchar_t*> (pds.fChar32.data ()), pds.fChar32.size ()};
943 }
944 }
945 return span<const wchar_t>{};
946 }
947 else if constexpr (same_as<CHAR_TYPE, Character>) {
948 if (pds.fInCP == StorageCodePointType::eChar32) {
949 return span<const Character>{reinterpret_cast<const Character*> (pds.fChar32.data ()), pds.fChar32.size ()};
950 }
951 return span<const Character>{};
952 }
953 return nullopt; // can easily happen if you request a type that is not stored in the rep
954 }
955 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
956 inline optional<span<const CHAR_TYPE>> String::PeekData () const
957 {
958 return PeekData<CHAR_TYPE> (GetPeekSpanData<CHAR_TYPE> ());
959 }
960 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
961 span<const CHAR_TYPE> String::GetData (const PeekSpanData& pds, Memory::StackBuffer<CHAR_TYPE, STACK_BUFFER_SZ>* possiblyUsedBuffer)
962 {
963 RequireNotNull (possiblyUsedBuffer);
964 using StorageCodePointType = PeekSpanData::StorageCodePointType;
965 if constexpr (same_as<CHAR_TYPE, wchar_t>) {
966 if constexpr (sizeof (CHAR_TYPE) == 2) {
967 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char16_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
968 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
969 }
970 else if constexpr (sizeof (wchar_t) == 4) {
971 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char32_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
972 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
973 }
974 }
975 else if constexpr (same_as<CHAR_TYPE, Character>) {
976 auto p = GetData (pds, reinterpret_cast<Memory::StackBuffer<char32_t, STACK_BUFFER_SZ>*> (possiblyUsedBuffer));
977 return span<const CHAR_TYPE>{reinterpret_cast<const CHAR_TYPE*> (p.data ()), p.size ()};
978 }
979 if constexpr (same_as<CHAR_TYPE, char8_t>) {
980 switch (pds.fInCP) {
981 case StorageCodePointType::eAscii:
982 // ASCII chars are subset of char8_t so any span of ascii is legit span of char8_t
983 return span{reinterpret_cast<const char8_t*> (pds.fAscii.data ()), pds.fAscii.size ()};
984 case StorageCodePointType::eSingleByteLatin1: {
985 // Convert ISO-Latin to UTF8 requires a little work sadly
986 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
987 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
988 }
989 case StorageCodePointType::eChar16: {
990 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar16));
991 return UTFConvert::kThe.ConvertSpan (pds.fChar16, span{*possiblyUsedBuffer});
992 }
993 case StorageCodePointType::eChar32: {
994 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar32));
995 return UTFConvert::kThe.ConvertSpan (pds.fChar32, span{*possiblyUsedBuffer});
996 }
997 default:
999 return span<const CHAR_TYPE>{};
1000 }
1001 }
1002 else if constexpr (same_as<CHAR_TYPE, char16_t>) {
1003 switch (pds.fInCP) {
1004 case StorageCodePointType::eAscii:
1005 case StorageCodePointType::eSingleByteLatin1: {
1006 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
1007 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
1008 }
1009 case StorageCodePointType::eChar16:
1010 return pds.fChar16;
1011 case StorageCodePointType::eChar32: {
1012 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar32));
1013 return UTFConvert::kThe.ConvertSpan (pds.fChar32, span{*possiblyUsedBuffer});
1014 }
1015 default:
1017 return span<const CHAR_TYPE>{};
1018 }
1019 }
1020 else if constexpr (same_as<CHAR_TYPE, char32_t>) {
1021 switch (pds.fInCP) {
1022 case StorageCodePointType::eAscii:
1023 case StorageCodePointType::eSingleByteLatin1: {
1024 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fSingleByteLatin1));
1025 return UTFConvert::kThe.ConvertSpan (pds.fSingleByteLatin1, span{*possiblyUsedBuffer});
1026 }
1027 case StorageCodePointType::eChar16: {
1028 possiblyUsedBuffer->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_TYPE> (pds.fChar16));
1029 return UTFConvert::kThe.ConvertSpan (pds.fChar16, span{*possiblyUsedBuffer});
1030 }
1031 case StorageCodePointType::eChar32:
1032 return pds.fChar32;
1033 default:
1035 return span<const CHAR_TYPE>{};
1036 }
1037 }
1038 }
1039 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
1040 inline span<const CHAR_TYPE> String::GetData (Memory::StackBuffer<CHAR_TYPE, STACK_BUFFER_SZ>* possiblyUsedBuffer) const
1041 {
1042 RequireNotNull (possiblyUsedBuffer);
1043 return GetData (GetPeekSpanData<CHAR_TYPE> (), possiblyUsedBuffer);
1044 }
1045 inline size_t String::length () const noexcept
1046 {
1047 return size ();
1048 }
1049 inline tuple<const wchar_t*, wstring_view> String::c_str (Memory::StackBuffer<wchar_t>* possibleBackingStore) const
1050 {
1051 // @todo FIRST check if default impl already returns c_str () and just use it if we can. ONLY if that fails, do we
1052 // convert, and write to possibleBackingStore
1053 RequireNotNull (possibleBackingStore);
1054 // quickie weak implementation
1055 wstring tmp{As<wstring> ()};
1056 possibleBackingStore->resize_uninitialized (tmp.size () + 1);
1057 copy (tmp.begin (), tmp.end (), possibleBackingStore->begin ());
1058 (*possibleBackingStore)[tmp.length ()] = '\0'; // assure NUL-terminated
1059 return make_tuple (possibleBackingStore->begin (), wstring_view{possibleBackingStore->begin (), tmp.length ()});
1060 }
1061 inline size_t String::find (Character c, size_t startAt) const
1062 {
1063 return Find (c, startAt, eWithCase).value_or (npos);
1064 }
1065 inline size_t String::find (const String& s, size_t startAt) const
1066 {
1067 return Find (s, startAt, eWithCase).value_or (npos);
1068 }
1069 inline size_t String::rfind (Character c) const
1070 {
1071 return RFind (c).value_or (npos);
1072 }
1073 inline Character String::back () const
1074 {
1075 Require (not empty ());
1076 _SafeReadRepAccessor accessor{this};
1077 size_t thisLen = accessor._ConstGetRep ().size ();
1078 return accessor._ConstGetRep ().GetAt (thisLen - 1);
1079 }
1080 inline Character String::front () const
1081 {
1082 Require (not empty ());
1083 _SafeReadRepAccessor accessor{this};
1084 return accessor._ConstGetRep ().GetAt (0);
1085 }
1086 inline String String::substr (size_t from, size_t count) const
1087 {
1088 _SafeReadRepAccessor accessor{this};
1089 size_t thisLen = accessor._ConstGetRep ().size ();
1090 if (from > thisLen) [[unlikely]] {
1091 static auto kException_ = out_of_range{"string index out of range"};
1092 Execution::Throw (kException_);
1093 }
1094 // @todo
1095 // Not QUITE correct - due to overflow issues, but pragmatically this is probably close enough
1096 size_t to = (count == npos) ? thisLen : (from + min (thisLen, count));
1097 return SubString_ (accessor, from, to);
1098 }
1099 inline strong_ordering String::operator<=> (const String& rhs) const
1100 {
1101 return ThreeWayComparer{}(*this, rhs);
1102 }
1103 template <IConvertibleToString T>
1104 inline strong_ordering String::operator<=> (T&& rhs) const
1105 requires (not same_as<remove_cvref_t<T>, String>)
1106 {
1107 return ThreeWayComparer{}(*this, forward<T> (rhs));
1108 }
1109 inline bool String::operator== (const String& rhs) const
1110 {
1111 return EqualsComparer{}(*this, rhs);
1112 }
1113 template <IConvertibleToString T>
1114 inline bool String::operator== (T&& rhs) const
1115 requires (not same_as<remove_cvref_t<T>, String>)
1116 {
1117 return EqualsComparer{}(*this, rhs);
1118 }
1119
1120 /*
1121 ********************************************************************************
1122 *************************** Literals::operator"" _k ****************************
1123 ********************************************************************************
1124 */
1125 inline namespace Literals {
1126 inline String operator"" _k (const ASCII* s, size_t len)
1127 {
1128 return String::FromStringConstant (span<const char>{s, len});
1129 }
1130 inline String operator"" _k (const char8_t* s, size_t len)
1131 {
1132 return String::FromStringConstant (span<const char8_t>{s, len});
1133 }
1134 inline String operator"" _k (const wchar_t* s, size_t len)
1135 {
1136 return String::FromStringConstant (span<const wchar_t>{s, len});
1137 }
1138 inline String operator"" _k (const char16_t* s, size_t len)
1139 {
1140 return String::FromStringConstant (span<const char16_t>{s, len});
1141 }
1142 inline String operator"" _k (const char32_t* s, size_t len)
1143 {
1144 return String::FromStringConstant (span<const char32_t>{s, len});
1145 }
1146 }
1147
1148 /*
1149 ********************************************************************************
1150 **************************** String::EqualsComparer ****************************
1151 ********************************************************************************
1152 */
1153 constexpr String::EqualsComparer::EqualsComparer (CompareOptions co)
1154 : fCompareOptions{co}
1155 {
1156 }
1157 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1158 inline bool String::EqualsComparer::Cmp_ (LT&& lhs, RT&& rhs) const
1159 {
1160 // optimize very common case of ASCII String vs ASCII String
1161 if constexpr (same_as<remove_cvref_t<LT>, String> and same_as<remove_cvref_t<RT>, String>) {
1162 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1163 if (auto rhsAsciiSpan = rhs.template PeekData<ASCII> ()) {
1164 if (fCompareOptions == eWithCase) {
1165 if (lhsAsciiSpan->size () != rhsAsciiSpan->size ()) {
1166 return false;
1167 }
1168 return Memory::CompareBytes (lhsAsciiSpan->data (), rhsAsciiSpan->data (), lhsAsciiSpan->size ()) == 0;
1169 }
1170 else {
1171 return Character::Compare (*lhsAsciiSpan, *rhsAsciiSpan, eCaseInsensitive) == 0;
1172 }
1173 }
1174 }
1175 }
1176 // And optimize case of String vs string_view (basic_string_view<ASCII>
1177 else if constexpr (same_as<remove_cvref_t<LT>, String> and same_as<remove_cvref_t<RT>, basic_string_view<ASCII>>) {
1178 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1179 auto rhsAsciiSpan = span<const ASCII>{rhs};
1180 Require (Character::IsASCII (rhsAsciiSpan)); // in debug builds double check sv only used on ASCII strings with Stroika string library
1181 if (fCompareOptions == eWithCase) {
1182 if (lhsAsciiSpan->size () != rhsAsciiSpan.size ()) {
1183 return false;
1184 }
1185 return Memory::CompareBytes (lhsAsciiSpan->data (), rhsAsciiSpan.data (), lhsAsciiSpan->size ()) == 0;
1186 }
1187 else {
1188 return Character::Compare (*lhsAsciiSpan, rhsAsciiSpan, eCaseInsensitive) == 0;
1189 }
1190 }
1191 }
1192 return Cmp_Generic_ (forward<LT> (lhs), forward<RT> (rhs));
1193 }
1194 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1195 bool String::EqualsComparer::Cmp_Generic_ (LT&& lhs, RT&& rhs) const
1196 {
1197 // separate function - cuz large stackframe and on windows generates chkstk calls, so dont have in
1198 // same frame where we do optimizations
1199 // 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
1200 Memory::StackBuffer<Character, 256> ignore1;
1201 Memory::StackBuffer<Character, 256> ignore2;
1202 return Character::Compare (Private_::AsSpanOfCharacters_ (forward<LT> (lhs), &ignore1),
1203 Private_::AsSpanOfCharacters_ (forward<RT> (rhs), &ignore2), fCompareOptions) == 0;
1204 }
1205 template <IConvertibleToString LT, IConvertibleToString RT>
1206 inline bool String::EqualsComparer::operator() (LT&& lhs, RT&& rhs) const
1207 {
1208 if constexpr (requires { lhs.size (); } and requires { rhs.size (); }) {
1209 if (lhs.size () != rhs.size ()) {
1210 return false; // performance tweak
1211 }
1212 }
1213 if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LT> and Private_::ICanBeTreatedAsSpanOfCharacter_<RT>) {
1214 return Cmp_ (forward<LT> (lhs), forward<RT> (rhs));
1215 }
1216 else {
1217 // should almost never happen, but if it does, fall back on using String
1218 return operator() (String{forward<LT> (lhs)}, String{forward<RT> (rhs)});
1219 }
1220 }
1221
1222 /*
1223 ********************************************************************************
1224 **************************** String::ThreeWayComparer **************************
1225 ********************************************************************************
1226 */
1227 constexpr String::ThreeWayComparer::ThreeWayComparer (CompareOptions co)
1228 : fCompareOptions{co}
1229 {
1230 }
1231 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1232 inline strong_ordering String::ThreeWayComparer::Cmp_ (LT&& lhs, RT&& rhs) const
1233 {
1234 // optimize very common case of ASCII String vs ASCII String
1235 if constexpr (same_as<remove_cvref_t<LT>, String> and same_as<remove_cvref_t<RT>, String>) {
1236 if (auto lhsAsciiSpan = lhs.template PeekData<ASCII> ()) {
1237 if (auto rhsAsciiSpan = rhs.template PeekData<ASCII> ()) {
1238 return Character::Compare (*lhsAsciiSpan, *rhsAsciiSpan, fCompareOptions);
1239 }
1240 }
1241 }
1242 return Cmp_Generic_ (forward<LT> (lhs), forward<RT> (rhs));
1243 }
1244 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1245 strong_ordering String::ThreeWayComparer::Cmp_Generic_ (LT&& lhs, RT&& rhs) const
1246 {
1247 // separate function - cuz large stackframe and on windows generates chkstk calls, so dont have in
1248 // same frame where we do optimizations
1249 // 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
1250 Memory::StackBuffer<Character, 256> ignore1;
1251 Memory::StackBuffer<Character, 256> ignore2;
1252 return Character::Compare (Private_::AsSpanOfCharacters_ (forward<LT> (lhs), &ignore1),
1253 Private_::AsSpanOfCharacters_ (forward<RT> (rhs), &ignore2), fCompareOptions);
1254 }
1255 template <IConvertibleToString LT, IConvertibleToString RT>
1256 inline strong_ordering String::ThreeWayComparer::operator() (LT&& lhs, RT&& rhs) const
1257 {
1258 if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LT> and Private_::ICanBeTreatedAsSpanOfCharacter_<RT>) {
1259 return Cmp_ (forward<LT> (lhs), forward<RT> (rhs));
1260 }
1261 else {
1262 // should almost never happen, but if it does, fall back on using String
1263 return operator() (String{forward<LT> (lhs)}, String{forward<RT> (rhs)});
1264 }
1265 }
1266
1267 /*
1268 ********************************************************************************
1269 **************************** String::LessComparer ******************************
1270 ********************************************************************************
1271 */
1272 constexpr String::LessComparer::LessComparer (CompareOptions co)
1273 : fComparer_{co}
1274 {
1275 }
1276 template <typename T1, typename T2>
1277 inline bool String::LessComparer::operator() (T1 lhs, T2 rhs) const
1278 {
1279 return fComparer_ (lhs, rhs) < 0;
1280 }
1281
1282 /*
1283 ********************************************************************************
1284 *********************************** operator+ **********************************
1285 ********************************************************************************
1286 */
1287 template <IConvertibleToString LHS_T, IConvertibleToString RHS_T>
1288 inline String operator+ (LHS_T&& lhs, RHS_T&& rhs)
1289 requires (derived_from<remove_cvref_t<LHS_T>, String> or derived_from<remove_cvref_t<RHS_T>, String>)
1290 {
1291 if constexpr (derived_from<remove_cvref_t<LHS_T>, String>) {
1292 return lhs.Concatenate (forward<RHS_T> (rhs));
1293 }
1294#if 0
1295 else if constexpr (Private_::ICanBeTreatedAsSpanOfCharacter_<LHS_T> and Private_::ICanBeTreatedAsSpanOfCharacter_<RHS_T>) {
1296 // maybe always true?
1298 span<const Character> lSpan = Private_::AsSpanOfCharacters_ (forward<LHS_T> (lhs), &ignored1);
1300 span<const Character> rSpan = Private_::AsSpanOfCharacters_ (forward<RHS_T> (rhs), &ignored2);
1301 Memory::StackBuffer<Character, 512> buf{Memory::eUninitialized, lSpan.size () + rSpan.size ()};
1302 span bufSpan{buf};
1303 Memory::CopySpanData (lSpan, bufSpan);
1304 Memory::CopySpanData (rSpan, bufSpan.subspan (lSpan.size ()));
1305 return String{bufSpan};
1306 }
1307#endif
1308 else {
1309 return String{forward<LHS_T> (lhs)}.Concatenate (forward<RHS_T> (rhs));
1310 }
1311 }
1312
1313 inline const function<String (String, String, bool)> kDefaultStringCombiner = StringCombiner<String>{.fSeparator = ", "_k};
1314
1315#if qStroika_HasComponent_googletest
1316 inline void PrintTo (const String& s, std::ostream* os)
1317 {
1318 *os << s;
1319 }
1320#endif
1321
1322}
1323
1325
1326 // DEPRECATED
1327 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
1328 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
1329 DISABLE_COMPILER_MSC_WARNING_START (4996)
1330 template <typename CHAR_T>
1331 inline void String::Append (span<const CHAR_T> s)
1332 requires (same_as<CHAR_T, Character> or same_as<CHAR_T, char32_t>)
1333 {
1334 if (not s.empty ()) {
1335 Memory::StackBuffer<char32_t> ignored1;
1336 span<const char32_t> thisSpan = this->GetData (&ignored1);
1337 Memory::StackBuffer<char32_t> combinedBuf{Memory::eUninitialized, thisSpan.size () + s.size ()};
1338 Memory::CopySpanData (thisSpan, span{combinedBuf});
1339 char32_t* write2Buf = combinedBuf.data () + thisSpan.size ();
1340 for (auto i : s) {
1341 if constexpr (same_as<CHAR_T, Character>) {
1342 *write2Buf = i.template As<char32_t> ();
1343 }
1344 else {
1345 *write2Buf = i;
1346 }
1347 ++write2Buf;
1348 }
1349 *this = mk_ (span{combinedBuf});
1350 }
1351 }
1352 inline void String::Append (const wchar_t* from, const wchar_t* to)
1353 {
1354 Require (from <= to);
1355 if (from != to) {
1356 Memory::StackBuffer<wchar_t> ignored1;
1357 span<const wchar_t> thisSpan = this->GetData (&ignored1);
1358 Memory::StackBuffer<wchar_t> buf{Memory::eUninitialized, thisSpan.size () + (to - from)};
1359 span<wchar_t> bufSpan{buf};
1360 Memory::CopySpanData (thisSpan, bufSpan);
1361 Memory::CopySpanData (span{from, to}, bufSpan.subspan (thisSpan.size ()));
1362 *this = mk_ (bufSpan);
1363 }
1364 }
1365 inline void String::Append (Character c)
1366 {
1367 Append (&c, &c + 1);
1368 }
1369 inline void String::Append (const String& s)
1370 {
1371 Memory::StackBuffer<char32_t> ignored1;
1372 auto rhsSpan = s.GetData (&ignored1);
1373 Append (rhsSpan);
1374 }
1375 inline void String::Append (const wchar_t* s)
1376 {
1377 Append (s, s + ::wcslen (s));
1378 }
1379 inline void String::Append (const Character* from, const Character* to)
1380 {
1381 Append (span{from, to});
1382 }
1383 inline String& String::operator+= (Character appendage)
1384 {
1385 Append (appendage);
1386 return *this;
1387 }
1388 inline String& String::operator+= (const String& appendage)
1389 {
1390 Append (appendage);
1391 return *this;
1392 }
1393 inline String& String::operator+= (const wchar_t* appendageCStr)
1394 {
1395 Append (appendageCStr);
1396 return *this;
1397 }
1398
1399 inline void String::push_back (wchar_t c)
1400 {
1401 Append (Character (c));
1402 }
1403 inline void String::push_back (Character c)
1404 {
1405 Append (c);
1406 }
1407
1408 DISABLE_COMPILER_MSC_WARNING_END (4996)
1409 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
1410 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
1411
1412 [[deprecated ("Since Stroika v3.0d1 - just use _k, sv, or nothing")]] inline String operator"" _ASCII (const char* str, size_t len)
1413 {
1414 return String{span{str, len}};
1415 }
1416 class [[deprecated ("Since Stroika v3.0 - just use String::FromStringConstant")]] String_Constant : public String {
1417 public:
1418 template <size_t SIZE>
1419 explicit String_Constant (const wchar_t (&cString)[SIZE])
1420 : String{String::FromStringConstant (std::basic_string_view<wchar_t>{cString, SIZE - 1})}
1421 {
1422 }
1423
1424 String_Constant (const wchar_t* start, const wchar_t* end)
1425 : String{String::FromStringConstant (std::basic_string_view<wchar_t>{start, static_cast<size_t> (end - start)})}
1426 {
1427 }
1428
1429 String_Constant (const std::basic_string_view<wchar_t>& str)
1431 {
1432 }
1433 };
1434}
1435namespace Stroika::Foundation::Characters::Concrete {
1436 class [[deprecated ("Since Stroika v3.0 - just use String::FromStringConstant")]] String_ExternalMemoryOwnership_ApplicationLifetime : public String {
1437 public:
1438 template <size_t SIZE>
1439 explicit String_ExternalMemoryOwnership_ApplicationLifetime (const wchar_t (&cString)[SIZE - 1])
1440 : String{String::FromStringConstant (basic_string_view<wchar_t>{cString, SIZE})}
1441 {
1442 }
1443
1444 String_ExternalMemoryOwnership_ApplicationLifetime (const wchar_t* start, const wchar_t* end)
1445 : String{String::FromStringConstant (basic_string_view<wchar_t>{start, static_cast<size_t> (end - start)})}
1446 {
1447 }
1448
1449 String_ExternalMemoryOwnership_ApplicationLifetime (const basic_string_view<wchar_t>& str)
1451 {
1452 }
1453 };
1454}
1455
1457
1458 template <>
1459 String StringCombiner<String>::operator() (const String& lhs, const String& rhs, bool isLast) const;
1460
1461 template <typename STRING>
1462 STRING StringCombiner<STRING>::operator() (const STRING& lhs, const STRING& rhs, bool isLast) const
1463 {
1464 STRING sb{lhs};
1465 if (isLast and fSpecialSeparatorForLastPair) [[unlikely]] {
1466 sb = sb + *fSpecialSeparatorForLastPair;
1467 }
1468 else {
1469 sb = sb + fSeparator;
1470 }
1471 sb = sb + rhs;
1472 return sb;
1473 }
1474}
#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:693
nonvirtual size_t length() const noexcept
Definition String.inl:1045
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:711
nonvirtual bool operator==(const String &rhs) const
Definition String.inl:1109
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:741
nonvirtual string AsNarrowSDKString() const
Definition String.inl:830
nonvirtual optional< String > Col(size_t i) const
Useful to replace 'awk print $3' - replace with Col(2) - zero based.
Definition String.cpp:1362
nonvirtual String InsertAt(Character c, size_t at) const
Definition String.inl:715
nonvirtual size_t rfind(Character c) const
Definition String.inl:1069
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
nonvirtual string AsNarrowString(const locale &l) const
Definition String.cpp:1838
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:802
nonvirtual size_t size() const noexcept
Definition String.inl:534
static constexpr size_t npos
Definition String.h:1390
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:1099
nonvirtual Character back() const
Definition String.inl:1073
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:1080
nonvirtual optional< size_t > RFind(Character c) const noexcept
Definition String.cpp:1011
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:961
nonvirtual String RemoveAt(size_t charAt) const
Definition String.inl:604
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:735
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:681
nonvirtual String substr(size_t from, size_t count=npos) const
Definition String.inl:1086
nonvirtual size_t find(Character c, size_t startAt=0) const
Definition String.inl:1061
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....
static constexpr size_t ComputeTargetBufferSize(span< const FROM > src)
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...
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:300
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
wstring SDK2Wide(span< const SDKChar > 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:1288
const function< String(String, String, bool)> kDefaultStringCombiner
Definition String.inl:1313
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:1153
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:1261
StringCombiner is a simple function object used to combine two strings visually - used in Iterable<>:...
Definition String.h:1912