Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
StringBuilder.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <memory>
5
9#include "Stroika/Foundation/Execution/Common.h"
10#include "Stroika/Foundation/Memory/Common.h"
12
14
15 /*
16 ********************************************************************************
17 ***************************** StringBuilder<OPTIONS> ***************************
18 ********************************************************************************
19 */
20 template <typename OPTIONS>
21 inline StringBuilder<OPTIONS>::StringBuilder (const String& initialValue)
22 {
23 Append (initialValue);
24 }
25 template <typename OPTIONS>
26 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
27 inline StringBuilder<OPTIONS>::StringBuilder (span<const CHAR_T> initialValue)
28 {
29 Append (initialValue);
30 }
31 template <typename OPTIONS>
32 template <convertible_to<String> T>
33 inline auto StringBuilder<OPTIONS>::operator= (const T& rhs) -> StringBuilder&
34 {
35 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
36 clear ();
37 Append (rhs);
38 return *this;
39 }
40 template <typename OPTIONS>
41 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
42 inline void StringBuilder<OPTIONS>::Append (span<const CHAR_T> s)
43 {
44 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
45 size_t spanSize = s.size ();
46 if (spanSize != 0) {
47 if constexpr (same_as<CHAR_T, ASCII>) {
49 }
50 if constexpr (same_as<CHAR_T, BufferElementType> or (same_as<BufferElementType, char8_t> and same_as<CHAR_T, ASCII>) or
51 (sizeof (CHAR_T) == sizeof (BufferElementType) and (same_as<CHAR_T, wchar_t> or same_as<BufferElementType, wchar_t>))) {
52 // easy case - just resize buffer, and copy data in
53 size_t i = fData_.size ();
54 fData_.GrowToSize_uninitialized (i + spanSize);
55 if constexpr (same_as<CHAR_T, BufferElementType>) {
56 Memory::CopyBytes (Memory::SpanBytesCast<span<const CHAR_T>> (s), span<CHAR_T>{fData_}.subspan (i));
57 }
58 else {
59 Memory::CopyBytes (Memory::SpanBytesCast<span<const BufferElementType>> (s), span<BufferElementType>{fData_}.subspan (i));
60 }
61 }
62 else {
63 //
64 // This case is more complicated. we must 'transcode' from one 'Unicode character type to another.
65 //
66 // But before falling back on the expensive UTFConvert call, first try some special cases.
67 // (1) src is ASCII - can just be copied in
68 // (2) NYI, but if src is char16_t (or equiv) and target is char32_t or equiv, can just look for if is surrogate or not.
69 //
70 // In both cases, walk source - and find first non-complaint character. Use quick algorithm for compliant ones
71 // and if needed, slow algorithm for the rest.
72 //
73 // For now, only the ASCII case is optimized --LGP 2023-09-12
74 //
75 auto charITodoHardWay = s.begin ();
76 for (; charITodoHardWay != s.end (); ++charITodoHardWay) {
77 // for now - KISS - and just check ASCII case
78 if (not Character::IsASCII (*charITodoHardWay)) [[unlikely]] {
79 break;
80 }
81 }
82 if (s.begin () != charITodoHardWay) [[likely]] {
83 if constexpr (same_as<CHAR_T, Character>) {
84 for (auto c : s.subspan (0, charITodoHardWay - s.begin ())) {
85 this->fData_.push_back (static_cast<ASCII> (c.GetCharacterCode ()));
86 }
87 }
88 else {
89 this->fData_.push_back_coerced (s.subspan (0, charITodoHardWay - s.begin ()));
90 }
91 }
92 if (charITodoHardWay != s.end ()) [[unlikely]] {
93 auto hardWaySpan = span{charITodoHardWay, s.end ()};
94 Memory::StackBuffer<BufferElementType> buf{Memory::eUninitialized,
95 UTFConvert::ComputeTargetBufferSize<BufferElementType> (hardWaySpan)};
96 Append (UTFConvert::kThe.ConvertSpan (hardWaySpan, span{buf}));
97 }
98 }
99 }
100 }
101 template <typename OPTIONS>
102 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
103 inline void StringBuilder<OPTIONS>::Append (span<CHAR_T> s)
104 {
105 Append (Memory::ConstSpan (s));
106 }
107 template <typename OPTIONS>
108 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
109 inline void StringBuilder<OPTIONS>::Append (const CHAR_T* s)
110 {
111 Append (span{s, CString::Length (s)});
112 }
113 template <typename OPTIONS>
114 template <IStdBasicStringCompatibleCharacter CHAR_T>
115 inline void StringBuilder<OPTIONS>::Append (const basic_string<CHAR_T>& s)
116 requires (IUNICODECanUnambiguouslyConvertFrom<CHAR_T>)
117 {
118 Append (span{s});
119 }
120 template <typename OPTIONS>
121 template <IStdBasicStringCompatibleCharacter CHAR_T>
122 inline void StringBuilder<OPTIONS>::Append (const basic_string_view<CHAR_T>& s)
123 requires (IUNICODECanUnambiguouslyConvertFrom<CHAR_T>)
124 {
125 if constexpr (same_as<CHAR_T, ASCII>) {
126 Require (Character::IsASCII (span{s})); // Optimization here is just avoiding Character::CheckASCII (s); in release builds
127 this->fData_.push_back_coerced (span{s});
128 }
129 else {
130 Append (span{s});
131 }
132 }
133 template <typename OPTIONS>
134 inline void StringBuilder<OPTIONS>::Append (const String& s)
135 {
136 Memory::StackBuffer<BufferElementType> ignored; // could use char8_t maybe here - optimizing for ASCII case or BufferElementType
137 span<const BufferElementType> p = s.GetData (&ignored);
138 if (not p.empty ()) {
139 Append (p);
140 }
141 }
142 template <typename OPTIONS>
143 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
144 inline
145#if defined(_MSC_VER)
146 // Makes significant difference in JSON parser runtime with vs2k 17.4.3
147 __forceinline
148#endif
149 void StringBuilder<OPTIONS>::Append (CHAR_T c)
150 {
151 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
152 if constexpr (same_as<BufferElementType, char32_t>) {
153 if constexpr (same_as<CHAR_T, Character>) {
154 fData_.push_back (c.GetCharacterCode ());
155 }
156 else {
157 fData_.push_back (c);
158 }
159 return; // handled
160 }
161 else if constexpr (same_as<BufferElementType, char8_t>) {
162 if constexpr (same_as<CHAR_T, Character>) {
163 if (c.IsASCII ()) [[likely]] {
164 fData_.push_back (c.GetAsciiCode ());
165 return; // handled
166 }
167 }
168 else {
169 if (isascii (c)) [[likely]] {
170 fData_.push_back (static_cast<ASCII> (c));
171 return; // handled
172 }
173 }
174 }
175 // fall thru - handle
176 this->Append (span{&c, 1});
177 }
178#if !qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
179 template <typename OPTIONS>
180 template <typename APPEND_ARG_T>
181 inline auto StringBuilder<OPTIONS>::operator+= (APPEND_ARG_T&& a) -> StringBuilder&
182 requires (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
183 {
184 Append (forward<APPEND_ARG_T> (a));
185 return *this;
186 }
187 template <typename OPTIONS>
188 template <typename APPEND_ARG_T>
189 inline auto StringBuilder<OPTIONS>::operator<< (APPEND_ARG_T&& a) -> StringBuilder&
190 requires (Characters::Private_::IToString<APPEND_ARG_T> or
191 requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
192 {
193 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
194 Append (forward<APPEND_ARG_T> (a));
195 }
196 else {
197 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
198 }
199 return *this;
200 }
201#endif
202 template <typename OPTIONS>
203 inline void StringBuilder<OPTIONS>::push_back (Character c)
204 {
205 Append (c);
206 }
207 template <typename OPTIONS>
208 inline size_t StringBuilder<OPTIONS>::size () const noexcept
209 {
210 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fAssertExternallySynchronized_};
211 if constexpr (sizeof (BufferElementType) == 4) {
212 return fData_.size ();
213 }
214 else {
215 return Memory::ValueOf (UTFConvert::kThe.ComputeCharacterLength<BufferElementType> (fData_));
216 }
217 }
218 template <typename OPTIONS>
219 inline bool StringBuilder<OPTIONS>::empty () const noexcept
220 {
221 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fAssertExternallySynchronized_};
222 return fData_.empty ();
223 }
224 template <typename OPTIONS>
225 inline Character StringBuilder<OPTIONS>::GetAt (size_t index) const noexcept
226 {
227 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fAssertExternallySynchronized_};
228 if constexpr (same_as<BufferElementType, char32_t>) {
229 Require (index < fData_.size ());
230 return fData_[index];
231 }
232 else {
233 // inefficient, but rarely used API
234 Memory::StackBuffer<char32_t> probablyIgnoredBuf;
235 span<const char32_t> sp = this->GetData (&probablyIgnoredBuf);
236 return sp[index];
237 }
238 }
239 template <typename OPTIONS>
240 inline void StringBuilder<OPTIONS>::SetAt (Character item, size_t index) noexcept
241 {
242 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
243 if constexpr (same_as<BufferElementType, char32_t>) {
244 Require (index < fData_.size ());
245 fData_[index] = item.GetCharacterCode ();
246 }
247 else {
248 // inefficient, but rarely used API
249 Memory::StackBuffer<char32_t> probablyIgnoredBuf;
250 span<const char32_t> sp = this->GetData (&probablyIgnoredBuf);
251 Require (index < sp.size ());
252 char32_t* p = const_cast<char32_t*> (sp.data ()) + index;
253 *p = item.GetCharacterCode ();
254 this->fData_.clear ();
255 this->Append (sp);
256 }
257 }
258 template <typename OPTIONS>
259 inline const Character StringBuilder<OPTIONS>::operator[] (size_t i) const noexcept
260 {
261 return GetAt (i);
262 }
263 template <typename OPTIONS>
264 template <Common::IAnyOf<char, Character, String, span<const Character>, span<Character>> T>
265 void StringBuilder<OPTIONS>::InsertAt (T c, size_t at)
266 {
267 // @todo easy todo efficient implementation (at least for more common cases, like T fits inside of buffer-char-type - like T = ascii or bufferchartype=char32_t
268 // inefficient, but functional for now - implementation
269 String asStr = this->As<String> ();
270 *this = asStr.InsertAt (c, at);
271 }
272 template <typename OPTIONS>
273 inline void StringBuilder<OPTIONS>::ShrinkTo (size_t sz) noexcept
274 {
275 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
276 Require (sz <= this->size ());
277 fData_.resize (sz);
278 }
279 template <typename OPTIONS>
280 inline void StringBuilder<OPTIONS>::clear () noexcept
281 {
282 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_};
283 fData_.resize (0);
284 }
285 template <typename OPTIONS>
287 {
288 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fAssertExternallySynchronized_};
289 return String{span{fData_}};
290 }
291 template <typename OPTIONS>
292 template <Common::IAnyOf<String, wstring, u8string, u16string, u32string> RESULT_T>
293 inline RESULT_T StringBuilder<OPTIONS>::As () const
294 {
295 if constexpr (same_as<RESULT_T, String>) {
296 return str ();
297 }
298 if constexpr (same_as<RESULT_T, wstring>) {
299 Memory::StackBuffer<wchar_t> maybeIngored;
300 span<const wchar_t> p = this->GetData (&maybeIngored);
301 return wstring{p.data (), p.size ()};
302 }
303 if constexpr (same_as<RESULT_T, u8string>) {
304 Memory::StackBuffer<char8_t> maybeIngored;
305 span<const char8_t> p = this->GetData (&maybeIngored);
306 return u8string{p.data (), p.size ()};
307 }
308 if constexpr (same_as<RESULT_T, u16string>) {
309 Memory::StackBuffer<char16_t> maybeIngored;
310 span<const char16_t> p = this->GetData (&maybeIngored);
311 return u16string{p.data (), p.size ()};
312 }
313 if constexpr (same_as<RESULT_T, u32string>) {
314 Memory::StackBuffer<char32_t> maybeIngored;
315 span<const char32_t> p = this->GetData (&maybeIngored);
316 return u32string{p.data (), p.size ()};
317 }
318 }
319 template <typename OPTIONS>
320 inline StringBuilder<OPTIONS>::operator String () const
321 {
322 return As<String> ();
323 }
324 template <typename OPTIONS>
325 inline StringBuilder<OPTIONS>::operator wstring () const
326 {
327 return As<wstring> ();
328 }
329 template <typename OPTIONS>
330 inline StringBuilder<OPTIONS>::operator u8string () const
331 {
332 return As<u8string> ();
333 }
334 template <typename OPTIONS>
335 inline StringBuilder<OPTIONS>::operator u16string () const
336 {
337 return As<u16string> ();
338 }
339 template <typename OPTIONS>
340 inline StringBuilder<OPTIONS>::operator u32string () const
341 {
342 return As<u32string> ();
343 }
344 template <typename OPTIONS>
345 inline StringBuilder<OPTIONS>::operator span<const typename StringBuilder<OPTIONS>::BufferElementType> () const
346 {
347 return data ();
348 }
349 template <typename OPTIONS>
350 inline size_t StringBuilder<OPTIONS>::length () const noexcept
351 {
352 return size ();
353 }
354 template <typename OPTIONS>
355 inline auto StringBuilder<OPTIONS>::data () -> span<BufferElementType>
356 {
357 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fAssertExternallySynchronized_}; // doesn't do much good bit a little
358 return fData_.data ();
359 }
360 template <typename OPTIONS>
361 inline auto StringBuilder<OPTIONS>::data () const -> span<const BufferElementType>
362 {
363 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fAssertExternallySynchronized_}; // doesn't do much good bit a little
364 return fData_.data ();
365 }
366 template <typename OPTIONS>
367 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
368 inline span<const CHAR_T> StringBuilder<OPTIONS>::GetData (Memory::StackBuffer<CHAR_T>* probablyIgnoredBuf) const
369 requires (not is_const_v<CHAR_T>)
370 {
371 RequireNotNull (probablyIgnoredBuf); // required param even if not used
372 if constexpr (sizeof (CHAR_T) == sizeof (BufferElementType)) {
373 return span{reinterpret_cast<const CHAR_T*> (fData_.data ()), fData_.size ()};
374 }
375 else {
376 probablyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_T> (span{fData_}));
377 return UTFConvert::kThe.ConvertSpan (span{fData_}, span{*probablyIgnoredBuf});
378 }
379 }
380
381 template <typename OPTIONS>
382 bool StringBuilder<OPTIONS>::operator== (const String& rhs) const
383 {
384 return As<String> () == rhs;
385 }
386 template <typename OPTIONS>
387 bool StringBuilder<OPTIONS>::operator== (const StringBuilder& rhs) const
388 {
389 return As<String> () == rhs.As<String> ();
390 }
391 template <typename OPTIONS>
392 void StringBuilder<OPTIONS>::erase (size_t from)
393 {
394 erase (from, size () - from);
395 }
396 template <typename OPTIONS>
397 void StringBuilder<OPTIONS>::erase (size_t from, size_t count)
398 {
399 //tmphack
400 String a = *this;
401 *this = a.RemoveAt (from, from + count);
402 }
403
404}
#define RequireNotNull(p)
Definition Assertions.h:348
constexpr bool IsASCII() const noexcept
Return true iff the given character (or all in span) is (are) in the ascii range [0....
static constexpr void CheckASCII(span< const CHAR_T > s)
if not IsASCII (arg) throw RuntimeException...
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual const Character operator[](size_t i) const noexcept
return (read-only) Character object
nonvirtual size_t size() const noexcept
nonvirtual span< BufferElementType > data()
nonvirtual auto operator+=(APPEND_ARG_T &&a) -> StringBuilder &
nonvirtual void ShrinkTo(size_t sz) noexcept
nonvirtual span< const CHAR_T > GetData(Memory::StackBuffer< CHAR_T > *probablyIgnoredBuf) const
access a span of data located inside the StringBuilder. Return internal pointer, or pointer internal ...
nonvirtual size_t length() const noexcept
number of characters, not bytes or code-points
nonvirtual auto operator<<(APPEND_ARG_T &&a) -> StringBuilder &
nonvirtual Character GetAt(size_t index) const noexcept
nonvirtual void Append(span< const CHAR_T > s)
nonvirtual void SetAt(Character item, size_t index) noexcept
nonvirtual void InsertAt(T c, size_t at)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String InsertAt(Character c, size_t at) const
Definition String.inl:721
static const UTFConvert kThe
Nearly always use this default UTFConvert.
Definition UTFConvert.h:369
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....
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
char ASCII
Stroika's string/character classes treat 'char' as being an ASCII character.
Definition Character.h:59
String UnoverloadedToString(const T &t)
same as ToString()/1 - but without the potentially confusing multi-arg overloads (confused some templ...
Definition ToString.inl:476