1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef API_BASE_CONTAINERS_STRING_H
17 #define API_BASE_CONTAINERS_STRING_H
18
19 #include <cstddef>
20 #include <cstdint>
21
22 #include <base/containers/allocator.h>
23 #include <base/containers/iterator.h>
24 #include <base/containers/string_view.h>
25 #include <base/containers/type_traits.h>
26 #include <base/namespace.h>
27 #include <base/util/log.h>
28
29 BASE_BEGIN_NAMESPACE()
30 template<class CharT>
31 class basic_string;
32
33 using string = BASE_NS::basic_string<char>;
34 using wstring = BASE_NS::basic_string<wchar_t>;
35
36 template<class CharT>
37 class basic_string {
38 public:
39 using string_view = basic_string_view<CharT>;
40 using value_type = CharT;
41 using pointer = value_type*;
42 using const_pointer = const value_type*;
43 using reference = value_type&;
44 using const_reference = const value_type&;
45 using const_iterator = BASE_NS::const_iterator<basic_string>;
46 using iterator = BASE_NS::iterator<basic_string>;
47 using const_reverse_iterator = BASE_NS::reverse_iterator<const_iterator>;
48 using reverse_iterator = BASE_NS::reverse_iterator<iterator>;
49 using size_type = size_t;
50 using difference_type = ptrdiff_t;
51
52 static constexpr size_type npos = size_type(-1);
53
54 template<typename T>
55 using StringViewLikeNotCStr =
56 enable_if_t<is_convertible_v<const T&, string_view> && !is_convertible_v<const T&, const_pointer>>;
57
58 template<typename T>
59 using StringViewLike = enable_if_t<is_convertible_v<const T&, string_view>>;
60
basic_string()61 basic_string() noexcept : basic_string(default_allocator()) {}
62
~basic_string()63 ~basic_string()
64 {
65 if (!is_short()) {
66 allocator_.free(data_.longString.begin);
67 }
68 }
69
basic_string(allocator & alloc)70 explicit basic_string(allocator& alloc) noexcept : allocator_(alloc)
71 {
72 set_short(true);
73 data_.shortString.begin[1] = '\0';
74 data_.shortString.size = static_cast<value_type>(shortCapacity);
75 }
76
basic_string(const basic_string & str)77 basic_string(const basic_string& str) : basic_string(str, default_allocator()) {}
78
basic_string(const basic_string & str,allocator & alloc)79 basic_string(const basic_string& str, allocator& alloc) : allocator_(alloc)
80 {
81 if (str.data_.longString.isShort) {
82 data_.shortString = str.data_.shortString;
83 } else {
84 const auto len = str.data_.longString.size;
85 if (auto ptr = allocator_.alloc(len + 1); ptr) {
86 // destination and source are valid and the allocation sizes are for at least len characters.
87 CloneData(ptr, len * sizeof(value_type), str.data_.longString.begin, len * sizeof(value_type));
88 ptr[len] = '\0';
89
90 data_.longString = str.data_.longString;
91 data_.longString.capacity = len;
92 data_.longString.begin = ptr;
93 } else {
94 set_short(true);
95 data_.shortString.begin[1] = '\0';
96 data_.shortString.size = static_cast<value_type>(shortCapacity);
97 }
98 }
99 }
100
basic_string(basic_string && a)101 basic_string(basic_string&& a) noexcept : allocator_(a.allocator_), data_(BASE_NS::move(a.data_))
102 {
103 a.set_short(true);
104 a.data_.shortString.begin[1] = '\0';
105 a.data_.shortString.size = static_cast<value_type>(shortCapacity);
106 }
107
basic_string(const_pointer const str)108 basic_string(const_pointer const str) : basic_string(str, default_allocator()) {}
109
basic_string(const_pointer const str,allocator & alloc)110 basic_string(const_pointer const str, allocator& alloc) : basic_string(str, constexpr_strlen(str), alloc) {}
111
basic_string(const_pointer const str,size_type count)112 basic_string(const_pointer const str, size_type count) : basic_string(str, count, default_allocator()) {}
113
basic_string(const_pointer const str,size_type count,allocator & alloc)114 basic_string(const_pointer const str, size_type count, allocator& alloc) : allocator_(alloc)
115 {
116 construct(str, count);
117 }
118
basic_string(size_type count,const value_type a)119 basic_string(size_type count, const value_type a) : basic_string(count, a, default_allocator()) {}
120
basic_string(size_type count,const value_type a,allocator & alloc)121 basic_string(size_type count, const value_type a, allocator& alloc) : allocator_(alloc)
122 {
123 set_short(true);
124 data_.shortString.size = static_cast<value_type>(shortCapacity);
125 assign(count, a);
126 }
127
128 template<class StringT, class = StringViewLikeNotCStr<StringT>>
basic_string(const StringT & a)129 explicit basic_string(const StringT& a) : basic_string(a, default_allocator())
130 {}
131
132 template<class StringT, class = StringViewLikeNotCStr<StringT>>
basic_string(const StringT & a,allocator & alloc)133 explicit basic_string(const StringT& a, allocator& alloc) : allocator_(alloc)
134 {
135 if constexpr (is_same_v<StringT, string_view>) {
136 construct(a.data(), a.size());
137 } else {
138 const auto view = string_view(a);
139 construct(view.data(), view.size());
140 }
141 }
142
143 template<class StringT, class = StringViewLike<StringT>>
basic_string(const StringT & a,size_type pos,size_type n)144 basic_string(const StringT& a, size_type pos, size_type n) : basic_string(a, pos, n, default_allocator())
145 {}
146
147 template<class StringT, class = StringViewLike<StringT>>
basic_string(const StringT & a,size_type pos,size_type n,allocator & alloc)148 basic_string(const StringT& a, size_type pos, size_type n, allocator& alloc)
149 : basic_string(string_view(a).substr(pos, n), alloc)
150 {}
151
empty()152 bool empty() const noexcept
153 {
154 return !size();
155 }
156
clear()157 void clear()
158 {
159 assign("", 0);
160 }
161
back()162 value_type& back()
163 {
164 return *(data() + size() - 1);
165 }
166
back()167 const value_type& back() const
168 {
169 return *(data() + size() - 1);
170 }
171
front()172 value_type& front()
173 {
174 return *data();
175 }
176
front()177 const value_type& front() const
178 {
179 return *data();
180 }
181
capacity()182 size_type capacity() const noexcept
183 {
184 if (is_short()) {
185 return shortCapacity;
186 } else {
187 return data_.longString.capacity;
188 }
189 }
190
length()191 size_type length() const noexcept
192 {
193 return size();
194 }
195
size()196 size_type size() const noexcept
197 {
198 if (is_short()) {
199 return shortCapacity - data_.shortString.size;
200 } else {
201 return data_.longString.size;
202 }
203 }
204
205 basic_string& operator=(const basic_string& a)
206 {
207 if (&a != this) {
208 assign(a.data(), a.length());
209 }
210 return *this;
211 }
212
213 basic_string& operator=(basic_string&& a) noexcept
214 {
215 if (&a != this) {
216 if (!is_short()) {
217 allocator_.free(data_.longString.begin);
218 }
219 allocator_ = a.allocator_;
220 data_ = BASE_NS::move(a.data_);
221
222 a.set_short(true);
223 a.data_.shortString.begin[1] = '\0';
224 a.data_.shortString.size = static_cast<value_type>(shortCapacity);
225 }
226 return *this;
227 }
228
229 basic_string& operator=(const value_type ch)
230 {
231 auto ptr = data();
232 *ptr++ = ch;
233 *ptr++ = '\0';
234 set_size(1);
235 return *this;
236 }
237
238 basic_string& operator=(const_pointer const a)
239 {
240 if (data() != a) {
241 assign(a, constexpr_strlen(a));
242 }
243 return *this;
244 }
245
246 template<class StringT, class = StringViewLikeNotCStr<StringT>>
247 basic_string& operator=(const StringT& a)
248 {
249 const auto view = string_view(a);
250 if (data() != view.data()) {
251 assign(view.data(), view.length());
252 } else {
253 resize(view.length());
254 }
255 return *this;
256 }
257
assign(const_pointer const str)258 basic_string& assign(const_pointer const str)
259 {
260 const auto view = string_view(str);
261 return assign(view.data(), view.length());
262 }
263
assign(const_pointer const str,size_type count)264 basic_string& assign(const_pointer const str, size_type count)
265 {
266 if (count) {
267 reserve(count);
268 const pointer dst = data();
269 const size_type cap = capacity();
270 if (static_cast<size_type>((dst > str) ? (dst - str) : (str - dst)) >= count) {
271 CloneData(dst, cap * sizeof(value_type), str, count * sizeof(value_type));
272 } else {
273 MoveData(dst, cap * sizeof(value_type), str, count * sizeof(value_type));
274 }
275 dst[count] = '\0';
276 } else {
277 const pointer dst = data();
278 *dst = '\0';
279 }
280 set_size(count);
281 return *this;
282 }
283
assign(size_type count,value_type ch)284 basic_string& assign(size_type count, value_type ch)
285 {
286 if (count) {
287 reserve(count);
288 const pointer dst = data();
289 const size_type cap = capacity();
290 count = (count <= cap) ? count : cap;
291 // dst is valid, count fits capacity
292 ClearToValue(dst, cap * sizeof(value_type), static_cast<uint8_t>(ch), count * sizeof(value_type));
293 dst[count] = '\0';
294 set_size(count);
295 } else {
296 const pointer dst = data();
297 *dst = '\0';
298 set_size(0);
299 }
300 return *this;
301 }
302
cbegin()303 const_iterator cbegin() const noexcept
304 {
305 return begin();
306 }
307
cend()308 const_iterator cend() const noexcept
309 {
310 return end();
311 }
312
begin()313 iterator begin() noexcept
314 {
315 return iterator(data());
316 }
317
end()318 iterator end() noexcept
319 {
320 return iterator(data() + size());
321 }
322
begin()323 const_iterator begin() const noexcept
324 {
325 return const_iterator(data());
326 }
327
end()328 const_iterator end() const noexcept
329 {
330 return const_iterator(data() + size());
331 }
332
data()333 pointer data() noexcept
334 {
335 if (is_short()) {
336 return data_.shortString.begin + 1;
337 } else {
338 return data_.longString.begin;
339 }
340 }
341
data()342 const_pointer data() const noexcept
343 {
344 if (is_short()) {
345 return data_.shortString.begin + 1;
346 } else {
347 return data_.longString.begin;
348 }
349 }
350
c_str()351 const_pointer c_str() const
352 {
353 return data();
354 }
355
string_view()356 operator string_view() const noexcept
357 {
358 return string_view(data(), size());
359 }
360
copy(pointer dst,size_type todo)361 size_type copy(pointer dst, size_type todo) const
362 {
363 if (const auto len = size(); todo > len) {
364 todo = len;
365 }
366 auto ptr = data();
367 const auto end = ptr + todo;
368 while (ptr != end) {
369 *dst++ = *ptr++;
370 }
371 return todo;
372 }
373
reserve(size_type size)374 void reserve(size_type size)
375 {
376 if (size > capacity()) {
377 // setup new storage with old data
378 allocate(size);
379 }
380 }
381
resize(size_type size,value_type ch)382 void resize(size_type size, value_type ch)
383 {
384 if (const auto oldSize = length(); size < oldSize) {
385 data()[size] = '\0';
386 } else if (size > oldSize) {
387 reserve(size);
388 const auto ptr = data() + oldSize;
389 const size_type cap = capacity();
390 size = (size <= cap) ? size : cap;
391 const auto count = size - oldSize;
392 // ptr is valid, count fits capacity
393 ClearToValue(ptr, cap * sizeof(value_type), static_cast<uint8_t>(ch), count * sizeof(value_type));
394 ptr[count] = '\0';
395 }
396
397 set_size(size);
398 }
399
resize(size_type size)400 void resize(size_type size)
401 {
402 resize(size, '\0');
403 }
404
405 template<class StringT, class = StringViewLikeNotCStr<StringT>>
406 basic_string& operator+=(const StringT& a)
407 {
408 const auto view = string_view(a);
409 return append(view.data(), view.length());
410 }
411
412 basic_string& operator+=(const value_type a)
413 {
414 return push_back(a);
415 }
416
417 basic_string& operator+=(const_pointer const a)
418 {
419 return append(a, constexpr_strlen(a));
420 }
421
422 const_reference operator[](size_type i) const
423 {
424 return data()[i];
425 }
426
427 reference operator[](size_type i)
428 {
429 return data()[i];
430 }
431
replace(const_iterator first,const_iterator last,const string_view & str)432 basic_string& replace(const_iterator first, const_iterator last, const string_view& str)
433 {
434 const auto pos = first - cbegin();
435 const auto replace = last - first;
436 const auto add = static_cast<difference_type>(str.length());
437 if (add < replace) {
438 CloneData(data() + pos, replace * sizeof(value_type), str.data(), add * sizeof(value_type));
439 erase(first + add, last);
440 } else if (add > replace) {
441 CloneData(data() + pos, replace * sizeof(value_type), str.data(), replace * sizeof(value_type));
442 insert(static_cast<size_type>(pos + replace), str.data() + replace, static_cast<size_type>(add - replace));
443 } else {
444 CloneData(data() + pos, replace * sizeof(value_type), str.data(), add * sizeof(value_type));
445 }
446 return *this;
447 }
448
449 string_view substr(size_type pos = 0, size_type count = npos) const
450 {
451 return string_view(*this).substr(pos, count);
452 }
453
erase()454 basic_string& erase()
455 {
456 return erase(0, npos);
457 }
458
erase(const size_type off)459 basic_string& erase(const size_type off)
460 {
461 return erase(off, npos);
462 }
463
erase(const size_type off,size_type count)464 basic_string& erase(const size_type off, size_type count)
465 {
466 const auto oldSize = size();
467 if (off > oldSize) {
468 return *this;
469 } else if (count == 0) {
470 return *this;
471 } else {
472 auto newSize = oldSize;
473 const auto dst = data() + off;
474 if (count < (oldSize - off)) {
475 const auto dstSize = capacity() - off;
476 const auto src = dst + count;
477 const auto srcSize = oldSize + 1 - off - count;
478 // dst and src are valid, dst < src, and srcSize doesn't exceed capacity.
479 MoveData(dst, dstSize * sizeof(value_type), src, srcSize * sizeof(value_type));
480 newSize -= count;
481 } else {
482 *dst = '\0';
483 newSize = off;
484 }
485
486 set_size(newSize);
487 }
488 return *this;
489 }
490
erase(const_iterator pos)491 iterator erase(const_iterator pos)
492 {
493 const auto offset = pos - cbegin();
494 const auto count = 1U;
495 erase(static_cast<size_type>(offset), count);
496
497 return iterator(begin() + offset);
498 }
499
erase(const_iterator first,const_iterator last)500 iterator erase(const_iterator first, const_iterator last)
501 {
502 const auto offset = first - cbegin();
503 const auto count = static_cast<size_type>(last - first);
504 erase(static_cast<size_type>(offset), count);
505
506 return iterator(begin() + offset);
507 }
508
insert(size_type pos,const_pointer str)509 basic_string& insert(size_type pos, const_pointer str)
510 {
511 const auto view = string_view(str);
512 return insert(pos, view.data(), view.length());
513 }
514
insert(size_type pos,const_pointer str,size_type n)515 basic_string& insert(size_type pos, const_pointer str, size_type n)
516 {
517 const auto oldSize = size();
518 if (pos > oldSize) {
519 pos = oldSize;
520 }
521 const auto newSize = oldSize + n;
522 if (newSize > capacity()) {
523 if (const auto ptr = allocator_.alloc(newSize + 1)) {
524 const auto oldPtr = data();
525 CloneData(ptr, newSize * sizeof(value_type), oldPtr, pos * sizeof(value_type));
526 CloneData(ptr + pos + n, (newSize - pos - n) * sizeof(value_type), oldPtr + pos,
527 (oldSize - pos) * sizeof(value_type));
528 CloneData(ptr + pos, (newSize - pos) * sizeof(value_type), str, n * sizeof(value_type));
529 ptr[newSize] = '\0';
530
531 if (!is_short()) {
532 allocator_.free(oldPtr);
533 }
534
535 data_.longString.capacity = newSize;
536 data_.longString.size = newSize;
537 data_.longString.begin = ptr;
538 set_short(false);
539 }
540 } else {
541 auto ptr = data();
542 if (pos < oldSize) {
543 auto dst = ptr + newSize;
544 auto src = ptr + oldSize;
545 auto count = oldSize + 1 - pos;
546 while (count--) {
547 *dst-- = *src--;
548 }
549 }
550 CloneData(ptr + pos, (newSize - pos) * sizeof(value_type), str, n * sizeof(value_type));
551 ptr[newSize] = '\0';
552
553 set_size(newSize);
554 }
555 return *this;
556 }
557
push_back(value_type a)558 basic_string& push_back(value_type a)
559 {
560 const auto oldSize = size();
561 if (size_type cap = capacity(); oldSize == cap) {
562 if (!grow(1)) {
563 return *this;
564 }
565 }
566
567 const auto ptr = data();
568 ptr[oldSize] = a;
569 ptr[oldSize + 1] = '\0';
570 set_size(oldSize + 1);
571 return *this;
572 }
573
append(size_type count,value_type a)574 basic_string& append(size_type count, value_type a)
575 {
576 if (count) {
577 const auto oldSize = size();
578 const auto newSize = oldSize + count;
579 if (size_type cap = capacity(); cap < newSize) {
580 if (!grow(newSize)) {
581 return *this;
582 }
583 }
584
585 const auto ptr = data();
586 for (auto i = ptr + oldSize; i != (ptr + newSize); ++i) {
587 *i = a;
588 }
589 ptr[newSize] = '\0';
590
591 set_size(newSize);
592 }
593 return *this;
594 }
595
append(const_pointer const a)596 basic_string& append(const_pointer const a)
597 {
598 return append(a, constexpr_strlen(a));
599 }
600
append(const_pointer const str,size_type count)601 basic_string& append(const_pointer const str, size_type count)
602 {
603 if (str && count) {
604 size_type oldSize;
605 size_type oldCapacity;
606 pointer dst;
607 if (is_short()) {
608 oldSize = shortCapacity - data_.shortString.size;
609 oldCapacity = shortCapacity;
610 dst = data_.shortString.begin + 1;
611 } else {
612 oldSize = data_.longString.size;
613 oldCapacity = data_.longString.capacity;
614 dst = data_.longString.begin;
615 }
616
617 const auto newSize = oldSize + count;
618 if (oldCapacity < newSize) {
619 oldCapacity += oldCapacity / 2U;
620 if (!allocate(newSize < oldCapacity ? oldCapacity : newSize)) {
621 return *this;
622 }
623 dst = data_.longString.begin;
624 oldCapacity = data_.longString.capacity;
625 }
626
627 // dst and src are valid, oldSize + count is less than capacity.
628 CloneData(dst + oldSize, oldCapacity * sizeof(value_type), str, count * sizeof(value_type));
629 dst[newSize] = '\0';
630 set_size(newSize);
631 }
632 return *this;
633 }
634
append(const string_view & b)635 basic_string& append(const string_view& b)
636 {
637 return append(b.data(), b.length());
638 }
639
append(const string_view & b,size_type pos)640 basic_string& append(const string_view& b, size_type pos)
641 {
642 auto count = b.length();
643 count = (pos < count) ? (count - pos) : 0;
644 return append(b.data() + pos, count);
645 }
646
append(const string_view & b,size_type pos,size_type count)647 basic_string& append(const string_view& b, size_type pos, size_type count)
648 {
649 auto const strLen = b.length();
650 if (pos > strLen) {
651 count = 0;
652 pos = strLen ? (strLen - 1) : strLen;
653 } else if (count == npos) {
654 count = strLen - pos;
655 } else if (count > (strLen - pos)) {
656 count = strLen - pos;
657 }
658 return append(b.data() + pos, count);
659 }
660
661 /** compares two strings */
compare(string_view v)662 int compare(string_view v) const noexcept
663 {
664 return string_view(*this).compare(v);
665 }
666
compare(size_type pos1,size_type count1,string_view v)667 int compare(size_type pos1, size_type count1, string_view v) const
668 {
669 return substr(pos1, count1).compare(v);
670 }
671
compare(size_type pos1,size_type count1,string_view v,size_type pos2,size_type count2)672 int compare(size_type pos1, size_type count1, string_view v, size_type pos2, size_type count2) const
673 {
674 return substr(pos1, count1).compare(v.substr(pos2, count2));
675 }
676
compare(const CharT * const s)677 int compare(const CharT* const s) const
678 {
679 return string_view(*this).compare(s);
680 }
681
compare(size_type pos1,size_type count1,const CharT * const s)682 int compare(size_type pos1, size_type count1, const CharT* const s) const
683 {
684 return substr(pos1, count1).compare(s);
685 }
686
compare(size_type pos1,size_type count1,const CharT * const s,size_type count2)687 int compare(size_type pos1, size_type count1, const CharT* const s, size_type count2) const
688 {
689 return substr(pos1, count1).compare(basic_string_view(s, count2));
690 }
691
692 /** find substring in the view */
693 size_type find(const value_type str, size_type pos = 0) const noexcept
694 {
695 return string_view(*this).find(str, pos);
696 }
697
698 size_type find(const string_view& str, size_type pos = 0) const noexcept
699 {
700 return string_view(*this).find(str, pos);
701 }
702
703 /* find the last occurrence of a substring in the view */
704 size_type rfind(const value_type str, size_type pos = npos) const noexcept
705 {
706 return string_view(*this).rfind(str, pos);
707 }
708
709 size_type rfind(const string_view& str, size_type pos = npos) const noexcept
710 {
711 return string_view(*this).rfind(str, pos);
712 }
713
714 /* find first occurance of characters in the view */
715 size_type find_first_of(const string_view& str, size_type pos = 0) const noexcept
716 {
717 return string_view(*this).find_first_of(str, pos);
718 }
719
720 size_type find_first_of(value_type ch, size_type pos = 0) const noexcept
721 {
722 return string_view(*this).find_first_of(ch, pos);
723 }
724
725 /* find last occurrence of characters in the view */
726 size_type find_last_of(const string_view& str, size_type pos = npos) const noexcept
727 {
728 return string_view(*this).find_last_of(str, pos);
729 }
730
731 size_type find_last_of(value_type ch, size_type pos = npos) const noexcept
732 {
733 return string_view(*this).find_last_of(ch, pos);
734 }
735
736 /* checks if the string starts with the given prefix */
starts_with(basic_string_view<CharT> sv)737 bool starts_with(basic_string_view<CharT> sv) const noexcept
738 {
739 return operator basic_string_view<CharT>().starts_with(sv);
740 }
741
starts_with(CharT ch)742 bool starts_with(CharT ch) const noexcept
743 {
744 return operator basic_string_view<CharT>().starts_with(ch);
745 }
746
starts_with(const CharT * s)747 bool starts_with(const CharT* s) const
748 {
749 return operator basic_string_view<CharT>().starts_with(s);
750 }
751
752 /* checks if the string ends with the given suffix */
ends_with(basic_string_view<CharT> sv)753 bool ends_with(basic_string_view<CharT> sv) const noexcept
754 {
755 return operator basic_string_view<CharT>().ends_with(sv);
756 }
757
ends_with(CharT ch)758 bool ends_with(CharT ch) const noexcept
759 {
760 return operator basic_string_view<CharT>().ends_with(ch);
761 }
762
ends_with(const CharT * s)763 bool ends_with(const CharT* s) const
764 {
765 return operator basic_string_view<CharT>().ends_with(s);
766 }
767 /* find first absence of characters
768 find_first_not_of
769
770 find last absence of characters
771 find_last_not_of */
772
upper()773 basic_string& upper()
774 {
775 for (size_type i = 0; i < length(); i++) {
776 if (data()[i] >= 'a' && data()[i] <= 'z') {
777 data()[i] = (data()[i] - 'a') + 'A';
778 }
779 }
780 return *this;
781 }
782
lower()783 basic_string& lower()
784 {
785 for (size_type i = 0; i < length(); i++) {
786 if (data()[i] >= 'A' && data()[i] <= 'Z') {
787 data()[i] = (data()[i] - 'A') + 'a';
788 }
789 }
790 return *this;
791 }
792
toUpper()793 basic_string toUpper() const
794 {
795 basic_string res;
796 res.resize(length());
797 for (size_type i = 0; i < length(); i++) {
798 if (data()[i] >= 'a' && data()[i] <= 'z') {
799 res[i] = (data()[i] - 'a') + 'A';
800 } else {
801 res[i] = data()[i];
802 }
803 }
804 return res;
805 }
806
toLower()807 basic_string toLower() const
808 {
809 basic_string res;
810 res.resize(length());
811 for (size_type i = 0; i < length(); i++) {
812 if (data()[i] >= 'A' && data()[i] <= 'Z') {
813 res[i] = (data()[i] - 'A') + 'a';
814 } else {
815 res[i] = data()[i];
816 }
817 }
818 return res;
819 }
820
821 protected:
is_short()822 inline bool is_short() const
823 {
824 return data_.longString.isShort;
825 }
826
set_short(bool isShort)827 inline void set_short(bool isShort)
828 {
829 data_.longString.isShort = isShort;
830 }
831
set_size(size_type size)832 inline void set_size(size_type size)
833 {
834 if (is_short()) {
835 data_.shortString.size = static_cast<value_type>(shortCapacity - size);
836 } else {
837 data_.longString.size = size;
838 }
839 }
840
grow(const size_type minCapacity)841 inline bool grow(const size_type minCapacity)
842 {
843 auto cap = capacity();
844 cap += cap / 2; // 2: parm
845 return allocate(minCapacity < cap ? cap : minCapacity);
846 }
847
allocate(size_type size)848 bool allocate(size_type size)
849 {
850 // setup new storage with old data
851 if (auto ptr = allocator_.alloc(size + 1); ptr) {
852 const auto oldSize = length();
853 if (oldSize) {
854 CloneData(ptr, size * sizeof(value_type), data(), oldSize * sizeof(value_type));
855 }
856 ptr[oldSize] = '\0';
857
858 if (!is_short()) {
859 allocator_.free(data_.longString.begin);
860 }
861
862 set_short(false);
863 data_.longString.capacity = size;
864 data_.longString.size = oldSize;
865 data_.longString.begin = ptr;
866 return true;
867 }
868 return false;
869 }
870
construct(const_pointer const str,size_type count)871 void construct(const_pointer const str, size_type count)
872 {
873 if (!str || count == 0) {
874 set_short(true);
875 data_.shortString.begin[1] = '\0';
876 data_.shortString.size = static_cast<value_type>(shortCapacity);
877 } else {
878 pointer dst;
879 if (count <= shortCapacity) {
880 dst = data_.shortString.begin + 1;
881 set_short(true);
882 data_.shortString.size = static_cast<value_type>(shortCapacity - count);
883 } else {
884 dst = allocator_.alloc(count + 1);
885 if (!dst) {
886 set_short(true);
887 data_.shortString.begin[1] = '\0';
888 data_.shortString.size = static_cast<value_type>(shortCapacity);
889 return;
890 }
891 set_short(false);
892 data_.longString.capacity = count;
893 data_.longString.size = count;
894 data_.longString.begin = dst;
895 }
896 // destination and source are valid and the allocation sizes are for at least count characters.
897 CloneData(dst, count * sizeof(value_type), str, count * sizeof(value_type));
898 dst[count] = '\0';
899 }
900 }
901
902 struct LongString {
903 bool isShort;
904 size_type capacity;
905 size_type size;
906 pointer begin;
907 };
908
909 // short string capacity: one value_type for the size and another for isShort
910 static constexpr auto shortCapacity = (sizeof(LongString) - 2u * sizeof(value_type)) / sizeof(value_type);
911
912 struct ShortString {
913 // add one for isShort in the begining
914 value_type begin[shortCapacity + 1];
915 value_type size;
916 };
917
918 union Data {
919 LongString longString;
920 ShortString shortString;
921 };
922
923 // Wrapper to create a "re-seatable" reference.
924 class Wrapper {
925 public:
Wrapper(allocator & a)926 inline Wrapper(allocator& a) : allocator_(&a) {}
927 inline Wrapper& operator=(allocator& a)
928 {
929 allocator_ = &a;
930 return *this;
931 }
alloc(size_type size)932 inline pointer alloc(size_type size)
933 {
934 if ((allocator_) && (allocator_->alloc)) {
935 return static_cast<pointer>(allocator_->alloc(allocator_->instance, sizeof(value_type) * size));
936 }
937 return nullptr;
938 }
free(void * ptr)939 inline void free(void* ptr)
940 {
941 if ((allocator_) && (allocator_->free)) {
942 allocator_->free(allocator_->instance, ptr);
943 }
944 }
get()945 allocator& get()
946 {
947 BASE_ASSERT(allocator_ != nullptr);
948 return *allocator_;
949 }
get()950 const allocator& get() const
951 {
952 BASE_ASSERT(allocator_ != nullptr);
953 return *allocator_;
954 }
955
956 private:
957 allocator* allocator_ { nullptr };
958 } allocator_;
959 Data data_;
960 };
961
962 inline string operator+(const string& a, const string& b)
963 {
964 string res;
965 res.reserve(a.length() + b.length());
966 res = a;
967 res += b;
968 return res;
969 }
970
971 inline string operator+(const string& a, const char* b)
972 {
973 string res;
974 const auto sv = string_view(b);
975 res.reserve(a.length() + sv.length());
976 res = a;
977 res += sv;
978 return res;
979 }
980
981 inline string operator+(const string& a, char b)
982 {
983 string res;
984 res.reserve(a.length() + 1);
985 res = a;
986 res += b;
987 return res;
988 }
989
990 inline string operator+(const char* a, const string& b)
991 {
992 string res;
993 const auto sv = string_view(a);
994 res.reserve(sv.length() + b.length());
995 res = sv;
996 res += b;
997 return res;
998 }
999
1000 inline string operator+(char a, const string& b)
1001 {
1002 string res;
1003 res.reserve(1 + b.length());
1004 res = a;
1005 res += b;
1006 return res;
1007 }
1008
1009 inline string operator+(const string_view& a, const string_view& b)
1010 {
1011 string res;
1012 res.reserve(a.length() + b.length());
1013 res = a;
1014 res += b;
1015 return res;
1016 }
1017
1018 inline string operator+(const string_view& a, char b)
1019 {
1020 string res;
1021 res.reserve(a.length() + 1);
1022 res = a;
1023 res += b;
1024 return res;
1025 }
1026
1027 inline string operator+(const char a, string_view& b)
1028 {
1029 string res;
1030 res.reserve(b.length() + 1);
1031 res = a;
1032 res += b;
1033 return res;
1034 }
1035
1036 inline string operator+(string&& a, string&& b)
1037 {
1038 if (a.capacity() >= b.capacity()) {
1039 return move(a.append(b));
1040 } else {
1041 return move(b.insert(0, a.data(), a.size()));
1042 }
1043 }
1044
1045 inline string operator+(string&& a, const char* b)
1046 {
1047 return move(a.append(b));
1048 }
1049
1050 inline string operator+(string&& a, char b)
1051 {
1052 return move(a.push_back(b));
1053 }
1054
1055 inline string operator+(const char* a, string&& b)
1056 {
1057 return move(b.insert(0, a));
1058 }
1059
1060 inline string operator+(char a, string&& b)
1061 {
1062 return move(b.insert(0, &a, 1));
1063 }
1064
1065 template<class CharT>
1066 inline bool operator==(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1067 {
1068 return string_view(lhs) == string_view(rhs);
1069 }
1070
1071 template<class CharT>
1072 inline bool operator==(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1073 {
1074 return string_view(lhs) == rhs;
1075 }
1076
1077 template<class CharT>
1078 inline bool operator==(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1079 {
1080 return lhs == string_view(rhs);
1081 }
1082
1083 template<class CharT>
1084 inline bool operator!=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1085 {
1086 return string_view(lhs) != string_view(rhs);
1087 }
1088
1089 template<class CharT>
1090 inline bool operator!=(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1091 {
1092 return string_view(lhs) != rhs;
1093 }
1094
1095 template<class CharT>
1096 inline bool operator!=(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1097 {
1098 return lhs != string_view(rhs);
1099 }
1100
1101 template<class CharT>
1102 inline bool operator<(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1103 {
1104 return string_view(lhs) < string_view(rhs);
1105 }
1106
1107 template<class CharT>
1108 inline bool operator<(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1109 {
1110 return string_view(lhs) < rhs;
1111 }
1112
1113 template<class CharT>
1114 inline bool operator<(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1115 {
1116 return lhs < string_view(rhs);
1117 }
1118
1119 template<class CharT>
1120 inline bool operator<=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1121 {
1122 return string_view(lhs) <= string_view(rhs);
1123 }
1124
1125 template<class CharT>
1126 inline bool operator<=(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1127 {
1128 return string_view(lhs) <= rhs;
1129 }
1130
1131 template<class CharT>
1132 inline bool operator<=(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1133 {
1134 return lhs <= string_view(rhs);
1135 }
1136
1137 template<class CharT>
1138 inline bool operator>(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1139 {
1140 return string_view(lhs) > string_view(rhs);
1141 }
1142
1143 template<class CharT>
1144 inline bool operator>(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1145 {
1146 return string_view(lhs) > rhs;
1147 }
1148
1149 template<class CharT>
1150 inline bool operator>(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1151 {
1152 return lhs > string_view(rhs);
1153 }
1154
1155 template<class CharT>
1156 inline bool operator>=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs) noexcept
1157 {
1158 return string_view(lhs) >= string_view(rhs);
1159 }
1160
1161 template<class CharT>
1162 inline bool operator>=(const basic_string<CharT>& lhs, const CharT* const rhs) noexcept
1163 {
1164 return string_view(lhs) >= rhs;
1165 }
1166
1167 template<class CharT>
1168 inline bool operator>=(const CharT* const lhs, const basic_string<CharT>& rhs) noexcept
1169 {
1170 return lhs >= string_view(rhs);
1171 }
1172
1173 template<>
hash(const string & value)1174 inline uint64_t hash(const string& value)
1175 {
1176 return BASE_NS::FNV1aHash(value.data(), value.size());
1177 }
1178
1179 namespace literals {
1180 inline namespace string_literals {
1181 /// User-defined literal to allow the following syntax to construct a string: "myLiteral"_s
1182 [[nodiscard]] inline BASE_NS::string operator""_s(const char* literal, const size_t length)
1183 {
1184 return { literal, length };
1185 }
1186 } // namespace string_literals
1187 } // namespace literals
1188 BASE_END_NAMESPACE()
1189
1190 #endif // API_BASE_CONTAINERS_STRING_H
1191