• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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