FastLED 3.10.8
Loading...
Searching...
No Matches
basic_string.cpp.hpp
Go to the documentation of this file.
2#include "fl/stl/cstring.h"
3#include "fl/stl/noexcept.h"
4#include "fl/stl/span.h"
6#include "fl/system/sketch_macros.h" // FL_PLATFORM_HAS_LARGE_MEMORY -- gates int64 itoa path
7
8namespace fl {
9
10// ODR-use definition
11const fl::size basic_string::npos;
12
13// ======= PUBLIC CONSTRUCTORS (span delegate) =======
14
15basic_string::basic_string(fl::span<char, static_cast<fl::size>(-1)> storage) FL_NO_EXCEPT
16 : basic_string(storage.data(), storage.size()) {}
17
18// ======= DESTRUCTOR =======
19
21
22// ======= string_view CONSTRUCTOR FROM basic_string =======
23// Defined here (in the same TU as basic_string itself) so
24// string_view.h can stay light: it forward-declares basic_string
25// and declares this ctor without needing basic_string's complete
26// type.
27
29 : mData(str.c_str()), mSize(str.size()) {}
30
31// ======= ACCESSORS =======
32
33const char* basic_string::c_str() const FL_NO_EXCEPT {
34 if (mStorage.is<ConstView>()) {
35 const ConstView& view = mStorage.get<ConstView>();
36 if (view.data && view.length > 0 && view.data[view.length] != '\0') {
37 const_cast<basic_string*>(this)->materialize();
38 }
39 }
40 return constData();
41}
42
44 // Inline mode — already mutable
45 if (mStorage.empty()) {
46 return inlineBufferPtr();
47 }
48 struct Visitor {
49 basic_string* self;
50 char* result;
51 void accept(NotNullStringHolderPtr& heap) FL_NO_EXCEPT {
52 if (heap.get().use_count() > 1) {
53 // COW: detach from shared data before returning mutable pointer
55 fl::make_shared<StringHolder>(heap->data(), self->mLength));
56 result = self->heapData()->data();
57 } else {
58 result = heap->data();
59 }
60 }
61 void accept(ConstLiteral&) FL_NO_EXCEPT {
62 self->materialize();
63 result = self->hasHeapData() ? self->heapData()->data()
64 : self->inlineBufferPtr();
65 }
66 void accept(ConstView&) FL_NO_EXCEPT {
67 self->materialize();
68 result = self->hasHeapData() ? self->heapData()->data()
69 : self->inlineBufferPtr();
70 }
71 };
72 Visitor v{this, nullptr};
73 mStorage.visit(v);
74 return v.result;
75}
76
78 if (hasHeapData()) {
79 return heapData()->capacity();
80 } else if (isNonOwning()) {
81 return 0;
82 }
83 return mInlineCapacity;
84}
85
86// ======= ELEMENT ACCESS =======
87
88char basic_string::operator[](fl::size index) const FL_NO_EXCEPT {
89 if (index >= mLength) {
90 return '\0';
91 }
92 return c_str()[index];
93}
94
96 static char dummy = '\0';
97 if (index >= mLength) {
98 dummy = '\0';
99 return dummy;
100 }
101 return c_str_mutable()[index];
102}
103
105 static char dummy = '\0';
106 if (pos >= mLength) {
107 dummy = '\0';
108 return dummy;
109 }
110 return c_str_mutable()[pos];
111}
112
113const char& basic_string::at(fl::size pos) const FL_NO_EXCEPT {
114 static char dummy = '\0';
115 if (pos >= mLength) {
116 dummy = '\0';
117 return dummy;
118 }
119 return c_str()[pos];
120}
121
123 if (empty()) return '\0';
124 return c_str()[0];
125}
126
128 if (empty()) return '\0';
129 return c_str()[mLength - 1];
130}
131
132char basic_string::charAt(fl::size index) const FL_NO_EXCEPT {
133 if (index >= mLength) return '\0';
134 return c_str()[index];
135}
136
137// ======= COMPARISON OPERATORS =======
138
139bool basic_string::operator==(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) == 0; }
140bool basic_string::operator!=(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) != 0; }
141bool basic_string::operator<(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) < 0; }
142bool basic_string::operator>(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) > 0; }
143bool basic_string::operator<=(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) <= 0; }
144bool basic_string::operator>=(const basic_string& other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other.c_str()) >= 0; }
145
146bool basic_string::operator==(const char* other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other ? other : "") == 0; }
147bool basic_string::operator!=(const char* other) const FL_NO_EXCEPT { return fl::strcmp(c_str(), other ? other : "") != 0; }
148
149// ======= HELPER METHODS =======
150
152 if (isInline()) {
153 return inlineBufferPtr();
154 } else if (mStorage.is<NotNullStringHolderPtr>()) {
155 return mStorage.get<NotNullStringHolderPtr>()->data();
156 } else if (mStorage.is<ConstLiteral>()) {
157 return mStorage.get<ConstLiteral>().data;
158 } else if (mStorage.is<ConstView>()) {
159 return mStorage.get<ConstView>().data;
160 }
161 return "";
162}
163
165 if (mStorage.is<ConstLiteral>()) {
166 const char* data = mStorage.get<ConstLiteral>().data;
167 if (!data) {
168 mLength = 0;
169 mStorage.reset();
170 inlineBufferPtr()[0] = '\0';
171 return;
172 }
173 fl::size len = mLength;
174 if (len + 1 <= mInlineCapacity) {
176 inlineBufferPtr()[len] = '\0';
177 mStorage.reset();
178 } else {
180 }
181 } else if (mStorage.is<ConstView>()) {
182 const ConstView& view = mStorage.get<ConstView>();
183 if (!view.data) {
184 mLength = 0;
185 mStorage.reset();
186 inlineBufferPtr()[0] = '\0';
187 return;
188 }
189 fl::size len = view.length;
190 mLength = len;
191 if (len + 1 <= mInlineCapacity) {
192 fl::memcpy(inlineBufferPtr(), view.data, len);
193 inlineBufferPtr()[len] = '\0';
194 mStorage.reset();
195 } else {
197 }
198 }
199}
200
207
211
212// ======= WRITE =======
213
214// Trivial delegates to the workhorse write(const char*, fl::size) below.
215// Folding away the address-of-byte indirection that write(u8) previously
216// did, and dropping the verbose static_cast<void*> intermediate from
217// write(u8*) — shrinks each call site by a few bytes (#3122 B5 /
218// #2886 Stage 5).
219fl::size basic_string::write(const fl::u8* data, fl::size n) FL_NO_EXCEPT {
221}
222
223fl::size basic_string::write(char c) FL_NO_EXCEPT { return write(&c, 1); }
226 return write(static_cast<char>(c));
227}
228
229fl::size basic_string::write(const char* str, fl::size n) FL_NO_EXCEPT {
230 fl::size newLen = mLength + n;
232 // Handle non-owning storage
233 if (isNonOwning()) {
234 const char* existingData = constData();
235 fl::size existingLen = mLength;
236 if (newLen + 1 <= mInlineCapacity) {
237 if (existingLen > 0 && existingData) {
238 fl::memcpy(inlineBufferPtr(), existingData, existingLen);
239 }
240 fl::memcpy(inlineBufferPtr() + existingLen, str, n);
241 inlineBufferPtr()[newLen] = '\0';
242 mStorage.reset();
243 mLength = newLen;
244 } else {
246 if (existingLen > 0 && existingData) {
247 fl::memcpy(newData->data(), existingData, existingLen);
248 }
249 fl::memcpy(newData->data() + existingLen, str, n);
250 newData->data()[newLen] = '\0';
251 mStorage = newData;
252 mLength = newLen;
253 }
254 return mLength;
255 }
256
257 if (hasHeapData() && heapData().get().use_count() <= 1) {
258 NotNullStringHolderPtr& heap = heapData();
259 if (!heap->hasCapacity(newLen)) {
260 // Check if str points into our buffer (self-referential write).
261 // grow() uses realloc which can relocate the buffer.
262 const char* bufStart = heap->data();
263 fl::size grow_length = fl::max(3, newLen * 3 / 2);
264 if (str >= bufStart && str < bufStart + mLength + 1) {
265 fl::size offset = static_cast<fl::size>(str - bufStart);
266 heap->grow(grow_length);
267 str = heap->data() + offset; // update to new location
268 } else {
269 heap->grow(grow_length);
270 }
271 }
272 fl::memcpy(heap->data() + mLength, str, n);
273 mLength = newLen;
274 heap->data()[mLength] = '\0';
275 return mLength;
276 } else if (hasHeapData()) {
277 // Copy-on-write
279 {
280 const NotNullStringHolderPtr& heap = heapData();
281 fl::memcpy(newData->data(), heap->data(), mLength);
282 fl::memcpy(newData->data() + mLength, str, n);
283 newData->data()[newLen] = '\0';
284 mStorage = newData;
285 mLength = newLen;
286 }
287 return mLength;
288 }
289 if (newLen + 1 <= mInlineCapacity) {
292 fl::memcpy(inlineBufferPtr() + mLength, str, n);
294 mLength = newLen;
295 inlineBufferPtr()[mLength] = '\0';
296 return mLength;
297 }
298 // Transition from inline to heap
300 {
301 fl::memcpy(newData->data(), inlineBufferPtr(), mLength);
302 fl::memcpy(newData->data() + mLength, str, n);
303 newData->data()[newLen] = '\0';
304 mStorage = newData;
305 mLength = newLen;
306 }
307 return mLength;
308}
309
310fl::size basic_string::write(const fl::u16& n) FL_NO_EXCEPT {
311 char buf[64] = {0};
312 int len = fl::utoa32(static_cast<fl::u32>(n), buf, 10);
313 return write(buf, len);
314}
315
316fl::size basic_string::write(const fl::u32& val) FL_NO_EXCEPT {
317 char buf[64] = {0};
318 int len = fl::utoa32(val, buf, 10);
319 return write(buf, len);
323 char buf[64] = {0};
324#if FL_PLATFORM_HAS_LARGE_MEMORY
325 int len = fl::utoa64(val, buf, 10);
326#else
327 // Low-memory gate per #3224 Tier 3G: route through the 32-bit utoa to
328 // avoid `__udivmoddi4` + `__clzdi2` (libgcc-nofp's 64-bit divmod cascade,
329 // ~1.5 KB on Cortex-M0+). Values larger than UINT32_MAX saturate.
330 int len = fl::utoa32(val > 0xFFFFFFFFull ? 0xFFFFFFFFu
331 : static_cast<fl::u32>(val),
332 buf, 10);
333#endif
334 return write(buf, len);
336
338 char buf[64] = {0};
339#if FL_PLATFORM_HAS_LARGE_MEMORY
340 int len = fl::itoa64(val, buf, 10);
341#else
342 // Low-memory gate per #3224 Tier 3G: route through the 32-bit itoa to
343 // avoid `__udivmoddi4` + `__clzdi2`. Values outside INT32 range saturate
344 // to INT32_MIN / INT32_MAX. Sketches that need full int64 formatting on
345 // Low-memory can opt in by defining FL_PLATFORM_HAS_LARGE_MEMORY=1.
346 fl::i32 narrow = val > 2147483647LL ? 2147483647
347 : val < -2147483648LL ? -2147483648
348 : static_cast<fl::i32>(val);
349 int len = fl::itoa(narrow, buf, 10);
350#endif
351 return write(buf, len);
352}
354fl::size basic_string::write(const fl::i32& val) FL_NO_EXCEPT {
355 char buf[64] = {0};
356 int len = fl::itoa(val, buf, 10);
357 return write(buf, len);
361 char buf[64] = {0};
362 int len = fl::itoa(static_cast<fl::i32>(val), buf, 10);
363 return write(buf, len);
366// ======= COPY =======
368void basic_string::copy(const char* str) FL_NO_EXCEPT {
369 fl::size len = fl::strlen(str);
370 mLength = len;
371 if (len + 1 <= mInlineCapacity) {
372 if (!isInline()) {
373 mStorage.reset();
374 }
375 fl::memcpy(inlineBufferPtr(), str, len + 1);
376 } else {
377 if (hasHeapData() && heapData().get().use_count() <= 1) {
378 heapData()->copy(str, len);
379 return;
380 }
382 }
383}
384
385void basic_string::copy(const char* str, fl::size len) FL_NO_EXCEPT {
386 mLength = len;
387 if (len + 1 <= mInlineCapacity) {
388 if (!isInline()) {
389 mStorage.reset();
390 }
391 fl::memcpy(inlineBufferPtr(), str, len);
392 inlineBufferPtr()[len] = '\0';
393 } else {
394 if (hasHeapData() && heapData().get().use_count() <= 1) {
395 heapData()->copy(str, len);
396 return;
397 }
399 }
400}
401
403 fl::size len = other.size();
404 if (other.hasHeapData()) {
405 // Share the heap pointer
406 const auto& otherHeap = other.mStorage.get<NotNullStringHolderPtr>();
407 mStorage = otherHeap;
408 } else if (len + 1 <= mInlineCapacity) {
409 if (!isInline()) {
410 mStorage.reset();
411 }
412 const char* src = other.c_str();
413 char* dst = inlineBufferPtr();
414 fl::memcpy(dst, src, len + 1);
415 } else {
417 }
418 mLength = len;
419}
420
421fl::size basic_string::copy(char* dest, fl::size count, fl::size pos) const FL_NO_EXCEPT {
422 if (!dest) return 0;
423 if (pos >= mLength) return 0;
424 fl::size actualCount = count;
425 if (actualCount > mLength - pos) {
426 actualCount = mLength - pos;
427 }
428 if (actualCount > 0) {
429 fl::memcpy(dest, c_str() + pos, actualCount);
430 }
431 return actualCount;
432}
433
434// ======= ASSIGN =======
435
436void basic_string::assign(const char* str, fl::size len) FL_NO_EXCEPT {
437 mLength = len;
438 if (len + 1 <= mInlineCapacity) {
439 if (!isInline()) {
440 mStorage.reset();
441 }
442 fl::memcpy(inlineBufferPtr(), str, len);
443 inlineBufferPtr()[len] = '\0';
444 } else {
446 }
447}
448
450 copy(str);
451 return *this;
452}
453
454basic_string& basic_string::assign(const basic_string& str, fl::size pos, fl::size count) FL_NO_EXCEPT {
455 if (pos >= str.size()) {
456 clear();
457 return *this;
458 }
459 fl::size actualCount = count;
460 if (actualCount == npos || pos + actualCount > str.size()) {
461 actualCount = str.size() - pos;
462 }
463 copy(str.c_str() + pos, actualCount);
464 return *this;
465}
466
468 if (count == 0) {
469 clear();
470 return *this;
471 }
472 mLength = count;
473 if (count + 1 <= mInlineCapacity) {
474 if (!isInline()) {
475 mStorage.reset();
476 }
477 for (fl::size i = 0; i < count; ++i) {
478 inlineBufferPtr()[i] = c;
479 }
480 inlineBufferPtr()[count] = '\0';
481 } else {
484 for (fl::size i = 0; i < count; ++i) {
485 ptr->data()[i] = c;
486 }
487 ptr->data()[count] = '\0';
488 }
489 return *this;
490}
491
496
497// ======= MEMORY MANAGEMENT =======
498
499void basic_string::reserve(fl::size newCapacity) FL_NO_EXCEPT {
500 if (newCapacity <= mLength) return;
501 if (newCapacity + 1 <= mInlineCapacity) return;
502 if (hasHeapData()) {
503 const NotNullStringHolderPtr& heap = heapData();
504 if (heap.get().use_count() <= 1 && heap->hasCapacity(newCapacity)) {
505 return;
506 }
507 }
509 fl::memcpy(newData->data(), c_str(), mLength);
510 newData->data()[mLength] = '\0';
511 mStorage = newData;
512}
513
514void basic_string::clear(bool freeMemory) FL_NO_EXCEPT {
515 mLength = 0;
516 if (isNonOwning() || (freeMemory && hasHeapData())) {
517 mStorage.reset();
518 inlineBufferPtr()[0] = '\0';
519 } else {
520 c_str_mutable()[0] = '\0';
521 }
522}
523
525 if (hasHeapData()) {
527 if (heap.get().use_count() > 1) return;
528 if (heap->capacity() <= mLength + 1) return;
529 if (mLength + 1 <= mInlineCapacity) {
530 fl::memcpy(inlineBufferPtr(), heap->data(), mLength + 1);
531 mStorage.reset();
532 return;
533 }
535 fl::memcpy(newData->data(), heap->data(), mLength + 1);
536 mStorage = newData;
537 }
538}
539
540// ======= FIND =======
541
542fl::size basic_string::find(const char& value) const FL_NO_EXCEPT {
543 for (fl::size i = 0; i < mLength; ++i) {
544 if (c_str()[i] == value) return i;
545 }
546 return npos;
547}
548
549fl::size basic_string::find(const char* substr) const FL_NO_EXCEPT {
550 if (!substr) return npos;
551 auto begin_ptr = c_str();
552 const char* found = fl::strstr(begin_ptr, substr);
553 if (found) return found - begin_ptr;
554 return npos;
555}
556
557fl::size basic_string::find(const char& value, fl::size start_pos) const FL_NO_EXCEPT {
558 if (start_pos >= mLength) return npos;
559 for (fl::size i = start_pos; i < mLength; ++i) {
560 if (c_str()[i] == value) return i;
561 }
562 return npos;
563}
564
565fl::size basic_string::find(const char* substr, fl::size start_pos) const FL_NO_EXCEPT {
566 if (!substr || start_pos >= mLength) return npos;
567 auto begin_ptr = c_str() + start_pos;
568 const char* found = fl::strstr(begin_ptr, substr);
569 if (found) return found - c_str();
570 return npos;
571}
572
573fl::size basic_string::find(const basic_string& other) const FL_NO_EXCEPT { return find(other.c_str()); }
574fl::size basic_string::find(const basic_string& other, fl::size start_pos) const FL_NO_EXCEPT { return find(other.c_str(), start_pos); }
575
576// ======= RFIND =======
577
578fl::size basic_string::rfind(char c, fl::size pos) const FL_NO_EXCEPT {
579 if (mLength == 0) return npos;
580 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
581 const char* str = c_str();
582 for (fl::size i = searchPos + 1; i > 0; --i) {
583 if (str[i - 1] == c) return i - 1;
584 }
585 return npos;
586}
587
588fl::size basic_string::rfind(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
589 if (!s || count == 0) {
590 if (count == 0) return (pos > mLength) ? mLength : pos;
591 return npos;
592 }
593 if (count > mLength) return npos;
594 fl::size maxStart = mLength - count;
595 fl::size searchStart = (pos >= mLength || pos == npos) ? maxStart : pos;
596 if (searchStart + count > mLength) searchStart = maxStart;
597 const char* str = c_str();
598 for (fl::size i = searchStart + 1; i > 0; --i) {
599 fl::size idx = i - 1;
600 if (idx + count > mLength) continue;
601 bool match = true;
602 for (fl::size j = 0; j < count; ++j) {
603 if (str[idx + j] != s[j]) { match = false; break; }
604 }
605 if (match) return idx;
606 }
607 return npos;
608}
609
610fl::size basic_string::rfind(const char* s, fl::size pos) const FL_NO_EXCEPT {
611 if (!s) return npos;
612 return rfind(s, pos, fl::strlen(s));
613}
614
615fl::size basic_string::rfind(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return rfind(str.c_str(), pos, str.size()); }
616
617// ======= FIND_FIRST_OF =======
618
619fl::size basic_string::find_first_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
620 if (!s || count == 0) return npos;
621 if (pos >= mLength) return npos;
622 const char* str = c_str();
623 for (fl::size i = pos; i < mLength; ++i) {
624 for (fl::size j = 0; j < count; ++j) {
625 if (str[i] == s[j]) return i;
626 }
627 }
628 return npos;
629}
630
631fl::size basic_string::find_first_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
632 if (!s) return npos;
633 return find_first_of(s, pos, fl::strlen(s));
634}
635
636fl::size basic_string::find_first_of(char c, fl::size pos) const FL_NO_EXCEPT { return find(c, pos); }
637fl::size basic_string::find_first_of(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return find_first_of(str.c_str(), pos, str.size()); }
638
639// ======= FIND_LAST_OF =======
640
641fl::size basic_string::find_last_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
642 if (!s || count == 0) return npos;
643 if (mLength == 0) return npos;
644 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
645 const char* str = c_str();
646 for (fl::size i = searchPos + 1; i > 0; --i) {
647 for (fl::size j = 0; j < count; ++j) {
648 if (str[i - 1] == s[j]) return i - 1;
649 }
650 }
651 return npos;
652}
653
654fl::size basic_string::find_last_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
655 if (!s) return npos;
656 return find_last_of(s, pos, fl::strlen(s));
657}
658
659fl::size basic_string::find_last_of(char c, fl::size pos) const FL_NO_EXCEPT { return rfind(c, pos); }
660fl::size basic_string::find_last_of(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return find_last_of(str.c_str(), pos, str.size()); }
661
662// ======= FIND_FIRST_NOT_OF =======
663
664fl::size basic_string::find_first_not_of(char c, fl::size pos) const FL_NO_EXCEPT {
665 if (pos >= mLength) return npos;
666 const char* str = c_str();
667 for (fl::size i = pos; i < mLength; ++i) {
668 if (str[i] != c) return i;
669 }
670 return npos;
671}
672
673fl::size basic_string::find_first_not_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
674 if (!s || count == 0) return (pos < mLength) ? pos : npos;
675 if (pos >= mLength) return npos;
676 const char* str = c_str();
677 for (fl::size i = pos; i < mLength; ++i) {
678 bool found_in_set = false;
679 for (fl::size j = 0; j < count; ++j) {
680 if (str[i] == s[j]) { found_in_set = true; break; }
681 }
682 if (!found_in_set) return i;
683 }
684 return npos;
685}
686
687fl::size basic_string::find_first_not_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
688 if (!s) return (pos < mLength) ? pos : npos;
689 return find_first_not_of(s, pos, fl::strlen(s));
690}
691
692fl::size basic_string::find_first_not_of(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return find_first_not_of(str.c_str(), pos, str.size()); }
693
694// ======= FIND_LAST_NOT_OF =======
695
696fl::size basic_string::find_last_not_of(char c, fl::size pos) const FL_NO_EXCEPT {
697 if (mLength == 0) return npos;
698 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
699 const char* str = c_str();
700 for (fl::size i = searchPos + 1; i > 0; --i) {
701 if (str[i - 1] != c) return i - 1;
702 }
703 return npos;
704}
705
706fl::size basic_string::find_last_not_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
707 if (!s || count == 0) {
708 if (mLength == 0) return npos;
709 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
710 return searchPos;
711 }
712 if (mLength == 0) return npos;
713 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
714 const char* str = c_str();
715 for (fl::size i = searchPos + 1; i > 0; --i) {
716 bool found_in_set = false;
717 for (fl::size j = 0; j < count; ++j) {
718 if (str[i - 1] == s[j]) { found_in_set = true; break; }
719 }
720 if (!found_in_set) return i - 1;
721 }
722 return npos;
723}
724
725fl::size basic_string::find_last_not_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
726 if (!s) {
727 if (mLength == 0) return npos;
728 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
729 return searchPos;
730 }
731 return find_last_not_of(s, pos, fl::strlen(s));
732}
733
734fl::size basic_string::find_last_not_of(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return find_last_not_of(str.c_str(), pos, str.size()); }
735
736// ======= CONTAINS =======
737
738bool basic_string::contains(const char* substr) const FL_NO_EXCEPT { return find(substr) != npos; }
739bool basic_string::contains(char c) const FL_NO_EXCEPT { return find(c) != npos; }
740bool basic_string::contains(const basic_string& other) const FL_NO_EXCEPT { return find(other.c_str()) != npos; }
741
742// ======= STARTS_WITH / ENDS_WITH =======
743
744bool basic_string::starts_with(const char* prefix) const FL_NO_EXCEPT {
745 if (!prefix) return true;
746 fl::size prefix_len = fl::strlen(prefix);
747 if (prefix_len > mLength) return false;
748 return fl::strncmp(c_str(), prefix, prefix_len) == 0;
749}
750
751bool basic_string::ends_with(const char* suffix) const FL_NO_EXCEPT {
752 if (!suffix) return true;
753 fl::size suffix_len = fl::strlen(suffix);
754 if (suffix_len > mLength) return false;
755 return fl::strncmp(c_str() + mLength - suffix_len, suffix, suffix_len) == 0;
756}
757
758bool basic_string::starts_with(char c) const FL_NO_EXCEPT { return mLength > 0 && c_str()[0] == c; }
759bool basic_string::starts_with(const basic_string& prefix) const FL_NO_EXCEPT { return starts_with(prefix.c_str()); }
760bool basic_string::ends_with(char c) const FL_NO_EXCEPT { return mLength > 0 && c_str()[mLength - 1] == c; }
761bool basic_string::ends_with(const basic_string& suffix) const FL_NO_EXCEPT { return ends_with(suffix.c_str()); }
762
763// ======= STACK OPERATIONS =======
764
767
768// ======= POP_BACK =======
769
771 if (mLength > 0) {
772 mLength--;
773 c_str_mutable()[mLength] = '\0';
774 }
775}
776
777// ======= INSERT =======
778
779basic_string& basic_string::insert(fl::size pos, fl::size count, char ch) FL_NO_EXCEPT {
780 if (pos > mLength) pos = mLength;
781 if (count == 0) return *this;
782
783 // Materialize non-owning storage before modifying
784 if (isNonOwning()) {
785 materialize();
786 }
787
788 fl::size newLen = mLength + count;
789
790 // Handle COW
791 if (hasHeapData() && heapData().get().use_count() > 1) {
792 const NotNullStringHolderPtr& heap = heapData();
794 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
795 for (fl::size i = 0; i < count; ++i) newData->data()[pos + i] = ch;
796 if (pos < mLength) fl::memcpy(newData->data() + pos + count, heap->data() + pos, mLength - pos);
797 newData->data()[newLen] = '\0';
798 mStorage = newData;
799 mLength = newLen;
800 return *this;
801 }
802
803 // Inline buffer
804 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
805 if (pos < mLength) {
807 }
808 for (fl::size i = 0; i < count; ++i) inlineBufferPtr()[pos + i] = ch;
809 mLength = newLen;
810 inlineBufferPtr()[mLength] = '\0';
811 return *this;
812 }
813
814 // Heap in-place or new allocation
815 bool canInsertInPlace = hasHeapData();
816 if (canInsertInPlace) {
818 canInsertInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
819 if (canInsertInPlace) {
820 char* data = heap->data();
821 if (pos < mLength) fl::memmove(data + pos + count, data + pos, mLength - pos);
822 for (fl::size i = 0; i < count; ++i) data[pos + i] = ch;
823 mLength = newLen;
824 data[mLength] = '\0';
825 }
826 }
827 if (!canInsertInPlace) {
829 const char* src = c_str();
830 if (pos > 0) fl::memcpy(newData->data(), src, pos);
831 for (fl::size i = 0; i < count; ++i) newData->data()[pos + i] = ch;
832 if (pos < mLength) fl::memcpy(newData->data() + pos + count, src + pos, mLength - pos);
833 newData->data()[newLen] = '\0';
834 mStorage = newData;
835 mLength = newLen;
836 }
837 return *this;
838}
839
841 if (!s) return *this;
842 return insert(pos, s, fl::strlen(s));
843}
844
846 return insert(pos, str.c_str(), str.size());
847}
848
849basic_string& basic_string::insert(fl::size pos, const basic_string& str, fl::size pos2, fl::size count) FL_NO_EXCEPT {
850 if (pos2 >= str.size()) return *this;
851 fl::size actualCount = count;
852 if (actualCount == npos || pos2 + actualCount > str.size()) {
853 actualCount = str.size() - pos2;
854 }
855 return insert(pos, str.c_str() + pos2, actualCount);
856}
857
858basic_string& basic_string::insert(fl::size pos, const char* s, fl::size count) FL_NO_EXCEPT {
859 if (pos > mLength) pos = mLength;
860 if (!s || count == 0) return *this;
861
862 // Materialize non-owning storage before modifying
863 if (isNonOwning()) {
864 materialize();
865 }
866
867 fl::size newLen = mLength + count;
868
869 // Handle COW
870 if (hasHeapData() && heapData().get().use_count() > 1) {
871 const NotNullStringHolderPtr& heap = heapData();
873 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
874 fl::memcpy(newData->data() + pos, s, count);
875 if (pos < mLength) fl::memcpy(newData->data() + pos + count, heap->data() + pos, mLength - pos);
876 newData->data()[newLen] = '\0';
877 mStorage = newData;
878 mLength = newLen;
879 return *this;
880 }
881
882 // Inline buffer
883 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
884 if (pos < mLength) {
886 }
887 fl::memcpy(inlineBufferPtr() + pos, s, count);
888 mLength = newLen;
889 inlineBufferPtr()[mLength] = '\0';
890 return *this;
891 }
892
893 // Heap in-place or new allocation
894 bool canInsertInPlace = hasHeapData();
895 if (canInsertInPlace) {
897 canInsertInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
898 if (canInsertInPlace) {
899 char* data = heap->data();
900 if (pos < mLength) fl::memmove(data + pos + count, data + pos, mLength - pos);
901 fl::memcpy(data + pos, s, count);
902 mLength = newLen;
903 data[mLength] = '\0';
904 }
905 }
906 if (!canInsertInPlace) {
908 const char* src = c_str();
909 if (pos > 0) fl::memcpy(newData->data(), src, pos);
910 fl::memcpy(newData->data() + pos, s, count);
911 if (pos < mLength) fl::memcpy(newData->data() + pos + count, src + pos, mLength - pos);
912 newData->data()[newLen] = '\0';
913 mStorage = newData;
914 mLength = newLen;
915 }
916 return *this;
917}
918
919// ======= ERASE =======
920
922 if (pos >= mLength) return *this;
923
924 fl::size actualCount = count;
925 if (actualCount == npos || pos + actualCount > mLength) {
926 actualCount = mLength - pos;
927 }
928 if (actualCount == 0) return *this;
929
930 // Handle COW
931 if (hasHeapData() && heapData().get().use_count() > 1) {
932 const NotNullStringHolderPtr& heap = heapData();
934 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
935 fl::size remainingLen = mLength - pos - actualCount;
936 if (remainingLen > 0) {
937 fl::memcpy(newData->data() + pos, heap->data() + pos + actualCount, remainingLen);
938 }
939 mLength = mLength - actualCount;
940 newData->data()[mLength] = '\0';
941 mStorage = newData;
942 return *this;
943 }
944
945 fl::size remainingLen = mLength - pos - actualCount;
946 if (remainingLen > 0) {
947 char* data = c_str_mutable();
948 fl::memmove(data + pos, data + pos + actualCount, remainingLen);
949 }
950 mLength -= actualCount;
951 c_str_mutable()[mLength] = '\0';
952 return *this;
953}
954
955// ======= REPLACE =======
956
957basic_string& basic_string::replace(fl::size pos, fl::size count, const basic_string& str) FL_NO_EXCEPT {
958 return replace(pos, count, str.c_str(), str.size());
959}
960
961basic_string& basic_string::replace(fl::size pos, fl::size count, const basic_string& str,
962 fl::size pos2, fl::size count2) FL_NO_EXCEPT {
963 if (pos2 >= str.size()) return erase(pos, count);
964 fl::size actualCount2 = count2;
965 if (actualCount2 == npos || pos2 + actualCount2 > str.size()) {
966 actualCount2 = str.size() - pos2;
967 }
968 return replace(pos, count, str.c_str() + pos2, actualCount2);
969}
970
971basic_string& basic_string::replace(fl::size pos, fl::size count, const char* s, fl::size count2) FL_NO_EXCEPT {
972 if (pos > mLength) return *this;
973 if (!s) return erase(pos, count);
974
975 // Materialize non-owning storage before modifying
976 if (isNonOwning()) {
977 materialize();
978 }
979
980 fl::size actualCount = count;
981 if (actualCount == npos || pos + actualCount > mLength) {
982 actualCount = mLength - pos;
983 }
984 fl::size newLen = mLength - actualCount + count2;
985
986 // Handle COW
987 if (hasHeapData() && heapData().get().use_count() > 1) {
988 const NotNullStringHolderPtr& heap = heapData();
990 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
991 fl::memcpy(newData->data() + pos, s, count2);
992 fl::size remainingLen = mLength - pos - actualCount;
993 if (remainingLen > 0) {
994 fl::memcpy(newData->data() + pos + count2, heap->data() + pos + actualCount, remainingLen);
995 }
996 newData->data()[newLen] = '\0';
997 mStorage = newData;
998 mLength = newLen;
999 return *this;
1000 }
1001
1002 // Inline buffer
1003 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
1004 if (count2 != actualCount) {
1005 fl::size remainingLen = mLength - pos - actualCount;
1006 if (remainingLen > 0) {
1007 fl::memmove(inlineBufferPtr() + pos + count2, inlineBufferPtr() + pos + actualCount, remainingLen);
1008 }
1009 }
1010 fl::memcpy(inlineBufferPtr() + pos, s, count2);
1011 mLength = newLen;
1012 inlineBufferPtr()[mLength] = '\0';
1013 return *this;
1014 }
1015
1016 // Heap in-place or new allocation
1017 bool canReplaceInPlace = hasHeapData();
1018 if (canReplaceInPlace) {
1020 canReplaceInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
1021 if (canReplaceInPlace) {
1022 char* data = heap->data();
1023 if (count2 != actualCount) {
1024 fl::size remainingLen = mLength - pos - actualCount;
1025 if (remainingLen > 0) {
1026 fl::memmove(data + pos + count2, data + pos + actualCount, remainingLen);
1027 }
1028 }
1029 fl::memcpy(data + pos, s, count2);
1030 mLength = newLen;
1031 data[mLength] = '\0';
1032 }
1033 }
1034 if (!canReplaceInPlace) {
1036 const char* src = c_str();
1037 if (pos > 0) fl::memcpy(newData->data(), src, pos);
1038 fl::memcpy(newData->data() + pos, s, count2);
1039 fl::size remainingLen = mLength - pos - actualCount;
1040 if (remainingLen > 0) {
1041 fl::memcpy(newData->data() + pos + count2, src + pos + actualCount, remainingLen);
1042 }
1043 newData->data()[newLen] = '\0';
1044 mStorage = newData;
1045 mLength = newLen;
1046 }
1047 return *this;
1048}
1049
1050basic_string& basic_string::replace(fl::size pos, fl::size count, const char* s) FL_NO_EXCEPT {
1051 if (!s) return erase(pos, count);
1052 return replace(pos, count, s, fl::strlen(s));
1053}
1054
1055basic_string& basic_string::replace(fl::size pos, fl::size count, fl::size count2, char ch) FL_NO_EXCEPT {
1056 if (pos > mLength) return *this;
1057
1058 // Materialize non-owning storage before modifying
1059 if (isNonOwning()) {
1060 materialize();
1061 }
1062
1063 fl::size actualCount = count;
1064 if (actualCount == npos || pos + actualCount > mLength) {
1065 actualCount = mLength - pos;
1066 }
1067 fl::size newLen = mLength - actualCount + count2;
1068
1069 // Handle COW
1070 if (hasHeapData() && heapData().get().use_count() > 1) {
1071 const NotNullStringHolderPtr& heap = heapData();
1073 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
1074 for (fl::size i = 0; i < count2; ++i) newData->data()[pos + i] = ch;
1075 fl::size remainingLen = mLength - pos - actualCount;
1076 if (remainingLen > 0) {
1077 fl::memcpy(newData->data() + pos + count2, heap->data() + pos + actualCount, remainingLen);
1078 }
1079 newData->data()[newLen] = '\0';
1080 mStorage = newData;
1081 mLength = newLen;
1082 return *this;
1083 }
1084
1085 // Inline buffer
1086 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
1087 if (count2 != actualCount) {
1088 fl::size remainingLen = mLength - pos - actualCount;
1089 if (remainingLen > 0) {
1090 fl::memmove(inlineBufferPtr() + pos + count2, inlineBufferPtr() + pos + actualCount, remainingLen);
1091 }
1092 }
1093 for (fl::size i = 0; i < count2; ++i) inlineBufferPtr()[pos + i] = ch;
1094 mLength = newLen;
1095 inlineBufferPtr()[mLength] = '\0';
1096 return *this;
1097 }
1098
1099 // Heap in-place or new allocation
1100 bool canReplaceInPlace = hasHeapData();
1101 if (canReplaceInPlace) {
1103 canReplaceInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
1104 if (canReplaceInPlace) {
1105 char* data = heap->data();
1106 if (count2 != actualCount) {
1107 fl::size remainingLen = mLength - pos - actualCount;
1108 if (remainingLen > 0) {
1109 fl::memmove(data + pos + count2, data + pos + actualCount, remainingLen);
1110 }
1111 }
1112 for (fl::size i = 0; i < count2; ++i) data[pos + i] = ch;
1113 mLength = newLen;
1114 data[mLength] = '\0';
1115 }
1116 }
1117 if (!canReplaceInPlace) {
1119 const char* src = c_str();
1120 if (pos > 0) fl::memcpy(newData->data(), src, pos);
1121 for (fl::size i = 0; i < count2; ++i) newData->data()[pos + i] = ch;
1122 fl::size remainingLen = mLength - pos - actualCount;
1123 if (remainingLen > 0) {
1124 fl::memcpy(newData->data() + pos + count2, src + pos + actualCount, remainingLen);
1125 }
1126 newData->data()[newLen] = '\0';
1127 mStorage = newData;
1128 mLength = newLen;
1129 }
1130 return *this;
1131}
1132
1133// ======= COMPARE =======
1134
1135int basic_string::compare(const basic_string& str) const FL_NO_EXCEPT { return fl::strcmp(c_str(), str.c_str()); }
1136
1137int basic_string::compare(fl::size pos1, fl::size count1, const basic_string& str) const FL_NO_EXCEPT {
1138 if (pos1 > mLength) {
1139 return str.empty() ? 0 : -1;
1140 }
1141 fl::size actualCount1 = count1;
1142 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1143 actualCount1 = mLength - pos1;
1144 }
1145 fl::size minLen = (actualCount1 < str.size()) ? actualCount1 : str.size();
1146 int result = fl::strncmp(c_str() + pos1, str.c_str(), minLen);
1147 if (result != 0) return result;
1148 if (actualCount1 < str.size()) return -1;
1149 if (actualCount1 > str.size()) return 1;
1150 return 0;
1151}
1152
1153int basic_string::compare(fl::size pos1, fl::size count1, const basic_string& str,
1154 fl::size pos2, fl::size count2) const FL_NO_EXCEPT {
1155 if (pos1 > mLength || pos2 >= str.size()) {
1156 if (pos1 > mLength && pos2 >= str.size()) return 0;
1157 if (pos1 > mLength) return -1;
1158 return 1;
1159 }
1160 fl::size actualCount1 = count1;
1161 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1162 actualCount1 = mLength - pos1;
1163 }
1164 fl::size actualCount2 = count2;
1165 if (actualCount2 == npos || pos2 + actualCount2 > str.size()) {
1166 actualCount2 = str.size() - pos2;
1167 }
1168 fl::size minLen = (actualCount1 < actualCount2) ? actualCount1 : actualCount2;
1169 int result = fl::strncmp(c_str() + pos1, str.c_str() + pos2, minLen);
1170 if (result != 0) return result;
1171 if (actualCount1 < actualCount2) return -1;
1172 if (actualCount1 > actualCount2) return 1;
1173 return 0;
1174}
1175
1176int basic_string::compare(const char* s) const FL_NO_EXCEPT {
1177 if (!s) return mLength > 0 ? 1 : 0;
1178 return fl::strcmp(c_str(), s);
1179}
1180
1181int basic_string::compare(fl::size pos1, fl::size count1, const char* s) const FL_NO_EXCEPT {
1182 if (!s) {
1183 if (pos1 >= mLength) return 0;
1184 fl::size actualCount1 = count1;
1185 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1186 actualCount1 = mLength - pos1;
1187 }
1188 return (actualCount1 > 0) ? 1 : 0;
1189 }
1190 if (pos1 > mLength) return (s[0] == '\0') ? 0 : -1;
1191 fl::size actualCount1 = count1;
1192 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1193 actualCount1 = mLength - pos1;
1194 }
1195 fl::size sLen = fl::strlen(s);
1196 fl::size minLen = (actualCount1 < sLen) ? actualCount1 : sLen;
1197 int result = fl::strncmp(c_str() + pos1, s, minLen);
1198 if (result != 0) return result;
1199 if (actualCount1 < sLen) return -1;
1200 if (actualCount1 > sLen) return 1;
1201 return 0;
1202}
1203
1204int basic_string::compare(fl::size pos1, fl::size count1, const char* s, fl::size count2) const FL_NO_EXCEPT {
1205 if (!s) {
1206 if (pos1 >= mLength) return (count2 == 0) ? 0 : -1;
1207 fl::size actualCount1 = count1;
1208 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1209 actualCount1 = mLength - pos1;
1210 }
1211 return (actualCount1 > 0) ? 1 : ((count2 == 0) ? 0 : -1);
1212 }
1213 if (pos1 > mLength) return (count2 == 0) ? 0 : -1;
1214 fl::size actualCount1 = count1;
1215 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1216 actualCount1 = mLength - pos1;
1217 }
1218 fl::size minLen = (actualCount1 < count2) ? actualCount1 : count2;
1219 int result = fl::strncmp(c_str() + pos1, s, minLen);
1220 if (result != 0) return result;
1221 if (actualCount1 < count2) return -1;
1222 if (actualCount1 > count2) return 1;
1223 return 0;
1224}
1225
1226// ======= PROTECTED: MOVE / SWAP / FACTORY HELPERS =======
1227
1229 if (other.isInline()) {
1230 mLength = other.mLength;
1231 fl::memcpy(inlineBufferPtr(), other.inlineBufferPtr(), other.mLength + 1);
1232 // mStorage is already empty (inline mode) from constructor
1233 } else {
1234 mLength = other.mLength;
1235 mStorage = fl::move(other.mStorage);
1236 }
1237 other.mLength = 0;
1238 other.mStorage.reset();
1239 other.inlineBufferPtr()[0] = '\0';
1240}
1241
1243 if (this == &other) return;
1244 if (other.isInline()) {
1245 mLength = other.mLength;
1246 mStorage.reset();
1247 fl::memcpy(inlineBufferPtr(), other.inlineBufferPtr(), other.mLength + 1);
1248 } else {
1249 mLength = other.mLength;
1250 mStorage = fl::move(other.mStorage);
1251 }
1252 other.mLength = 0;
1253 other.mStorage.reset();
1254 other.inlineBufferPtr()[0] = '\0';
1255}
1256
1258 if (this == &other) return;
1259
1260 bool thisInline = isInline();
1261 bool otherInline = other.isInline();
1262
1263 if (!thisInline && !otherInline) {
1264 // Both non-inline: swap variant + length
1265 fl::swap(mStorage, other.mStorage);
1266 fl::swap(mLength, other.mLength);
1267 } else if (thisInline && otherInline) {
1268 // Both inline: check capacity before swapping
1269 bool thisFits = other.mLength + 1 <= mInlineCapacity;
1270 bool otherFits = mLength + 1 <= other.mInlineCapacity;
1271 if (thisFits && otherFits) {
1272 // Both fit: swap buffer contents directly
1273 fl::size maxLen = fl::max(mLength, other.mLength);
1274 for (fl::size i = 0; i <= maxLen; ++i) {
1275 char tmp = inlineBufferPtr()[i];
1276 inlineBufferPtr()[i] = other.inlineBufferPtr()[i];
1277 other.inlineBufferPtr()[i] = tmp;
1278 }
1279 fl::swap(mLength, other.mLength);
1280 } else {
1281 // Capacity mismatch: promote to heap where needed
1282 NotNullStringHolderPtr thisData(
1284 NotNullStringHolderPtr otherData(
1285 fl::make_shared<StringHolder>(other.inlineBufferPtr(), other.mLength));
1286 fl::size thisLen = mLength;
1287 fl::size otherLen = other.mLength;
1288 if (thisFits) {
1289 mStorage.reset();
1290 fl::memcpy(inlineBufferPtr(), otherData->data(), otherLen + 1);
1291 } else {
1292 mStorage = otherData;
1293 }
1294 mLength = otherLen;
1295 if (otherFits) {
1296 other.mStorage.reset();
1297 fl::memcpy(other.inlineBufferPtr(), thisData->data(), thisLen + 1);
1298 } else {
1299 other.mStorage = thisData;
1300 }
1301 other.mLength = thisLen;
1302 }
1303 } else if (thisInline) {
1304 // this inline, other non-inline
1305 fl::size thisLen = mLength;
1306 // Take other's non-inline storage
1307 mStorage = fl::move(other.mStorage);
1308 mLength = other.mLength;
1309 // Put this's old inline data into other
1310 other.mStorage.reset();
1311 if (thisLen + 1 <= other.mInlineCapacity) {
1312 fl::memcpy(other.inlineBufferPtr(), inlineBufferPtr(), thisLen + 1);
1313 } else {
1314 other.mStorage = NotNullStringHolderPtr(
1316 }
1317 other.mLength = thisLen;
1318 } else {
1319 // this non-inline, other inline — reverse
1320 other.swapWith(*this);
1321 }
1322}
1323
1324void basic_string::setLiteral(const char* literal) FL_NO_EXCEPT {
1325 if (literal) {
1326 mLength = fl::strlen(literal);
1327 mStorage = ConstLiteral(literal);
1328 }
1329}
1330
1331void basic_string::setView(const char* data, fl::size len) FL_NO_EXCEPT {
1332 if (data && len > 0) {
1333 mLength = len;
1334 mStorage = ConstView(data, len);
1335 }
1336}
1337
1339 if (!holder || holder->length() == 0) return;
1340 mLength = holder->length();
1342}
1343
1344// ======= APPEND =======
1345
1347 write(str, fl::strlen(str));
1348 return *this;
1349}
1350
1351basic_string& basic_string::append(const char* str, fl::size len) FL_NO_EXCEPT {
1352 write(str, len);
1353 return *this;
1354}
1355
1357 write(&c, 1);
1358 return *this;
1359}
1360
1362 write(val);
1363 return *this;
1364}
1365
1367 write(static_cast<fl::u16>(val));
1368 return *this;
1369}
1370
1372 if (val) {
1373 write("true", 4);
1374 } else {
1375 write("false", 5);
1376 }
1377 return *this;
1378}
1379
1381 write(static_cast<fl::i32>(val));
1382 return *this;
1383}
1384
1386 write(val);
1387 return *this;
1388}
1389
1391 write(val);
1392 return *this;
1393}
1394
1396 write(val);
1397 return *this;
1398}
1399
1401 write(val);
1402 return *this;
1403}
1404
1406 write(val);
1407 return *this;
1408}
1409
1411 char buf[64] = {0};
1412 fl::ftoa(val, buf, 2);
1413 write(buf, fl::strlen(buf));
1414 return *this;
1415}
1416
1417basic_string& basic_string::append(const float& val, int precision) FL_NO_EXCEPT {
1418 char buf[64] = {0};
1419 fl::ftoa(val, buf, precision);
1420 write(buf, fl::strlen(buf));
1421 return *this;
1422}
1423
1425 return append(static_cast<float>(val));
1426}
1427
1429 write(str.c_str(), str.size());
1430 return *this;
1431}
1432
1433// ======= HEX/OCT APPEND =======
1434
1435basic_string& basic_string::appendHex(i32 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::itoa(val,b,16); write(b,l); return *this; }
1436basic_string& basic_string::appendHex(u32 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::utoa32(val,b,16); write(b,l); return *this; }
1437basic_string& basic_string::appendHex(i64 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::itoa64(val,b,16); write(b,l); return *this; }
1438basic_string& basic_string::appendHex(u64 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::utoa64(val,b,16); write(b,l); return *this; }
1439basic_string& basic_string::appendHex(i16 val) FL_NO_EXCEPT { return appendHex(static_cast<i32>(val)); }
1440basic_string& basic_string::appendHex(u16 val) FL_NO_EXCEPT { return appendHex(static_cast<u32>(val)); }
1441basic_string& basic_string::appendHex(i8 val) FL_NO_EXCEPT { return appendHex(static_cast<i32>(val)); }
1442basic_string& basic_string::appendHex(u8 val) FL_NO_EXCEPT { return appendHex(static_cast<u32>(val)); }
1443
1444basic_string& basic_string::appendOct(i32 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::itoa(val,b,8); write(b,l); return *this; }
1445basic_string& basic_string::appendOct(u32 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::utoa32(val,b,8); write(b,l); return *this; }
1446basic_string& basic_string::appendOct(i64 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::itoa64(val,b,8); write(b,l); return *this; }
1447basic_string& basic_string::appendOct(u64 val) FL_NO_EXCEPT { char b[64]={0}; int l=fl::utoa64(val,b,8); write(b,l); return *this; }
1448basic_string& basic_string::appendOct(i16 val) FL_NO_EXCEPT { return appendOct(static_cast<i32>(val)); }
1449basic_string& basic_string::appendOct(u16 val) FL_NO_EXCEPT { return appendOct(static_cast<u32>(val)); }
1450basic_string& basic_string::appendOct(i8 val) FL_NO_EXCEPT { return appendOct(static_cast<i32>(val)); }
1451basic_string& basic_string::appendOct(u8 val) FL_NO_EXCEPT { return appendOct(static_cast<u32>(val)); }
1452
1453// ======= OTHER =======
1454
1456
1457// ======= RESIZE =======
1458
1459void basic_string::resize(fl::size count) FL_NO_EXCEPT { resize(count, char()); }
1460
1461void basic_string::resize(fl::size count, char ch) FL_NO_EXCEPT {
1462 if (count < mLength) {
1463 mLength = count;
1464 c_str_mutable()[mLength] = '\0';
1465 } else if (count > mLength) {
1466 fl::size additional_chars = count - mLength;
1467 reserve(count);
1468 char* data_ptr = c_str_mutable();
1469 for (fl::size i = 0; i < additional_chars; ++i) {
1470 data_ptr[mLength + i] = ch;
1471 }
1472 mLength = count;
1473 data_ptr[mLength] = '\0';
1474 }
1475}
1476
1477} // namespace fl
uint8_t pos
Definition Blur.ino:11
Concrete type-erased string class operating on a caller- provided buffer (or fl::span<char>).
A fixed-size array implementation similar to std::array.
Definition array.h:27
int compare(const basic_string &str) const FL_NO_EXCEPT
const char * constData() const FL_NO_EXCEPT
char & at(fl::size pos) FL_NO_EXCEPT
bool isNonOwning() const FL_NO_EXCEPT
void materialize() FL_NO_EXCEPT
fl::size find_first_not_of(char c, fl::size pos=0) const FL_NO_EXCEPT
void setSharedHolder(const fl::shared_ptr< StringHolder > &holder) FL_NO_EXCEPT
void clear(bool freeMemory=false) FL_NO_EXCEPT
char * c_str_mutable() FL_NO_EXCEPT
void swapWith(basic_string &other) FL_NO_EXCEPT
bool starts_with(const char *prefix) const FL_NO_EXCEPT
bool operator<=(const basic_string &other) const FL_NO_EXCEPT
void moveFrom(basic_string &&other) FL_NO_EXCEPT
NotNullStringHolderPtr & heapData() FL_NO_EXCEPT
bool operator>=(const basic_string &other) const FL_NO_EXCEPT
char back() const FL_NO_EXCEPT
basic_string & replace(fl::size pos, fl::size count, const basic_string &str) FL_NO_EXCEPT
bool hasHeapData() const FL_NO_EXCEPT
void pop_back() FL_NO_EXCEPT
fl::size mInlineCapacity
bool ends_with(const char *suffix) const FL_NO_EXCEPT
basic_string & appendHex(i32 val) FL_NO_EXCEPT
basic_string & appendOct(i32 val) FL_NO_EXCEPT
float toFloat() const FL_NO_EXCEPT
static constexpr fl::size npos
fl::size find(const char &value) const FL_NO_EXCEPT
bool operator==(const basic_string &other) const FL_NO_EXCEPT
void push_back(char c) FL_NO_EXCEPT
void reserve(fl::size newCapacity) FL_NO_EXCEPT
basic_string(char *inlineBuffer, fl::size inlineCapacity) FL_NO_EXCEPT
fl::size rfind(char c, fl::size pos=npos) const FL_NO_EXCEPT
char operator[](fl::size index) const FL_NO_EXCEPT
char * inlineBufferPtr() FL_NO_EXCEPT
char charAt(fl::size index) const FL_NO_EXCEPT
void setView(const char *data, fl::size len) FL_NO_EXCEPT
fl::size find_first_of(char c, fl::size pos=0) const FL_NO_EXCEPT
bool isInline() const FL_NO_EXCEPT
fl::variant< NotNullStringHolderPtr, ConstLiteral, ConstView > mStorage
fl::size find_last_of(char c, fl::size pos=npos) const FL_NO_EXCEPT
bool operator!=(const basic_string &other) const FL_NO_EXCEPT
void setLiteral(const char *literal) FL_NO_EXCEPT
void push_ascii(char c) FL_NO_EXCEPT
bool empty() const FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
bool operator>(const basic_string &other) const FL_NO_EXCEPT
const char * data() const FL_NO_EXCEPT
char front() const FL_NO_EXCEPT
bool operator<(const basic_string &other) const FL_NO_EXCEPT
fl::size write(const fl::u8 *data, fl::size n) FL_NO_EXCEPT
void resize(fl::size count) FL_NO_EXCEPT
void shrink_to_fit() FL_NO_EXCEPT
fl::size find_last_not_of(char c, fl::size pos=npos) const FL_NO_EXCEPT
void moveAssign(basic_string &&other) FL_NO_EXCEPT
~basic_string() FL_DTOR_NOEXCEPT
basic_string & insert(fl::size pos, fl::size count, char ch) FL_NO_EXCEPT
void copy(const char *str) FL_NO_EXCEPT
bool contains(const char *substr) const FL_NO_EXCEPT
void assign(const char *str, fl::size len) FL_NO_EXCEPT
fl::size capacity() const FL_NO_EXCEPT
basic_string & erase(fl::size pos=0, fl::size count=npos) FL_NO_EXCEPT
basic_string & append(const char *str) FL_NO_EXCEPT
Concrete type-erased string class.
constexpr const T & get() const FL_NO_EXCEPT
Definition not_null.h:223
long use_count() const FL_NO_EXCEPT
Definition shared_ptr.h:350
const char * mData
constexpr string_view() FL_NO_EXCEPT
Definition string_view.h:39
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
void replace(Iterator first, Iterator last, const T &old_value, const T &new_value) FL_NO_EXCEPT
Definition algorithm.h:248
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
Definition move.h:30
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
Definition shared_ptr.h:423
Iterator find(Iterator first, Iterator last, const T &value) FL_NO_EXCEPT
Definition algorithm.h:215
unsigned char u8
Definition stdint.h:131
constexpr common_type_t< T, U > max(T a, U b) FL_NO_EXCEPT
Definition math.h:75
void swap(array< T, N > &lhs, array< T, N > &rhs) FL_NO_EXCEPT
Definition array.h:209
int utoa32(u32 value, char *sp, int radix) FL_NO_EXCEPT
Convert unsigned 32-bit integer to string buffer in given radix.
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
constexpr int type_rank< T >::value
fl::not_null< StringHolderPtr > NotNullStringHolderPtr
FASTLED_FORCE_INLINE int strcmp(const char *s1, const char *s2) FL_NO_EXCEPT
Definition cstring.h:60
void clear(CRGB(&arr)[N]) FL_NO_EXCEPT
Definition clear.h:14
FASTLED_FORCE_INLINE void * memmove(void *dest, const void *src, size_t n) FL_NO_EXCEPT
Definition cstring.h:144
int itoa64(i64 value, char *sp, int radix) FL_NO_EXCEPT
Convert signed 64-bit integer to string buffer in given radix.
FASTLED_FORCE_INLINE void * memcpy(void *dest, const void *src, size_t n) FL_NO_EXCEPT
Definition cstring.h:136
int itoa(i32 value, char *sp, int radix) FL_NO_EXCEPT
Convert signed 32-bit integer to string buffer in given radix.
FASTLED_FORCE_INLINE const char * strstr(const char *haystack, const char *needle) FL_NO_EXCEPT
Definition cstring.h:88
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
int utoa64(u64 value, char *sp, int radix) FL_NO_EXCEPT
Convert unsigned 64-bit integer to string buffer in given radix.
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NO_EXCEPT
Definition pair.h:119
float parseFloat(const char *str, fl::size len) FL_NO_EXCEPT
Parse a floating point number from a character buffer.
signed char i8
Definition stdint.h:130
void ftoa(float value, char *buffer, int precision) FL_NO_EXCEPT
Convert floating point number to string buffer.
To * bit_cast_ptr(void *storage) FL_NO_EXCEPT
Definition bit_cast.h:60
FASTLED_FORCE_INLINE int strncmp(const char *s1, const char *s2, size_t n) FL_NO_EXCEPT
Definition cstring.h:64
FASTLED_FORCE_INLINE size_t strlen(const char *s) FL_NO_EXCEPT
Definition cstring.h:56
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_DTOR_NOEXCEPT
fl::u64 u64
Definition stdint.h:220
fl::i64 i64
Definition stdint.h:221