FastLED 3.10.9
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 (!newData->hasCapacity(newLen)) {
247 return mLength;
248 }
249 if (existingLen > 0 && existingData) {
250 fl::memcpy(newData->data(), existingData, existingLen);
251 }
252 fl::memcpy(newData->data() + existingLen, str, n);
253 newData->data()[newLen] = '\0';
254 mStorage = newData;
255 mLength = newLen;
256 }
257 return mLength;
258 }
259
260 if (hasHeapData() && heapData().get().use_count() <= 1) {
261 NotNullStringHolderPtr& heap = heapData();
262 if (!heap->hasCapacity(newLen)) {
263 // Check if str points into our buffer (self-referential write).
264 // grow() uses realloc which can relocate the buffer.
265 const char* bufStart = heap->data();
266 fl::size grow_length = fl::max(3, newLen * 3 / 2);
267 if (str >= bufStart && str < bufStart + mLength + 1) {
268 fl::size offset = static_cast<fl::size>(str - bufStart);
269 heap->grow(grow_length);
270 str = heap->data() + offset; // update to new location
271 } else {
272 heap->grow(grow_length);
273 }
274 // grow() preserves the old allocation on OOM. Do not append or
275 // write its terminator unless the requested bytes actually fit.
276 if (!heap->hasCapacity(newLen)) {
277 return mLength;
278 }
279 }
280 fl::memcpy(heap->data() + mLength, str, n);
281 mLength = newLen;
282 heap->data()[mLength] = '\0';
283 return mLength;
284 } else if (hasHeapData()) {
285 // Copy-on-write
287 if (!newData->hasCapacity(newLen)) {
288 return mLength;
289 }
290 {
291 const NotNullStringHolderPtr& heap = heapData();
292 fl::memcpy(newData->data(), heap->data(), mLength);
293 fl::memcpy(newData->data() + mLength, str, n);
294 newData->data()[newLen] = '\0';
295 mStorage = newData;
296 mLength = newLen;
297 }
298 return mLength;
299 }
300 if (newLen + 1 <= mInlineCapacity) {
303 fl::memcpy(inlineBufferPtr() + mLength, str, n);
305 mLength = newLen;
306 inlineBufferPtr()[mLength] = '\0';
307 return mLength;
308 }
309 // Transition from inline to heap
311 if (!newData->hasCapacity(newLen)) {
312 return mLength;
313 }
314 {
315 fl::memcpy(newData->data(), inlineBufferPtr(), mLength);
316 fl::memcpy(newData->data() + mLength, str, n);
317 newData->data()[newLen] = '\0';
318 mStorage = newData;
319 mLength = newLen;
321 return mLength;
324fl::size basic_string::write(const fl::u16& n) FL_NO_EXCEPT {
325 char buf[64] = {0};
326 int len = fl::utoa32(static_cast<fl::u32>(n), buf, 10);
327 return write(buf, len);
330fl::size basic_string::write(const fl::u32& val) FL_NO_EXCEPT {
331 char buf[64] = {0};
332 int len = fl::utoa32(val, buf, 10);
333 return write(buf, len);
337 char buf[64] = {0};
338#if FL_PLATFORM_HAS_LARGE_MEMORY
339 int len = fl::utoa64(val, buf, 10);
340#else
341 // Low-memory gate per #3224 Tier 3G: route through the 32-bit utoa to
342 // avoid `__udivmoddi4` + `__clzdi2` (libgcc-nofp's 64-bit divmod cascade,
343 // ~1.5 KB on Cortex-M0+). Values larger than UINT32_MAX saturate.
344 int len = fl::utoa32(val > 0xFFFFFFFFull ? 0xFFFFFFFFu
345 : static_cast<fl::u32>(val),
346 buf, 10);
347#endif
348 return write(buf, len);
349}
350
352 char buf[64] = {0};
353#if FL_PLATFORM_HAS_LARGE_MEMORY
354 int len = fl::itoa64(val, buf, 10);
355#else
356 // Low-memory gate per #3224 Tier 3G: route through the 32-bit itoa to
357 // avoid `__udivmoddi4` + `__clzdi2`. Values outside INT32 range saturate
358 // to INT32_MIN / INT32_MAX. Sketches that need full int64 formatting on
359 // Low-memory can opt in by defining FL_PLATFORM_HAS_LARGE_MEMORY=1.
360 fl::i32 narrow = val > 2147483647LL ? 2147483647
361 : val < -2147483648LL ? -2147483648
362 : static_cast<fl::i32>(val);
363 int len = fl::itoa(narrow, buf, 10);
364#endif
365 return write(buf, len);
368fl::size basic_string::write(const fl::i32& val) FL_NO_EXCEPT {
369 char buf[64] = {0};
370 int len = fl::itoa(val, buf, 10);
371 return write(buf, len);
372}
373
375 char buf[64] = {0};
376 int len = fl::itoa(static_cast<fl::i32>(val), buf, 10);
377 return write(buf, len);
378}
379
380// ======= COPY =======
381
382void basic_string::copy(const char* str) FL_NO_EXCEPT {
383 fl::size len = fl::strlen(str);
384 mLength = len;
385 if (len + 1 <= mInlineCapacity) {
386 if (!isInline()) {
387 mStorage.reset();
388 }
389 fl::memcpy(inlineBufferPtr(), str, len + 1);
390 } else {
391 if (hasHeapData() && heapData().get().use_count() <= 1) {
392 heapData()->copy(str, len);
393 return;
394 }
396 }
397}
398
399void basic_string::copy(const char* str, fl::size len) FL_NO_EXCEPT {
400 mLength = len;
401 if (len + 1 <= mInlineCapacity) {
402 if (!isInline()) {
403 mStorage.reset();
404 }
405 fl::memcpy(inlineBufferPtr(), str, len);
406 inlineBufferPtr()[len] = '\0';
407 } else {
408 if (hasHeapData() && heapData().get().use_count() <= 1) {
409 heapData()->copy(str, len);
410 return;
411 }
413 }
414}
415
417 fl::size len = other.size();
418 if (other.hasHeapData()) {
419 // Share the heap pointer
420 const auto& otherHeap = other.mStorage.get<NotNullStringHolderPtr>();
421 mStorage = otherHeap;
422 } else if (len + 1 <= mInlineCapacity) {
423 if (!isInline()) {
424 mStorage.reset();
425 }
426 const char* src = other.c_str();
427 char* dst = inlineBufferPtr();
428 fl::memcpy(dst, src, len + 1);
429 } else {
431 }
432 mLength = len;
433}
434
435fl::size basic_string::copy(char* dest, fl::size count, fl::size pos) const FL_NO_EXCEPT {
436 if (!dest) return 0;
437 if (pos >= mLength) return 0;
438 fl::size actualCount = count;
439 if (actualCount > mLength - pos) {
440 actualCount = mLength - pos;
441 }
442 if (actualCount > 0) {
443 fl::memcpy(dest, c_str() + pos, actualCount);
444 }
445 return actualCount;
446}
447
448// ======= ASSIGN =======
449
450void basic_string::assign(const char* str, fl::size len) FL_NO_EXCEPT {
451 mLength = len;
452 if (len + 1 <= mInlineCapacity) {
453 if (!isInline()) {
454 mStorage.reset();
455 }
456 fl::memcpy(inlineBufferPtr(), str, len);
457 inlineBufferPtr()[len] = '\0';
458 } else {
460 }
461}
462
464 copy(str);
465 return *this;
466}
467
468basic_string& basic_string::assign(const basic_string& str, fl::size pos, fl::size count) FL_NO_EXCEPT {
469 if (pos >= str.size()) {
470 clear();
471 return *this;
472 }
473 fl::size actualCount = count;
474 if (actualCount == npos || pos + actualCount > str.size()) {
475 actualCount = str.size() - pos;
476 }
477 copy(str.c_str() + pos, actualCount);
478 return *this;
479}
480
482 if (count == 0) {
483 clear();
484 return *this;
485 }
486 mLength = count;
487 if (count + 1 <= mInlineCapacity) {
488 if (!isInline()) {
489 mStorage.reset();
490 }
491 for (fl::size i = 0; i < count; ++i) {
492 inlineBufferPtr()[i] = c;
493 }
494 inlineBufferPtr()[count] = '\0';
495 } else {
498 for (fl::size i = 0; i < count; ++i) {
499 ptr->data()[i] = c;
500 }
501 ptr->data()[count] = '\0';
502 }
503 return *this;
504}
505
510
511// ======= MEMORY MANAGEMENT =======
512
513void basic_string::reserve(fl::size newCapacity) FL_NO_EXCEPT {
514 if (newCapacity <= mLength) return;
515 if (newCapacity + 1 <= mInlineCapacity) return;
516 if (hasHeapData()) {
518 if (heap.get().use_count() <= 1) {
519 if (!heap->hasCapacity(newCapacity)) {
520 heap->grow(newCapacity);
521 // grow updates holder length to the allocation length, but
522 // reserve must retain the original string and its terminator.
523 if (heap->hasCapacity(mLength)) {
524 heap->data()[mLength] = '\0';
525 }
526 }
527 return;
528 }
529 }
531 if (!newData->hasCapacity(newCapacity)) {
532 return;
533 }
534 fl::memcpy(newData->data(), c_str(), mLength);
535 newData->data()[mLength] = '\0';
536 mStorage = newData;
537}
538
539void basic_string::clear(bool freeMemory) FL_NO_EXCEPT {
540 mLength = 0;
541 if (isNonOwning() || (freeMemory && hasHeapData())) {
542 mStorage.reset();
543 inlineBufferPtr()[0] = '\0';
544 } else {
545 c_str_mutable()[0] = '\0';
546 }
547}
548
550 if (hasHeapData()) {
552 if (heap.get().use_count() > 1) return;
553 if (heap->capacity() <= mLength + 1) return;
554 if (mLength + 1 <= mInlineCapacity) {
555 fl::memcpy(inlineBufferPtr(), heap->data(), mLength + 1);
556 mStorage.reset();
557 return;
558 }
560 fl::memcpy(newData->data(), heap->data(), mLength + 1);
561 mStorage = newData;
562 }
563}
564
565// ======= FIND =======
566
567fl::size basic_string::find(const char& value) const FL_NO_EXCEPT {
568 for (fl::size i = 0; i < mLength; ++i) {
569 if (c_str()[i] == value) return i;
570 }
571 return npos;
572}
573
574fl::size basic_string::find(const char* substr) const FL_NO_EXCEPT {
575 if (!substr) return npos;
576 auto begin_ptr = c_str();
577 const char* found = fl::strstr(begin_ptr, substr);
578 if (found) return found - begin_ptr;
579 return npos;
580}
581
582fl::size basic_string::find(const char& value, fl::size start_pos) const FL_NO_EXCEPT {
583 if (start_pos >= mLength) return npos;
584 for (fl::size i = start_pos; i < mLength; ++i) {
585 if (c_str()[i] == value) return i;
586 }
587 return npos;
588}
589
590fl::size basic_string::find(const char* substr, fl::size start_pos) const FL_NO_EXCEPT {
591 if (!substr || start_pos >= mLength) return npos;
592 auto begin_ptr = c_str() + start_pos;
593 const char* found = fl::strstr(begin_ptr, substr);
594 if (found) return found - c_str();
595 return npos;
596}
597
598fl::size basic_string::find(const basic_string& other) const FL_NO_EXCEPT { return find(other.c_str()); }
599fl::size basic_string::find(const basic_string& other, fl::size start_pos) const FL_NO_EXCEPT { return find(other.c_str(), start_pos); }
600
601// ======= RFIND =======
602
603fl::size basic_string::rfind(char c, fl::size pos) const FL_NO_EXCEPT {
604 if (mLength == 0) return npos;
605 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
606 const char* str = c_str();
607 for (fl::size i = searchPos + 1; i > 0; --i) {
608 if (str[i - 1] == c) return i - 1;
609 }
610 return npos;
611}
612
613fl::size basic_string::rfind(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
614 if (!s || count == 0) {
615 if (count == 0) return (pos > mLength) ? mLength : pos;
616 return npos;
617 }
618 if (count > mLength) return npos;
619 fl::size maxStart = mLength - count;
620 fl::size searchStart = (pos >= mLength || pos == npos) ? maxStart : pos;
621 if (searchStart + count > mLength) searchStart = maxStart;
622 const char* str = c_str();
623 for (fl::size i = searchStart + 1; i > 0; --i) {
624 fl::size idx = i - 1;
625 if (idx + count > mLength) continue;
626 bool match = true;
627 for (fl::size j = 0; j < count; ++j) {
628 if (str[idx + j] != s[j]) { match = false; break; }
629 }
630 if (match) return idx;
631 }
632 return npos;
633}
634
635fl::size basic_string::rfind(const char* s, fl::size pos) const FL_NO_EXCEPT {
636 if (!s) return npos;
637 return rfind(s, pos, fl::strlen(s));
638}
639
640fl::size basic_string::rfind(const basic_string& str, fl::size pos) const FL_NO_EXCEPT { return rfind(str.c_str(), pos, str.size()); }
641
642// ======= FIND_FIRST_OF =======
643
644fl::size basic_string::find_first_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
645 if (!s || count == 0) return npos;
646 if (pos >= mLength) return npos;
647 const char* str = c_str();
648 for (fl::size i = pos; i < mLength; ++i) {
649 for (fl::size j = 0; j < count; ++j) {
650 if (str[i] == s[j]) return i;
651 }
652 }
653 return npos;
654}
655
656fl::size basic_string::find_first_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
657 if (!s) return npos;
658 return find_first_of(s, pos, fl::strlen(s));
659}
660
661fl::size basic_string::find_first_of(char c, fl::size pos) const FL_NO_EXCEPT { return find(c, pos); }
662fl::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()); }
663
664// ======= FIND_LAST_OF =======
665
666fl::size basic_string::find_last_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
667 if (!s || count == 0) return npos;
668 if (mLength == 0) return npos;
669 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
670 const char* str = c_str();
671 for (fl::size i = searchPos + 1; i > 0; --i) {
672 for (fl::size j = 0; j < count; ++j) {
673 if (str[i - 1] == s[j]) return i - 1;
674 }
675 }
676 return npos;
677}
678
679fl::size basic_string::find_last_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
680 if (!s) return npos;
681 return find_last_of(s, pos, fl::strlen(s));
682}
683
684fl::size basic_string::find_last_of(char c, fl::size pos) const FL_NO_EXCEPT { return rfind(c, pos); }
685fl::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()); }
686
687// ======= FIND_FIRST_NOT_OF =======
688
689fl::size basic_string::find_first_not_of(char c, fl::size pos) const FL_NO_EXCEPT {
690 if (pos >= mLength) return npos;
691 const char* str = c_str();
692 for (fl::size i = pos; i < mLength; ++i) {
693 if (str[i] != c) return i;
694 }
695 return npos;
696}
697
698fl::size basic_string::find_first_not_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
699 if (!s || count == 0) return (pos < mLength) ? pos : npos;
700 if (pos >= mLength) return npos;
701 const char* str = c_str();
702 for (fl::size i = pos; i < mLength; ++i) {
703 bool found_in_set = false;
704 for (fl::size j = 0; j < count; ++j) {
705 if (str[i] == s[j]) { found_in_set = true; break; }
706 }
707 if (!found_in_set) return i;
708 }
709 return npos;
710}
711
712fl::size basic_string::find_first_not_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
713 if (!s) return (pos < mLength) ? pos : npos;
714 return find_first_not_of(s, pos, fl::strlen(s));
715}
716
717fl::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()); }
718
719// ======= FIND_LAST_NOT_OF =======
720
721fl::size basic_string::find_last_not_of(char c, fl::size pos) const FL_NO_EXCEPT {
722 if (mLength == 0) return npos;
723 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
724 const char* str = c_str();
725 for (fl::size i = searchPos + 1; i > 0; --i) {
726 if (str[i - 1] != c) return i - 1;
727 }
728 return npos;
729}
730
731fl::size basic_string::find_last_not_of(const char* s, fl::size pos, fl::size count) const FL_NO_EXCEPT {
732 if (!s || count == 0) {
733 if (mLength == 0) return npos;
734 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
735 return searchPos;
736 }
737 if (mLength == 0) return npos;
738 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
739 const char* str = c_str();
740 for (fl::size i = searchPos + 1; i > 0; --i) {
741 bool found_in_set = false;
742 for (fl::size j = 0; j < count; ++j) {
743 if (str[i - 1] == s[j]) { found_in_set = true; break; }
744 }
745 if (!found_in_set) return i - 1;
746 }
747 return npos;
748}
749
750fl::size basic_string::find_last_not_of(const char* s, fl::size pos) const FL_NO_EXCEPT {
751 if (!s) {
752 if (mLength == 0) return npos;
753 fl::size searchPos = (pos >= mLength || pos == npos) ? (mLength - 1) : pos;
754 return searchPos;
755 }
756 return find_last_not_of(s, pos, fl::strlen(s));
757}
758
759fl::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()); }
760
761// ======= CONTAINS =======
762
763bool basic_string::contains(const char* substr) const FL_NO_EXCEPT { return find(substr) != npos; }
764bool basic_string::contains(char c) const FL_NO_EXCEPT { return find(c) != npos; }
765bool basic_string::contains(const basic_string& other) const FL_NO_EXCEPT { return find(other.c_str()) != npos; }
766
767// ======= STARTS_WITH / ENDS_WITH =======
768
769bool basic_string::starts_with(const char* prefix) const FL_NO_EXCEPT {
770 if (!prefix) return true;
771 fl::size prefix_len = fl::strlen(prefix);
772 if (prefix_len > mLength) return false;
773 return fl::strncmp(c_str(), prefix, prefix_len) == 0;
774}
775
776bool basic_string::ends_with(const char* suffix) const FL_NO_EXCEPT {
777 if (!suffix) return true;
778 fl::size suffix_len = fl::strlen(suffix);
779 if (suffix_len > mLength) return false;
780 return fl::strncmp(c_str() + mLength - suffix_len, suffix, suffix_len) == 0;
781}
782
783bool basic_string::starts_with(char c) const FL_NO_EXCEPT { return mLength > 0 && c_str()[0] == c; }
784bool basic_string::starts_with(const basic_string& prefix) const FL_NO_EXCEPT { return starts_with(prefix.c_str()); }
785bool basic_string::ends_with(char c) const FL_NO_EXCEPT { return mLength > 0 && c_str()[mLength - 1] == c; }
786bool basic_string::ends_with(const basic_string& suffix) const FL_NO_EXCEPT { return ends_with(suffix.c_str()); }
787
788// ======= STACK OPERATIONS =======
789
792
793// ======= POP_BACK =======
794
796 if (mLength > 0) {
797 mLength--;
798 c_str_mutable()[mLength] = '\0';
799 }
800}
801
802// ======= INSERT =======
803
804basic_string& basic_string::insert(fl::size pos, fl::size count, char ch) FL_NO_EXCEPT {
805 if (pos > mLength) pos = mLength;
806 if (count == 0) return *this;
807
808 // Materialize non-owning storage before modifying
809 if (isNonOwning()) {
810 materialize();
811 }
812
813 fl::size newLen = mLength + count;
814
815 // Handle COW
816 if (hasHeapData() && heapData().get().use_count() > 1) {
817 const NotNullStringHolderPtr& heap = heapData();
819 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
820 for (fl::size i = 0; i < count; ++i) newData->data()[pos + i] = ch;
821 if (pos < mLength) fl::memcpy(newData->data() + pos + count, heap->data() + pos, mLength - pos);
822 newData->data()[newLen] = '\0';
823 mStorage = newData;
824 mLength = newLen;
825 return *this;
826 }
827
828 // Inline buffer
829 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
830 if (pos < mLength) {
832 }
833 for (fl::size i = 0; i < count; ++i) inlineBufferPtr()[pos + i] = ch;
834 mLength = newLen;
835 inlineBufferPtr()[mLength] = '\0';
836 return *this;
837 }
838
839 // Heap in-place or new allocation
840 bool canInsertInPlace = hasHeapData();
841 if (canInsertInPlace) {
843 canInsertInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
844 if (canInsertInPlace) {
845 char* data = heap->data();
846 if (pos < mLength) fl::memmove(data + pos + count, data + pos, mLength - pos);
847 for (fl::size i = 0; i < count; ++i) data[pos + i] = ch;
848 mLength = newLen;
849 data[mLength] = '\0';
850 }
851 }
852 if (!canInsertInPlace) {
854 const char* src = c_str();
855 if (pos > 0) fl::memcpy(newData->data(), src, pos);
856 for (fl::size i = 0; i < count; ++i) newData->data()[pos + i] = ch;
857 if (pos < mLength) fl::memcpy(newData->data() + pos + count, src + pos, mLength - pos);
858 newData->data()[newLen] = '\0';
859 mStorage = newData;
860 mLength = newLen;
861 }
862 return *this;
863}
864
866 if (!s) return *this;
867 return insert(pos, s, fl::strlen(s));
868}
869
871 return insert(pos, str.c_str(), str.size());
872}
873
874basic_string& basic_string::insert(fl::size pos, const basic_string& str, fl::size pos2, fl::size count) FL_NO_EXCEPT {
875 if (pos2 >= str.size()) return *this;
876 fl::size actualCount = count;
877 if (actualCount == npos || pos2 + actualCount > str.size()) {
878 actualCount = str.size() - pos2;
879 }
880 return insert(pos, str.c_str() + pos2, actualCount);
881}
882
883basic_string& basic_string::insert(fl::size pos, const char* s, fl::size count) FL_NO_EXCEPT {
884 if (pos > mLength) pos = mLength;
885 if (!s || count == 0) return *this;
886
887 // Materialize non-owning storage before modifying
888 if (isNonOwning()) {
889 materialize();
890 }
891
892 fl::size newLen = mLength + count;
893
894 // Handle COW
895 if (hasHeapData() && heapData().get().use_count() > 1) {
896 const NotNullStringHolderPtr& heap = heapData();
898 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
899 fl::memcpy(newData->data() + pos, s, count);
900 if (pos < mLength) fl::memcpy(newData->data() + pos + count, heap->data() + pos, mLength - pos);
901 newData->data()[newLen] = '\0';
902 mStorage = newData;
903 mLength = newLen;
904 return *this;
905 }
906
907 // Inline buffer
908 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
909 if (pos < mLength) {
911 }
912 fl::memcpy(inlineBufferPtr() + pos, s, count);
913 mLength = newLen;
914 inlineBufferPtr()[mLength] = '\0';
915 return *this;
916 }
917
918 // Heap in-place or new allocation
919 bool canInsertInPlace = hasHeapData();
920 if (canInsertInPlace) {
922 canInsertInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
923 if (canInsertInPlace) {
924 char* data = heap->data();
925 if (pos < mLength) fl::memmove(data + pos + count, data + pos, mLength - pos);
926 fl::memcpy(data + pos, s, count);
927 mLength = newLen;
928 data[mLength] = '\0';
929 }
930 }
931 if (!canInsertInPlace) {
933 const char* src = c_str();
934 if (pos > 0) fl::memcpy(newData->data(), src, pos);
935 fl::memcpy(newData->data() + pos, s, count);
936 if (pos < mLength) fl::memcpy(newData->data() + pos + count, src + pos, mLength - pos);
937 newData->data()[newLen] = '\0';
938 mStorage = newData;
939 mLength = newLen;
940 }
941 return *this;
942}
943
944// ======= ERASE =======
945
947 if (pos >= mLength) return *this;
948
949 fl::size actualCount = count;
950 if (actualCount == npos || pos + actualCount > mLength) {
951 actualCount = mLength - pos;
952 }
953 if (actualCount == 0) return *this;
954
955 // Handle COW
956 if (hasHeapData() && heapData().get().use_count() > 1) {
957 const NotNullStringHolderPtr& heap = heapData();
959 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
960 fl::size remainingLen = mLength - pos - actualCount;
961 if (remainingLen > 0) {
962 fl::memcpy(newData->data() + pos, heap->data() + pos + actualCount, remainingLen);
963 }
964 mLength = mLength - actualCount;
965 newData->data()[mLength] = '\0';
966 mStorage = newData;
967 return *this;
968 }
969
970 fl::size remainingLen = mLength - pos - actualCount;
971 if (remainingLen > 0) {
972 char* data = c_str_mutable();
973 fl::memmove(data + pos, data + pos + actualCount, remainingLen);
974 }
975 mLength -= actualCount;
976 c_str_mutable()[mLength] = '\0';
977 return *this;
978}
979
980// ======= REPLACE =======
981
982basic_string& basic_string::replace(fl::size pos, fl::size count, const basic_string& str) FL_NO_EXCEPT {
983 return replace(pos, count, str.c_str(), str.size());
984}
985
986basic_string& basic_string::replace(fl::size pos, fl::size count, const basic_string& str,
987 fl::size pos2, fl::size count2) FL_NO_EXCEPT {
988 if (pos2 >= str.size()) return erase(pos, count);
989 fl::size actualCount2 = count2;
990 if (actualCount2 == npos || pos2 + actualCount2 > str.size()) {
991 actualCount2 = str.size() - pos2;
992 }
993 return replace(pos, count, str.c_str() + pos2, actualCount2);
994}
995
996basic_string& basic_string::replace(fl::size pos, fl::size count, const char* s, fl::size count2) FL_NO_EXCEPT {
997 if (pos > mLength) return *this;
998 if (!s) return erase(pos, count);
999
1000 // Materialize non-owning storage before modifying
1001 if (isNonOwning()) {
1002 materialize();
1003 }
1004
1005 fl::size actualCount = count;
1006 if (actualCount == npos || pos + actualCount > mLength) {
1007 actualCount = mLength - pos;
1008 }
1009 fl::size newLen = mLength - actualCount + count2;
1010
1011 // Handle COW
1012 if (hasHeapData() && heapData().get().use_count() > 1) {
1013 const NotNullStringHolderPtr& heap = heapData();
1015 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
1016 fl::memcpy(newData->data() + pos, s, count2);
1017 fl::size remainingLen = mLength - pos - actualCount;
1018 if (remainingLen > 0) {
1019 fl::memcpy(newData->data() + pos + count2, heap->data() + pos + actualCount, remainingLen);
1020 }
1021 newData->data()[newLen] = '\0';
1022 mStorage = newData;
1023 mLength = newLen;
1024 return *this;
1025 }
1026
1027 // Inline buffer
1028 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
1029 if (count2 != actualCount) {
1030 fl::size remainingLen = mLength - pos - actualCount;
1031 if (remainingLen > 0) {
1032 fl::memmove(inlineBufferPtr() + pos + count2, inlineBufferPtr() + pos + actualCount, remainingLen);
1033 }
1034 }
1035 fl::memcpy(inlineBufferPtr() + pos, s, count2);
1036 mLength = newLen;
1037 inlineBufferPtr()[mLength] = '\0';
1038 return *this;
1039 }
1040
1041 // Heap in-place or new allocation
1042 bool canReplaceInPlace = hasHeapData();
1043 if (canReplaceInPlace) {
1045 canReplaceInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
1046 if (canReplaceInPlace) {
1047 char* data = heap->data();
1048 if (count2 != actualCount) {
1049 fl::size remainingLen = mLength - pos - actualCount;
1050 if (remainingLen > 0) {
1051 fl::memmove(data + pos + count2, data + pos + actualCount, remainingLen);
1052 }
1053 }
1054 fl::memcpy(data + pos, s, count2);
1055 mLength = newLen;
1056 data[mLength] = '\0';
1057 }
1058 }
1059 if (!canReplaceInPlace) {
1061 const char* src = c_str();
1062 if (pos > 0) fl::memcpy(newData->data(), src, pos);
1063 fl::memcpy(newData->data() + pos, s, count2);
1064 fl::size remainingLen = mLength - pos - actualCount;
1065 if (remainingLen > 0) {
1066 fl::memcpy(newData->data() + pos + count2, src + pos + actualCount, remainingLen);
1067 }
1068 newData->data()[newLen] = '\0';
1069 mStorage = newData;
1070 mLength = newLen;
1071 }
1072 return *this;
1073}
1074
1075basic_string& basic_string::replace(fl::size pos, fl::size count, const char* s) FL_NO_EXCEPT {
1076 if (!s) return erase(pos, count);
1077 return replace(pos, count, s, fl::strlen(s));
1078}
1079
1080basic_string& basic_string::replace(fl::size pos, fl::size count, fl::size count2, char ch) FL_NO_EXCEPT {
1081 if (pos > mLength) return *this;
1082
1083 // Materialize non-owning storage before modifying
1084 if (isNonOwning()) {
1085 materialize();
1086 }
1087
1088 fl::size actualCount = count;
1089 if (actualCount == npos || pos + actualCount > mLength) {
1090 actualCount = mLength - pos;
1091 }
1092 fl::size newLen = mLength - actualCount + count2;
1093
1094 // Handle COW
1095 if (hasHeapData() && heapData().get().use_count() > 1) {
1096 const NotNullStringHolderPtr& heap = heapData();
1098 if (pos > 0) fl::memcpy(newData->data(), heap->data(), pos);
1099 for (fl::size i = 0; i < count2; ++i) newData->data()[pos + i] = ch;
1100 fl::size remainingLen = mLength - pos - actualCount;
1101 if (remainingLen > 0) {
1102 fl::memcpy(newData->data() + pos + count2, heap->data() + pos + actualCount, remainingLen);
1103 }
1104 newData->data()[newLen] = '\0';
1105 mStorage = newData;
1106 mLength = newLen;
1107 return *this;
1108 }
1109
1110 // Inline buffer
1111 if (newLen + 1 <= mInlineCapacity && !hasHeapData()) {
1112 if (count2 != actualCount) {
1113 fl::size remainingLen = mLength - pos - actualCount;
1114 if (remainingLen > 0) {
1115 fl::memmove(inlineBufferPtr() + pos + count2, inlineBufferPtr() + pos + actualCount, remainingLen);
1116 }
1117 }
1118 for (fl::size i = 0; i < count2; ++i) inlineBufferPtr()[pos + i] = ch;
1119 mLength = newLen;
1120 inlineBufferPtr()[mLength] = '\0';
1121 return *this;
1122 }
1123
1124 // Heap in-place or new allocation
1125 bool canReplaceInPlace = hasHeapData();
1126 if (canReplaceInPlace) {
1128 canReplaceInPlace = heap.get().use_count() <= 1 && heap->hasCapacity(newLen);
1129 if (canReplaceInPlace) {
1130 char* data = heap->data();
1131 if (count2 != actualCount) {
1132 fl::size remainingLen = mLength - pos - actualCount;
1133 if (remainingLen > 0) {
1134 fl::memmove(data + pos + count2, data + pos + actualCount, remainingLen);
1135 }
1136 }
1137 for (fl::size i = 0; i < count2; ++i) data[pos + i] = ch;
1138 mLength = newLen;
1139 data[mLength] = '\0';
1140 }
1141 }
1142 if (!canReplaceInPlace) {
1144 const char* src = c_str();
1145 if (pos > 0) fl::memcpy(newData->data(), src, pos);
1146 for (fl::size i = 0; i < count2; ++i) newData->data()[pos + i] = ch;
1147 fl::size remainingLen = mLength - pos - actualCount;
1148 if (remainingLen > 0) {
1149 fl::memcpy(newData->data() + pos + count2, src + pos + actualCount, remainingLen);
1150 }
1151 newData->data()[newLen] = '\0';
1152 mStorage = newData;
1153 mLength = newLen;
1154 }
1155 return *this;
1156}
1157
1158// ======= COMPARE =======
1159
1160int basic_string::compare(const basic_string& str) const FL_NO_EXCEPT { return fl::strcmp(c_str(), str.c_str()); }
1161
1162int basic_string::compare(fl::size pos1, fl::size count1, const basic_string& str) const FL_NO_EXCEPT {
1163 if (pos1 > mLength) {
1164 return str.empty() ? 0 : -1;
1165 }
1166 fl::size actualCount1 = count1;
1167 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1168 actualCount1 = mLength - pos1;
1169 }
1170 fl::size minLen = (actualCount1 < str.size()) ? actualCount1 : str.size();
1171 int result = fl::strncmp(c_str() + pos1, str.c_str(), minLen);
1172 if (result != 0) return result;
1173 if (actualCount1 < str.size()) return -1;
1174 if (actualCount1 > str.size()) return 1;
1175 return 0;
1176}
1177
1178int basic_string::compare(fl::size pos1, fl::size count1, const basic_string& str,
1179 fl::size pos2, fl::size count2) const FL_NO_EXCEPT {
1180 if (pos1 > mLength || pos2 >= str.size()) {
1181 if (pos1 > mLength && pos2 >= str.size()) return 0;
1182 if (pos1 > mLength) return -1;
1183 return 1;
1184 }
1185 fl::size actualCount1 = count1;
1186 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1187 actualCount1 = mLength - pos1;
1188 }
1189 fl::size actualCount2 = count2;
1190 if (actualCount2 == npos || pos2 + actualCount2 > str.size()) {
1191 actualCount2 = str.size() - pos2;
1192 }
1193 fl::size minLen = (actualCount1 < actualCount2) ? actualCount1 : actualCount2;
1194 int result = fl::strncmp(c_str() + pos1, str.c_str() + pos2, minLen);
1195 if (result != 0) return result;
1196 if (actualCount1 < actualCount2) return -1;
1197 if (actualCount1 > actualCount2) return 1;
1198 return 0;
1199}
1200
1201int basic_string::compare(const char* s) const FL_NO_EXCEPT {
1202 if (!s) return mLength > 0 ? 1 : 0;
1203 return fl::strcmp(c_str(), s);
1204}
1205
1206int basic_string::compare(fl::size pos1, fl::size count1, const char* s) const FL_NO_EXCEPT {
1207 if (!s) {
1208 if (pos1 >= mLength) return 0;
1209 fl::size actualCount1 = count1;
1210 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1211 actualCount1 = mLength - pos1;
1212 }
1213 return (actualCount1 > 0) ? 1 : 0;
1214 }
1215 if (pos1 > mLength) return (s[0] == '\0') ? 0 : -1;
1216 fl::size actualCount1 = count1;
1217 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1218 actualCount1 = mLength - pos1;
1219 }
1220 fl::size sLen = fl::strlen(s);
1221 fl::size minLen = (actualCount1 < sLen) ? actualCount1 : sLen;
1222 int result = fl::strncmp(c_str() + pos1, s, minLen);
1223 if (result != 0) return result;
1224 if (actualCount1 < sLen) return -1;
1225 if (actualCount1 > sLen) return 1;
1226 return 0;
1227}
1228
1229int basic_string::compare(fl::size pos1, fl::size count1, const char* s, fl::size count2) const FL_NO_EXCEPT {
1230 if (!s) {
1231 if (pos1 >= mLength) return (count2 == 0) ? 0 : -1;
1232 fl::size actualCount1 = count1;
1233 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1234 actualCount1 = mLength - pos1;
1235 }
1236 return (actualCount1 > 0) ? 1 : ((count2 == 0) ? 0 : -1);
1237 }
1238 if (pos1 > mLength) return (count2 == 0) ? 0 : -1;
1239 fl::size actualCount1 = count1;
1240 if (actualCount1 == npos || pos1 + actualCount1 > mLength) {
1241 actualCount1 = mLength - pos1;
1242 }
1243 fl::size minLen = (actualCount1 < count2) ? actualCount1 : count2;
1244 int result = fl::strncmp(c_str() + pos1, s, minLen);
1245 if (result != 0) return result;
1246 if (actualCount1 < count2) return -1;
1247 if (actualCount1 > count2) return 1;
1248 return 0;
1249}
1250
1251// ======= PROTECTED: MOVE / SWAP / FACTORY HELPERS =======
1252
1254 if (other.isInline()) {
1255 mLength = other.mLength;
1256 fl::memcpy(inlineBufferPtr(), other.inlineBufferPtr(), other.mLength + 1);
1257 // mStorage is already empty (inline mode) from constructor
1258 } else {
1259 mLength = other.mLength;
1260 mStorage = fl::move(other.mStorage);
1261 }
1262 other.mLength = 0;
1263 other.mStorage.reset();
1264 other.inlineBufferPtr()[0] = '\0';
1265}
1266
1268 if (this == &other) return;
1269 if (other.isInline()) {
1270 mLength = other.mLength;
1271 mStorage.reset();
1272 fl::memcpy(inlineBufferPtr(), other.inlineBufferPtr(), other.mLength + 1);
1273 } else {
1274 mLength = other.mLength;
1275 mStorage = fl::move(other.mStorage);
1276 }
1277 other.mLength = 0;
1278 other.mStorage.reset();
1279 other.inlineBufferPtr()[0] = '\0';
1280}
1281
1283 if (this == &other) return;
1284
1285 bool thisInline = isInline();
1286 bool otherInline = other.isInline();
1287
1288 if (!thisInline && !otherInline) {
1289 // Both non-inline: swap variant + length
1290 fl::swap(mStorage, other.mStorage);
1291 fl::swap(mLength, other.mLength);
1292 } else if (thisInline && otherInline) {
1293 // Both inline: check capacity before swapping
1294 bool thisFits = other.mLength + 1 <= mInlineCapacity;
1295 bool otherFits = mLength + 1 <= other.mInlineCapacity;
1296 if (thisFits && otherFits) {
1297 // Both fit: swap buffer contents directly
1298 fl::size maxLen = fl::max(mLength, other.mLength);
1299 for (fl::size i = 0; i <= maxLen; ++i) {
1300 char tmp = inlineBufferPtr()[i];
1301 inlineBufferPtr()[i] = other.inlineBufferPtr()[i];
1302 other.inlineBufferPtr()[i] = tmp;
1303 }
1304 fl::swap(mLength, other.mLength);
1305 } else {
1306 // Capacity mismatch: promote to heap where needed
1307 NotNullStringHolderPtr thisData(
1309 NotNullStringHolderPtr otherData(
1310 fl::make_shared<StringHolder>(other.inlineBufferPtr(), other.mLength));
1311 fl::size thisLen = mLength;
1312 fl::size otherLen = other.mLength;
1313 if (thisFits) {
1314 mStorage.reset();
1315 fl::memcpy(inlineBufferPtr(), otherData->data(), otherLen + 1);
1316 } else {
1317 mStorage = otherData;
1318 }
1319 mLength = otherLen;
1320 if (otherFits) {
1321 other.mStorage.reset();
1322 fl::memcpy(other.inlineBufferPtr(), thisData->data(), thisLen + 1);
1323 } else {
1324 other.mStorage = thisData;
1325 }
1326 other.mLength = thisLen;
1327 }
1328 } else if (thisInline) {
1329 // this inline, other non-inline
1330 fl::size thisLen = mLength;
1331 // Take other's non-inline storage
1332 mStorage = fl::move(other.mStorage);
1333 mLength = other.mLength;
1334 // Put this's old inline data into other
1335 other.mStorage.reset();
1336 if (thisLen + 1 <= other.mInlineCapacity) {
1337 fl::memcpy(other.inlineBufferPtr(), inlineBufferPtr(), thisLen + 1);
1338 } else {
1339 other.mStorage = NotNullStringHolderPtr(
1341 }
1342 other.mLength = thisLen;
1343 } else {
1344 // this non-inline, other inline — reverse
1345 other.swapWith(*this);
1346 }
1347}
1348
1349void basic_string::setLiteral(const char* literal) FL_NO_EXCEPT {
1350 if (literal) {
1351 mLength = fl::strlen(literal);
1352 mStorage = ConstLiteral(literal);
1353 }
1354}
1355
1356void basic_string::setView(const char* data, fl::size len) FL_NO_EXCEPT {
1357 if (data && len > 0) {
1358 mLength = len;
1359 mStorage = ConstView(data, len);
1360 }
1361}
1362
1364 if (!holder || holder->length() == 0) return;
1365 mLength = holder->length();
1367}
1368
1369// ======= APPEND =======
1370
1372 write(str, fl::strlen(str));
1373 return *this;
1374}
1375
1376basic_string& basic_string::append(const char* str, fl::size len) FL_NO_EXCEPT {
1377 write(str, len);
1378 return *this;
1379}
1380
1382 write(&c, 1);
1383 return *this;
1384}
1385
1387 write(val);
1388 return *this;
1389}
1390
1392 write(static_cast<fl::u16>(val));
1393 return *this;
1394}
1395
1397 if (val) {
1398 write("true", 4);
1399 } else {
1400 write("false", 5);
1401 }
1402 return *this;
1403}
1404
1406 write(static_cast<fl::i32>(val));
1407 return *this;
1408}
1409
1411 write(val);
1412 return *this;
1413}
1414
1416 write(val);
1417 return *this;
1418}
1419
1421 write(val);
1422 return *this;
1423}
1424
1426 write(val);
1427 return *this;
1428}
1429
1431 write(val);
1432 return *this;
1433}
1434
1436 char buf[64] = {0};
1437 fl::ftoa(val, buf, 2);
1438 write(buf, fl::strlen(buf));
1439 return *this;
1440}
1441
1442basic_string& basic_string::append(const float& val, int precision) FL_NO_EXCEPT {
1443 char buf[64] = {0};
1444 fl::ftoa(val, buf, precision);
1445 write(buf, fl::strlen(buf));
1446 return *this;
1447}
1448
1450 return append(static_cast<float>(val));
1451}
1452
1454 write(str.c_str(), str.size());
1455 return *this;
1456}
1457
1458// ======= HEX/OCT APPEND =======
1459
1460basic_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; }
1461basic_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; }
1462basic_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; }
1463basic_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; }
1464basic_string& basic_string::appendHex(i16 val) FL_NO_EXCEPT { return appendHex(static_cast<i32>(val)); }
1465basic_string& basic_string::appendHex(u16 val) FL_NO_EXCEPT { return appendHex(static_cast<u32>(val)); }
1466basic_string& basic_string::appendHex(i8 val) FL_NO_EXCEPT { return appendHex(static_cast<i32>(val)); }
1467basic_string& basic_string::appendHex(u8 val) FL_NO_EXCEPT { return appendHex(static_cast<u32>(val)); }
1468
1469basic_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; }
1470basic_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; }
1471basic_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; }
1472basic_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; }
1473basic_string& basic_string::appendOct(i16 val) FL_NO_EXCEPT { return appendOct(static_cast<i32>(val)); }
1474basic_string& basic_string::appendOct(u16 val) FL_NO_EXCEPT { return appendOct(static_cast<u32>(val)); }
1475basic_string& basic_string::appendOct(i8 val) FL_NO_EXCEPT { return appendOct(static_cast<i32>(val)); }
1476basic_string& basic_string::appendOct(u8 val) FL_NO_EXCEPT { return appendOct(static_cast<u32>(val)); }
1477
1478// ======= OTHER =======
1479
1481
1482// ======= RESIZE =======
1483
1484void basic_string::resize(fl::size count) FL_NO_EXCEPT { resize(count, char()); }
1485
1486void basic_string::resize(fl::size count, char ch) FL_NO_EXCEPT {
1487 if (count < mLength) {
1488 mLength = count;
1489 c_str_mutable()[mLength] = '\0';
1490 } else if (count > mLength) {
1491 fl::size additional_chars = count - mLength;
1492 reserve(count);
1493 char* data_ptr = c_str_mutable();
1494 // Materialize literal/view storage before checking its writable space.
1495 // A failed heap growth must leave the existing bytes and length intact.
1496 if (capacity() <= count) { return; }
1497 for (fl::size i = 0; i < additional_chars; ++i) {
1498 data_ptr[mLength + i] = ch;
1499 }
1500 mLength = count;
1501 data_ptr[mLength] = '\0';
1502 }
1503}
1504
1505} // 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
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
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
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
Definition s16x16x4.h:30
unsigned char u8
Definition s16x16x4.h:132
signed char i8
Definition s16x16x4.h:131
enable_if_t< is_same< T, U >::value > swap(T &a, U &b) FL_NO_EXCEPT
Definition s16x16x4.h:889
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
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
fl::i64 i64
Definition s16x16x4.h:222
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.
fl::u64 u64
Definition s16x16x4.h:221
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.
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_DTOR_NOEXCEPT
#define FL_DISABLE_WARNING_POP