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_BASIC_FIXED_STRING_H
17 #define API_BASE_CONTAINERS_BASIC_FIXED_STRING_H
18 
19 #include <cstddef>
20 #include <cstdint>
21 
22 #include <base/containers/allocator.h>
23 #include <base/containers/string_view.h>
24 #include <base/containers/type_traits.h>
25 #include <base/namespace.h>
26 
27 BASE_BEGIN_NAMESPACE()
28 template<class CharT, size_t maxSize>
29 class basic_fixed_string;
30 
31 template<size_t maxSize>
32 using fixed_string = BASE_NS::basic_fixed_string<char, maxSize>;
33 
34 template<size_t maxSize>
35 using fixed_wstring = BASE_NS::basic_fixed_string<wchar_t, maxSize>;
36 
37 template<class CharT, size_t maxSize>
38 class basic_fixed_string {
39 public:
40     using string_view = basic_string_view<CharT>;
41     using value_type = CharT;
42     using size_type = typename string_view::size_type;
43     static constexpr size_type npos = string_view::npos;
44     basic_fixed_string() = default;
45     ~basic_fixed_string() = default;
46 
basic_fixed_string(const basic_fixed_string & a)47     constexpr basic_fixed_string(const basic_fixed_string& a) noexcept
48     {
49         initialize({ a.data_, a.len_ });
50     }
51 
basic_fixed_string(basic_fixed_string && a)52     constexpr basic_fixed_string(basic_fixed_string&& a) noexcept
53     {
54         initialize({ a.data_, a.len_ });
55     }
56 
57     template<size_t N>
basic_fixed_string(const basic_fixed_string<CharT,N> & a)58     constexpr basic_fixed_string(const basic_fixed_string<CharT, N>& a) noexcept
59     {
60         initialize({ a.data(), a.size() });
61     }
62 
63     template<size_t N>
basic_fixed_string(const CharT (& a)[N])64     constexpr basic_fixed_string(const CharT (&a)[N])
65     {
66         initialize({ a, N - 1 });
67     }
68 
basic_fixed_string(const CharT * const a)69     constexpr basic_fixed_string(const CharT* const a) noexcept
70     {
71         initialize({ a, constexpr_strlen(a) });
72     }
73 
basic_fixed_string(CharT a)74     constexpr basic_fixed_string(CharT a) noexcept
75     {
76         initialize({ &a, 1 });
77     }
78 
basic_fixed_string(const basic_string_view<CharT> & a)79     constexpr basic_fixed_string(const basic_string_view<CharT>& a) noexcept
80     {
81         initialize(a);
82     }
83 
basic_fixed_string(const size_t size)84     constexpr basic_fixed_string(const size_t size) noexcept : len_((size < maxSize) ? size : maxSize) {}
85 
86     template<typename T, typename = void>
87     struct HasData : false_type {};
88     template<typename T>
89     struct HasData<T, void_t<decltype(declval<T>().data())>> : true_type {};
90     template<typename T, typename = void>
91     struct HasSize : false_type {};
92     template<typename T>
93     struct HasSize<T, void_t<decltype(declval<T>().size())>> : true_type {};
94     template<typename T>
95     using HasDataAndSize = enable_if_t<(HasData<T>::value && HasSize<T>::value)>;
96     template<class StringT, class = HasDataAndSize<StringT>>
97     constexpr explicit basic_fixed_string(const StringT& s) noexcept;
98 
99     constexpr bool empty() const noexcept
100     {
101         return len_ == 0;
102     }
103 
104     constexpr size_t size() const noexcept
105     {
106         return len_;
107     }
108 
109     constexpr size_t length() const noexcept
110     {
111         return len_;
112     }
113 
114     constexpr size_t capacity() const noexcept
115     {
116         return maxSize;
117     }
118 
119     constexpr basic_fixed_string& operator=(const basic_fixed_string& a) noexcept
120     {
121         initialize({ a.data_, a.len_ });
122         return *this;
123     }
124 
125     constexpr basic_fixed_string& operator=(basic_fixed_string&& a) noexcept
126     {
127         initialize({ a.data_, a.len_ });
128         return *this;
129     }
130 
131     template<size_t N>
132     constexpr basic_fixed_string& operator=(const basic_fixed_string<CharT, N>& a) noexcept
133     {
134         initialize({ a.data(), a.size() });
135         return *this;
136     }
137 
138     template<size_t N>
139     constexpr basic_fixed_string& operator=(basic_fixed_string<CharT, N>&& a) noexcept
140     {
141         initialize({ a.data(), a.size() });
142         return *this;
143     }
144 
145     constexpr basic_fixed_string& operator=(const string_view& a) noexcept
146     {
147         initialize(a);
148         return *this;
149     }
150 
151     constexpr basic_fixed_string& operator=(CharT const* const& a) noexcept
152     {
153         initialize({ a, constexpr_strlen(a) });
154         return *this;
155     }
156 
157     constexpr basic_fixed_string operator+(const string_view& b) const
158     {
159         basic_fixed_string res(*this);
160         res.append_impl(b);
161         return res;
162     }
163 
164     constexpr basic_fixed_string operator+(CharT const* const& a) const
165     {
166         basic_fixed_string res(*this);
167         res.append_impl({ a, constexpr_strlen(a) });
168         return res;
169     }
170 
171     constexpr basic_fixed_string& operator+=(const string_view& a)
172     {
173         append_impl(a);
174         return *this;
175     }
176 
177     constexpr basic_fixed_string& operator+=(CharT const* const& a)
178     {
179         append_impl({ a, constexpr_strlen(a) });
180         return *this;
181     }
182 
183     constexpr basic_fixed_string& operator+=(CharT a)
184     {
185         append_impl({ &a, 1 });
186         return *this;
187     }
188 
189     constexpr CharT const* data() const
190     {
191         return data_;
192     }
193 
194     constexpr CharT* data()
195     {
196         return data_;
197     }
198 
199     constexpr CharT const* c_str() const
200     {
201         return data_;
202     }
203 
204     constexpr operator string_view() const noexcept
205     {
206         return string_view(data_, len_);
207     }
208 
209     constexpr size_type copy(CharT* const dst, size_type todo, size_type pos = 0) const
210     {
211         return string_view(*this).copy(dst, todo, pos);
212     }
213 
214     /** find substring in the view */
215     constexpr size_type find(const CharT str, size_type pos = 0) const noexcept
216     {
217         return string_view(*this).find(str, pos);
218     }
219 
220     constexpr size_type find(const string_view& str, size_type pos = 0) const noexcept
221     {
222         return string_view(*this).find(str, pos);
223     }
224 
225     /* find the last occurrence of a substring in the view */
226     constexpr size_type rfind(const CharT str, size_type pos = npos) const noexcept
227     {
228         return string_view(*this).rfind(str, pos);
229     }
230 
231     constexpr size_type rfind(const string_view& str, size_type pos = npos) const noexcept
232     {
233         return string_view(*this).rfind(str, pos);
234     }
235 
236     /* find first occurance of characters in the view */
237     constexpr size_type find_first_of(const string_view& str, size_type pos = 0) const noexcept
238     {
239         return string_view(*this).find_first_of(str, pos);
240     }
241 
242     constexpr size_type find_first_of(CharT ch, size_type pos = 0) const noexcept
243     {
244         return string_view(*this).find_first_of(ch, pos);
245     }
246 
247     /* find last occurrence of characters in the view */
248     constexpr size_type find_last_of(const string_view& str, size_type pos = npos) const noexcept
249     {
250         return string_view(*this).find_last_of(str, pos);
251     }
252 
253     constexpr size_type find_last_of(CharT ch, size_type pos = npos) const noexcept
254     {
255         return string_view(*this).find_last_of(ch, pos);
256     }
257 
258     /* find first absence of characters
259     find_first_not_of
260 
261     find last absence of characters
262     find_last_not_of */
263     constexpr basic_fixed_string& append(const string_view& b)
264     {
265         append_impl(b);
266         return *this;
267     }
268 
269     constexpr basic_fixed_string& append(CharT const* const a)
270     {
271         append_impl({ a, constexpr_strlen(a) });
272         return *this;
273     }
274 
275     constexpr basic_fixed_string& append(CharT a)
276     {
277         append_impl({ &a, 1 });
278         return *this;
279     }
280 
281     basic_fixed_string& replace(size_t first, size_t last, const basic_string_view<CharT>& str)
282     {
283         const auto replace = last - first;
284         const auto add = str.length();
285         const auto newSize = len_ + add - replace;
286         if (add < replace) {
287             CloneData(data() + first, replace, str.data(), add);
288             MoveData(data() + first + add, len_ - first - add, data() + last, len_ - last);
289         } else if (add > replace) {
290             const auto start = newSize < maxSize ? newSize : maxSize;
291             for (auto i = start; i > last; --i) {
292                 data_[i] = data_[i - add + replace];
293             }
294             CloneData(data() + first, len_, str.data(), add);
295         } else {
296             CloneData(data() + first, replace, str.data(), add);
297         }
298         len_ = newSize < maxSize ? newSize : maxSize;
299         data_[len_] = 0;
300         return *this;
301     }
302 
303 protected:
304     constexpr void initialize(const string_view& other)
305     {
306         len_ = other.length();
307         len_ = (len_ < maxSize) ? len_ : maxSize;
308         other.copy(data_, len_);
309         data_[len_] = 0;
310     }
311 
312     constexpr void append_impl(const string_view& other)
313     {
314         size_t todo = other.length();
315         todo = ((todo + len_) > maxSize) ? (maxSize - len_) : todo;
316         other.copy(data_ + len_, todo);
317         len_ += todo;
318         data_[len_] = 0;
319     }
320 
321     size_t len_ { 0 };
322     CharT data_[maxSize + 1] { 0 };
323 };
324 
325 template<class CharT, size_t maxSize>
326 template<class StringT, class>
327 constexpr basic_fixed_string<CharT, maxSize>::basic_fixed_string(const StringT& s) noexcept
328 {
329     initialize({ s.data(), s.length() });
330 }
331 template<class CharT, size_t N>
332 basic_fixed_string(const CharT (&)[N]) -> basic_fixed_string<CharT, N - 1>;
333 
334 template<class CharT, size_t M, size_t N>
335 constexpr basic_fixed_string<CharT, M + N> operator+(
336     const basic_fixed_string<CharT, M>& lhs, const basic_fixed_string<CharT, N>& rhs) noexcept
337 {
338     basic_fixed_string<CharT, M + N> res { lhs };
339     res.append(rhs);
340     return res;
341 }
342 
343 template<class CharT, size_t M, size_t N>
344 constexpr basic_fixed_string<CharT, M + N> operator+(
345     const CharT (&lhs)[M], const basic_fixed_string<CharT, N>& rhs) noexcept
346 {
347     basic_fixed_string<CharT, M + N> res { lhs };
348     res.append(rhs);
349     return res;
350 }
351 
352 template<class CharT, size_t M, size_t N>
353 constexpr basic_fixed_string<CharT, M + N> operator+(
354     const basic_fixed_string<CharT, M>& lhs, const CharT (&rhs)[N]) noexcept
355 {
356     basic_fixed_string<CharT, M + N> res { lhs };
357     res.append(rhs);
358     return res;
359 }
360 
361 template<class CharT, size_t N>
362 constexpr basic_fixed_string<CharT, 1 + N> operator+(CharT lhs, const basic_fixed_string<CharT, N>& rhs) noexcept
363 {
364     basic_fixed_string<CharT, 1 + N> res(string_view(&lhs, 1));
365     res.append(rhs);
366     return res;
367 }
368 
369 template<class CharT, size_t M, size_t N>
370 constexpr bool operator==(const basic_fixed_string<CharT, M>& lhs, const basic_fixed_string<CharT, N>& rhs) noexcept
371 {
372     return string_view(lhs) == string_view(rhs);
373 }
374 
375 template<class CharT, size_t M, int = 1>
376 constexpr bool operator==(
377     const basic_fixed_string<CharT, M>& lhs, const type_identity_t<basic_string_view<CharT>> rhs) noexcept
378 {
379     return string_view(lhs) == rhs;
380 }
381 
382 template<class CharT, size_t M, int = 2>
383 constexpr bool operator==(
384     const type_identity_t<basic_string_view<CharT>> lhs, const basic_fixed_string<CharT, M>& rhs) noexcept
385 {
386     return lhs == string_view(rhs);
387 }
388 
389 template<class CharT, size_t M, size_t N>
390 constexpr bool operator!=(const basic_fixed_string<CharT, M>& lhs, const basic_fixed_string<CharT, N>& rhs) noexcept
391 {
392     return string_view(lhs) != string_view(rhs);
393 }
394 
395 template<class CharT, size_t M, int = 1>
396 constexpr bool operator!=(
397     const basic_fixed_string<CharT, M>& lhs, const type_identity_t<basic_string_view<CharT>> rhs) noexcept
398 {
399     return string_view(lhs) != rhs;
400 }
401 
402 template<class CharT, size_t M, int = 2>
403 constexpr bool operator!=(
404     const type_identity_t<basic_string_view<CharT>> lhs, const basic_fixed_string<CharT, M>& rhs) noexcept
405 {
406     return lhs != string_view(rhs);
407 }
408 
409 template<class CharT, size_t M, size_t N>
410 constexpr bool operator<(const basic_fixed_string<CharT, M>& lhs, const basic_fixed_string<CharT, N>& rhs) noexcept
411 {
412     return string_view(lhs) < string_view(rhs);
413 }
414 
415 template<class CharT, size_t M, int = 1>
416 constexpr bool operator<(
417     const basic_fixed_string<CharT, M>& lhs, const type_identity_t<basic_string_view<CharT>> rhs) noexcept
418 {
419     return string_view(lhs) < rhs;
420 }
421 
422 template<class CharT, size_t M, int = 2>
423 constexpr bool operator<(
424     const type_identity_t<basic_string_view<CharT>> lhs, const basic_fixed_string<CharT, M>& rhs) noexcept
425 {
426     return lhs < string_view(rhs);
427 }
428 
429 template<typename Number, typename = enable_if_t<is_integral_v<Number>>>
430 constexpr fixed_string<21u> to_string(Number num)
431 {
432     fixed_string<21u> str;
433     uint64_t n = static_cast<uint64_t>(num);
434     // negate negative values
435     if constexpr (is_signed<Number>::value) {
436         if (num < 0) {
437             n = static_cast<uint64_t>(-num);
438         }
439     }
440 
441     // write starting from the end
442     const auto end = str.data() + str.capacity();
443     auto p = end - 1;
444     do {
445         *p-- = '0' + static_cast<char>(n % 10U);
446         n /= 10U;
447     } while (n != 0U);
448 
449     // add sign if needed
450     if constexpr (is_signed<Number>::value) {
451         if (num < 0) {
452             *p-- = '-';
453         }
454     }
455 
456     ++p;
457     str.append(string_view(p, static_cast<size_t>(end - p)));
458     return str;
459 }
460 
461 template<typename Number, typename = enable_if_t<is_integral_v<Number>>>
462 constexpr fixed_string<21u> to_hex(Number num)
463 {
464     fixed_string<21u> str;
465     uint64_t n = static_cast<uint64_t>(num);
466     // negate negative values
467     if constexpr (is_signed<Number>::value) {
468         if (num < 0) {
469             n = static_cast<uint64_t>(-num);
470         }
471     }
472 
473     // write starting from the end
474     const auto end = str.data() + str.capacity();
475     auto p = end - 1;
476     const char* hex = "0123456789ABCDEF";
477     do {
478         *p-- = hex[(n % 16U)];
479         n /= 16U;
480     } while (n != 0U);
481 
482     // add sign if needed
483     if constexpr (is_signed<Number>::value) {
484         if (num < 0) {
485             *p-- = '-';
486         }
487     }
488 
489     ++p;
490     str.append(string_view(p, static_cast<string_view::size_type>(end - p)));
491     return str;
492 }
493 
494 template<size_t maxSize>
495 inline uint64_t hash(const fixed_string<maxSize>& value)
496 {
497     return BASE_NS::FNV1aHash(value.data(), value.size());
498 }
499 BASE_END_NAMESPACE()
500 #endif // API_BASE_CONTAINERS_BASIC_FIXED_STRING_H