Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
StringBuilder.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
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::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
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
151 {
152 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
153 if constexpr (same_as<BufferElementType, char32_t>) {
154 if constexpr (same_as<CHAR_T, Character>) {
155 fData_.push_back (c.GetCharacterCode ());
156 }
157 else {
158 fData_.push_back (c);
159 }
160 return; // handled
161 }
162 else if constexpr (same_as<BufferElementType, char8_t>) {
163 if constexpr (same_as<CHAR_T, Character>) {
164 if (c.IsASCII ()) [[likely]] {
165 fData_.push_back (c.GetAsciiCode ());
166 return; // handled
167 }
168 }
169 else {
170 if (isascii (c)) [[likely]] {
171 fData_.push_back (static_cast<ASCII> (c));
172 return; // handled
173 }
174 }
175 }
176 // fall thru - handle
177 this->Append (span{&c, 1});
178 }
179#if !qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
180 template <typename OPTIONS>
181 template <typename APPEND_ARG_T>
182 inline auto StringBuilder<OPTIONS>::operator+= (APPEND_ARG_T&& a)
183 -> StringBuilder& requires (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
184 Append (forward<APPEND_ARG_T> (a));
185 return *this;
186 } template <typename OPTIONS>
187 template <typename APPEND_ARG_T>
188 inline auto StringBuilder<OPTIONS>::operator<< (APPEND_ARG_T&& a)
189 -> StringBuilder& requires (Characters::Private_::IToString<APPEND_ARG_T> or
190 requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
191 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
192 Append (forward<APPEND_ARG_T> (a));
193 }
194 else {
195 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
196 }
197 return *this;
198 }
199#endif
200 template <typename OPTIONS>
201 inline void StringBuilder<OPTIONS>::push_back (Character c)
202 {
203 Append (c);
204 }
205 template <typename OPTIONS>
206 inline size_t StringBuilder<OPTIONS>::size () const noexcept
207 {
208 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fAssertExternallySyncrhonized_};
209 if constexpr (sizeof (BufferElementType) == 4) {
210 return fData_.size ();
211 }
212 else {
213 return Memory::ValueOf (UTFConvert::kThe.ComputeCharacterLength<BufferElementType> (fData_));
214 }
215 }
216 template <typename OPTIONS>
217 inline bool StringBuilder<OPTIONS>::empty () const noexcept
218 {
219 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fAssertExternallySyncrhonized_};
220 return fData_.empty ();
221 }
222 template <typename OPTIONS>
223 inline Character StringBuilder<OPTIONS>::GetAt (size_t index) const noexcept
224 {
225 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fAssertExternallySyncrhonized_};
226 if constexpr (same_as<BufferElementType, char32_t>) {
227 Require (index < fData_.size ());
228 return fData_[index];
229 }
230 else {
231 // inefficient, but rarely used API
232 Memory::StackBuffer<char32_t> probablyIgnoredBuf;
233 span<const char32_t> sp = this->GetData (&probablyIgnoredBuf);
234 return sp[index];
235 }
236 }
237 template <typename OPTIONS>
238 inline void StringBuilder<OPTIONS>::SetAt (Character item, size_t index) noexcept
239 {
240 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
241 if constexpr (same_as<BufferElementType, char32_t>) {
242 Require (index < fData_.size ());
243 fData_[index] = item.GetCharacterCode ();
244 }
245 else {
246 // inefficient, but rarely used API
247 Memory::StackBuffer<char32_t> probablyIgnoredBuf;
248 span<const char32_t> sp = this->GetData (&probablyIgnoredBuf);
249 Require (index < sp.size ());
250 char32_t* p = const_cast<char32_t*> (sp.data ()) + index;
251 *p = item.GetCharacterCode ();
252 this->fData_.clear ();
253 this->Append (sp);
254 }
255 }
256 template <typename OPTIONS>
257 inline const Character StringBuilder<OPTIONS>::operator[] (size_t i) const noexcept
258 {
259 return GetAt (i);
260 }
261 template <typename OPTIONS>
262 template <Common::IAnyOf<char, Character, String, span<const Character>, span<Character>> T>
263 void StringBuilder<OPTIONS>::InsertAt (T c, size_t at)
264 {
265 // @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
266 // inefficient, but functional for now - implementation
267 String asStr = this->As<String> ();
268 *this = asStr.InsertAt (c, at);
269 }
270 template <typename OPTIONS>
271 inline void StringBuilder<OPTIONS>::ShrinkTo (size_t sz) noexcept
272 {
273 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
274 Require (sz <= this->size ());
275 fData_.resize (sz);
276 }
277 template <typename OPTIONS>
278 inline void StringBuilder<OPTIONS>::clear () noexcept
279 {
280 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_};
281 fData_.resize (0);
282 }
283 template <typename OPTIONS>
285 {
286 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fAssertExternallySyncrhonized_};
287 return String{span{fData_}};
288 }
289 template <typename OPTIONS>
290 template <Common::IAnyOf<String, wstring, u8string, u16string, u32string> RESULT_T>
291 inline RESULT_T StringBuilder<OPTIONS>::As () const
292 {
293 if constexpr (same_as<RESULT_T, String>) {
294 return str ();
295 }
296 if constexpr (same_as<RESULT_T, wstring>) {
297 Memory::StackBuffer<wchar_t> maybeIngored;
298 span<const wchar_t> p = this->GetData (&maybeIngored);
299 return wstring{p.data (), p.size ()};
300 }
301 if constexpr (same_as<RESULT_T, u8string>) {
302 Memory::StackBuffer<char8_t> maybeIngored;
303 span<const char8_t> p = this->GetData (&maybeIngored);
304 return u8string{p.data (), p.size ()};
305 }
306 if constexpr (same_as<RESULT_T, u16string>) {
307 Memory::StackBuffer<char16_t> maybeIngored;
308 span<const char16_t> p = this->GetData (&maybeIngored);
309 return u16string{p.data (), p.size ()};
310 }
311 if constexpr (same_as<RESULT_T, u32string>) {
312 Memory::StackBuffer<char32_t> maybeIngored;
313 span<const char32_t> p = this->GetData (&maybeIngored);
314 return u32string{p.data (), p.size ()};
315 }
316 }
317 template <typename OPTIONS>
318 inline StringBuilder<OPTIONS>::operator String () const
319 {
320 return As<String> ();
321 }
322 template <typename OPTIONS>
323 inline StringBuilder<OPTIONS>::operator wstring () const
324 {
325 return As<wstring> ();
326 }
327 template <typename OPTIONS>
328 inline StringBuilder<OPTIONS>::operator u8string () const
329 {
330 return As<u8string> ();
331 }
332 template <typename OPTIONS>
333 inline StringBuilder<OPTIONS>::operator u16string () const
334 {
335 return As<u16string> ();
336 }
337 template <typename OPTIONS>
338 inline StringBuilder<OPTIONS>::operator u32string () const
339 {
340 return As<u32string> ();
341 }
342 template <typename OPTIONS>
343 inline StringBuilder<OPTIONS>::operator span<const typename StringBuilder<OPTIONS>::BufferElementType> () const
344 {
345 return data ();
346 }
347 template <typename OPTIONS>
348 inline size_t StringBuilder<OPTIONS>::length () const noexcept
349 {
350 return size ();
351 }
352 template <typename OPTIONS>
353 inline auto StringBuilder<OPTIONS>::data () -> span<BufferElementType>
354 {
355 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fAssertExternallySyncrhonized_}; // doesn't do much good bit a little
356 return fData_.data ();
357 }
358 template <typename OPTIONS>
359 inline auto StringBuilder<OPTIONS>::data () const -> span<const BufferElementType>
360 {
361 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fAssertExternallySyncrhonized_}; // doesn't do much good bit a little
362 return fData_.data ();
363 }
364 template <typename OPTIONS>
365 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
366 inline span<const CHAR_T> StringBuilder<OPTIONS>::GetData (Memory::StackBuffer<CHAR_T>* probablyIgnoredBuf) const
367 requires (not is_const_v<CHAR_T>)
368 {
369 RequireNotNull (probablyIgnoredBuf); // required param even if not used
370 if constexpr (sizeof (CHAR_T) == sizeof (BufferElementType)) {
371 return span{reinterpret_cast<const CHAR_T*> (fData_.data ()), fData_.size ()};
372 }
373 else {
374 probablyIgnoredBuf->resize_uninitialized (UTFConvert::ComputeTargetBufferSize<CHAR_T> (span{fData_}));
375 return UTFConvert::kThe.ConvertSpan (span{fData_}, span{*probablyIgnoredBuf});
376 }
377 }
378
379 template <typename OPTIONS>
380 bool StringBuilder<OPTIONS>::operator== (const String& rhs) const
381 {
382 return As<String> () == rhs;
383 }
384 template <typename OPTIONS>
385 bool StringBuilder<OPTIONS>::operator== (const StringBuilder& rhs) const
386 {
387 return As<String> () == rhs.As<String> ();
388 }
389 template <typename OPTIONS>
390 void StringBuilder<OPTIONS>::erase (size_t from)
391 {
392 erase (from, size () - from);
393 }
394 template <typename OPTIONS>
395 void StringBuilder<OPTIONS>::erase (size_t from, size_t count)
396 {
397 //tmphack
398 String a = *this;
399 *this = a.RemoveAt (from, from + count);
400 }
401
402}
#define RequireNotNull(p)
Definition Assertions.h:347
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:715
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....
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
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