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