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