simstr 1.7.3
Yet another strings library
 
Loading...
Searching...
No Matches
sstring.h
1/*
2* ver. 1.7.3
3 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* Classes for working with strings
7 */
8
19#pragma once
20
21#ifndef __has_declspec_attribute
22#define __has_declspec_attribute(x) 0
23#endif
24const bool isWindowsOs = // NOLINT
25#ifdef _WIN32
26 true
27#else
28 false
29#endif
30 ;
31
32#ifdef SIMSTR_IN_SHARED
33 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
34 #ifdef SIMSTR_EXPORT
35 #define SIMSTR_API __declspec(dllexport)
36 #else
37 #define SIMSTR_API __declspec(dllimport)
38 #endif
39 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
40 #define SIMSTR_API __attribute__((visibility("default")))
41 #else
42 #define SIMSTR_API
43 #endif
44#else
45 #define SIMSTR_API
46#endif
47
48#define IN_FULL_SIMSTR
49#include "strexpr.h"
50
51#include <format>
52#include <unordered_map>
53#include <atomic>
54#include <memory>
55#include <string.h>
56#include <iostream>
57#include <cmath>
58
59#ifdef _WIN32
60#include <stdio.h>
61#endif
62
63#ifdef _MSC_VER
64// warning C4201 : nonstandard extension used : nameless struct / union
65#pragma warning(disable : 4201)
66#endif
67
68namespace simstr {
69
70template<typename T>
71struct unicode_traits {}; // NOLINT
72
73template<>
74struct unicode_traits<u8s> {
75 // Эти операции с utf-8 могут изменить длину строки
76 // Поэтому их специализации отличаются
77 // В функцию помимо текста и адреса буфера для записи передается размер буфера
78 // Возвращает длину получающейся строки.
79 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
80 // обработанные символы, для повторного возобновления работы,
81 // а для оставшихся символов считается нужный размер буфера.
82 // These utf-8 operations can change the length of the string
83 // Therefore their specializations are different
84 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
85 // Returns the length of the resulting string.
86 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
87 // processed characters to resume work again,
88 // and for the remaining characters the required buffer size is calculated.
89 static SIMSTR_API size_t upper(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
90 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
91 static SIMSTR_API size_t upper_len(const u8s* src, size_t len);
92 static SIMSTR_API size_t lower_len(const u8s* src, size_t len);
93
94 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
95
96 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
97 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
98};
99
100template<>
101struct unicode_traits<u16s> {
102 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
103 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
104
105 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
106 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
107 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
108};
109
110template<>
111struct unicode_traits<u32s> {
112 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
113 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
114
115 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
116 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
117 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
118};
119
120template<>
121struct unicode_traits<wchar_t> {
122 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
123 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
124 }
125 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
126 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
127 }
128
129 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
130 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
131 }
132 static size_t hashia(const wchar_t* src, size_t s) {
133 return unicode_traits<wchar_type>::hashia(to_w(src), s);
134 }
135 static size_t hashiu(const wchar_t* src, size_t s) {
136 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
137 }
138};
139
140template<>
141struct unicode_traits<char8_t> {
142 static size_t upper(const ubs*& src, size_t lenStr, ubs*& dest, size_t lenBuf) {
143 return unicode_traits<char>::upper((const u8s*&)src, lenStr, (u8s*&)dest, lenBuf);
144 }
145 static size_t lower(const ubs*& src, size_t len, ubs*& dest, size_t lenBuf) {
146 return unicode_traits<char>::lower((const u8s*&)src, len, (u8s*&)dest, lenBuf);
147 }
148 static size_t upper_len(const ubs* src, size_t len) {
149 return unicode_traits<char>::upper_len((const u8s*)src, len);
150 }
151 static size_t lower_len(const ubs* src, size_t len) {
152 return unicode_traits<char>::lower_len((const u8s*)src, len);
153 }
154
155 static int compareiu(const char8_t* text1, size_t len1, const char8_t* text2, size_t len2) {
156 return unicode_traits<char>::compareiu((const char*)text1, len1, (const char*)text2, len2);
157 }
158 static size_t hashia(const char8_t* src, size_t s) {
159 return unicode_traits<char>::hashia((const char*)src, s);
160 }
161 static size_t hashiu(const char8_t* src, size_t s) {
162 return unicode_traits<char>::hashiu((const char*)src, s);
163 }
164};
165
166#if defined(_MSC_VER) && _MSC_VER <= 1933
167template<typename K, typename... Args>
168using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
169#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
170template<typename K, typename... Args>
171using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
172#else
173template<typename K, typename... Args>
174using FmtString = std::basic_string_view<K>;
175#endif
176
177template<typename K>
178SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
179
192template<typename K, typename StrRef, typename Impl, bool Mutable>
193class str_algs : public str_src_algs<K, StrRef, Impl, Mutable> {
194 constexpr const Impl& d() const noexcept {
195 return *static_cast<const Impl*>(this);
196 }
197 constexpr size_t _len() const noexcept {
198 return d().length();
199 }
200 constexpr const K* _str() const noexcept {
201 return d().symbols();
202 }
203 constexpr bool _is_empty() const noexcept {
204 return d().is_empty();
205 }
206
207public:
208 using symb_type = K;
209 using str_piece = StrRef;
210 using traits = ch_traits<K>;
211 using uni = unicode_traits<K>;
212 using uns_type = std::make_unsigned_t<K>;
213 using my_type = Impl;
214 using base = str_src_algs<K, StrRef, Impl, Mutable>;
215 str_algs() = default;
216
217 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
218 if (!len)
219 return _is_empty() ? 0 : 1;
220 return uni::compareiu(_str(), _len(), text, len);
221 }
230 int compare_iu(str_piece text) const noexcept { // NOLINT
231 return compare_iu(text.symbols(), text.length());
232 }
233
241 bool equal_iu(str_piece text) const noexcept { // NOLINT
242 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
243 }
244
252 bool less_iu(str_piece text) const noexcept { // NOLINT
253 return compare_iu(text.symbols(), text.length()) < 0;
254 }
255 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
256 // Does this string begin with the specified substring, insensitive to unicode case
257 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
258 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
259 }
266 bool starts_with_iu(str_piece prefix) const noexcept {
267 return starts_with_iu(prefix.symbols(), prefix.length());
268 }
269 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
270 // Whether the string ends with the specified substring, case insensitive UNICODE
271 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
272 size_t myLen = _len();
273 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
274 }
281 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
282 return ends_with_iu(suffix.symbols(), suffix.length());
283 }
284
292 template<typename R = my_type>
293 R upperred() const {
294 return R::upperred_from(d());
295 }
296
304 template<typename R = my_type>
305 R lowered() const {
306 return R::lowered_from(d());
307 }
308
314 template<bool SkipWS = true, bool AllowPlus = true>
315 std::optional<double> to_double() const noexcept {
316 size_t len = _len();
317 const K* ptr = _str();
318 if constexpr (SkipWS) {
319 while (len && uns_type(*ptr) <= ' ') {
320 len--;
321 ptr++;
322 }
323 }
324 if constexpr (AllowPlus) {
325 if (len && *ptr == K('+')) {
326 ptr++;
327 len--;
328 }
329 }
330 if (!len) {
331 return {};
332 }
333 #ifdef __linux__
334 if constexpr(sizeof(K) == 1 && requires {std::from_chars(std::declval<const K*>(), std::declval<const K*>(), std::declval<double&>()); }) {
335 double d{};
336 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
337 return d;
338 }
339 return {};
340 }
341 #endif
342 if constexpr (sizeof(K) == 1) {
343 return impl_to_double((const char*)ptr, (const char*)ptr + len);
344 } else if constexpr (sizeof(K) == 2) {
345 return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
346 } else {
347 return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
348 }
349 }
350
356 void as_number(double& t) const {
357 auto res = to_double();
358 t = res ? *res : std::nan("0");
359 }
360
372 template<ToIntNumber T>
373 constexpr void as_number(T& t) const {
375 }
376
388 template<typename R = str_piece>
389 constexpr std::optional<R> strip_prefix_iu(str_piece prefix) const {
390 if (starts_with_iu(prefix)) {
391 return R{operator()(prefix.length())};
392 }
393 return {};
394 }
395
407 template<typename R = str_piece>
408 constexpr std::optional<R> strip_suffix_iu(str_piece suffix) const {
409 if (ends_with_iu(suffix)) {
410 return R{operator()(0, -suffix.length())};
411 }
412 return {};
413 }
414
426 template<typename R = str_piece>
427 constexpr R trimmed_prefix_iu(str_piece prefix, size_t max_count = 0) const {
428 str_piece res = *this;
429 while(res.starts_with_iu(prefix)) {
430 res = res(prefix.length());
431 if (--max_count == 0) {
432 break;
433 }
434 }
435 return res;
436 }
437
449 template<typename R = str_piece>
450 constexpr R trimmed_suffix_iu(str_piece suffix) const {
451 str_piece res = *this;
452 while(res.ends_with_iu(suffix)) {
453 res = res(0, -suffix.length());
454 }
455 return res;
456 }
457};
458
459/*
460* Базовая структура с информацией о строке.
461* Это структура для не владеющих строк.
462* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
463* в результате размер класса не увеличивается
464* Basic structure with string information.
465* This is the structure for non-owning strings.
466* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
467* as a result the class size does not increase
468*/
469
480template<typename K>
481struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
482 using symb_type = K;
483 using my_type = simple_str<K>;
484
485 const symb_type* str;
486 size_t len;
487
488 constexpr simple_str() = default;
489
490 constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
491
496 template<typename T, size_t N = const_lit_for<K, T>::Count>
497 constexpr simple_str(T&& v) noexcept : str((const K*)v), len(N - 1) {}
502 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
507 template<typename A>
508 constexpr simple_str(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
513 constexpr simple_str(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
518 constexpr size_t length() const noexcept {
519 return len;
520 }
521
525 constexpr const symb_type* symbols() const noexcept {
526 return str;
527 }
528
532 constexpr bool is_empty() const noexcept {
533 return len == 0;
534 }
535
541 constexpr bool is_same(simple_str<K> other) const noexcept {
542 return str == other.str && len == other.len;
543 }
544
550 constexpr bool is_part_of(simple_str<K> other) const noexcept {
551 return str >= other.str && str + len <= other.str + other.len;
552 }
553
561 constexpr K operator[](size_t idx) const {
562 return str[idx];
563 }
564
572 constexpr my_type& remove_prefix(size_t delta) {
573 str += delta;
574 len -= delta;
575 return *this;
576 }
577
585 constexpr my_type& remove_suffix(size_t delta) {
586 len -= delta;
587 return *this;
588 }
589};
590
591template<typename K>
592struct simple_str_selector {
593 using type = simple_str<K>;
594};
595
616template<typename K>
617struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
618 using symb_type = K;
619 using my_type = simple_str_nt<K>;
620 using base = simple_str<K>;
621
622 constexpr static const K empty_string[1] = {0};
623
624 simple_str_nt() = default;
641 template<typename T> requires is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
642 constexpr explicit simple_str_nt(T&& p) noexcept {
643 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
644 base::str = base::len ? p : empty_string;
645 }
646
650 template<typename T, size_t N = const_lit_for<K, T>::Count>
651 constexpr simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
652
657 constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
658
659 template<StrType<K> T>
660 constexpr simple_str_nt(T&& t) {
661 base::str = t.symbols();
662 base::len = t.length();
663 }
668 template<typename A>
669 constexpr simple_str_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
670
671 static const my_type empty_str;
680 constexpr my_type to_nts(size_t from) {
681 if (from > base::len) {
682 from = base::len;
683 }
684 return {base::str + from, base::len - from};
685 }
686};
687
688template<typename K>
689inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
690
691template<typename K>
692using Splitter = SplitterBase<K, simple_str<K>>;
693
694using ssa = simple_str<u8s>;
695using ssb = simple_str<ubs>;
696using ssw = simple_str<wchar_t>;
697using ssu = simple_str<u16s>;
698using ssuu = simple_str<u32s>;
699using stra = simple_str_nt<u8s>;
700using strb = simple_str_nt<ubs>;
701using strw = simple_str_nt<wchar_t>;
702using stru = simple_str_nt<u16s>;
703using struu = simple_str_nt<u32s>;
704
705template<typename Src, typename Dest>
706struct utf_convert_selector;
707
708template<typename Src>
709struct utf_convert_selector<Src, Src> {
710 static size_t need_len(const Src* src, size_t srcLen) {
711 return srcLen;
712 }
713 static size_t convert(const Src* src, size_t srcLen, Src* dest) {
714 ch_traits<Src>::copy(dest, src, srcLen + 1);
715 return srcLen;
716 }
717};
718
719template<>
720struct utf_convert_selector<u8s, u16s> {
721 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
722 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
723};
724
725template<>
726struct utf_convert_selector<u8s, u32s> {
727 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
728 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
729};
730
731template<>
732struct utf_convert_selector<u16s, u8s> {
733 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
734 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
735};
736
737template<>
738struct utf_convert_selector<u16s, u32s> {
739 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
740 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
741};
742
743template<>
744struct utf_convert_selector<u32s, u8s> {
745 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
746 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
747};
748
749template<>
750struct utf_convert_selector<u32s, u16s> {
751 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
752 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
753};
754
769template<typename K, typename Impl>
770class from_utf_convertible {
771protected:
772 from_utf_convertible() = default;
773 using my_type = Impl;
774 /*
775 Эти методы должен реализовать класс-наследник.
776 вызывается только при создании объекта
777 init(size_t size)
778 set_size(size_t size)
779 */
780 template<typename O>
781 requires(!std::is_same_v<O, K>)
782 void init_from_utf_convertible(simple_str<O> init) {
783 using from_t = to_base_char_t<O>;
784 using to_t = to_base_char_t<K>;
785
786 using worker = utf_convert_selector<from_t, to_t>;
787 Impl* d = static_cast<Impl*>(this);
788 size_t len = init.length();
789 if (!len)
790 d->create_empty();
791 else {
792 size_t need = worker::need_len((const from_t*)init.symbols(), len);
793 K* str = d->init(need);
794 str[need] = 0;
795 worker::convert((const from_t*)init.symbols(), len, (to_t*)str);
796 }
797 }
798};
799
808template<typename From, typename To> requires (!std::is_same_v<From, To>)
809struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
810 using symb_type = To;
811 using from_t = to_base_char_t<From>;
812 using to_t = to_base_char_t<To>;
813 using worker = utf_convert_selector<from_t, to_t>;
814
815 simple_str<From> source_;
816
817 constexpr expr_utf(simple_str<From> source) : source_(source){}
818
819 size_t length() const noexcept {
820 return worker::need_len((const from_t*)source_.symbols(), source_.length());
821 }
822 To* place(To* ptr) const noexcept {
823 return ptr + worker::convert((const from_t*)source_.symbols(), source_.length(), (to_t*)ptr);
824 }
825};
826
839template<typename To, typename From> requires (!std::is_same_v<From, To>)
841 return {from};
842}
843
848template<typename A, typename K>
849concept storable_str = requires {
850 A::is_str_storable == true;
851 std::is_same_v<typename A::symb_type, K>;
852};
853
858template<typename A, typename K>
859concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
860
865template<typename A, typename K>
867
910template<typename K, typename Impl, typename Allocator>
911class str_storable : protected Allocator {
912public:
913 using my_type = Impl;
914 using traits = ch_traits<K>;
915 using allocator_t = Allocator;
916 using s_str = simple_str<K>;
917 using s_str_nt = simple_str_nt<K>;
918
919protected:
924 constexpr allocator_t& allocator() {
925 return *static_cast<Allocator*>(this);
926 }
927 constexpr const allocator_t& allocator() const {
928 return *static_cast<const Allocator*>(this);
929 }
930
931 using uni = unicode_traits<K>;
932
933 constexpr Impl& d() noexcept {
934 return *static_cast<Impl*>(this);
935 }
936 constexpr const Impl& d() const noexcept {
937 return *static_cast<const Impl*>(this);
938 }
939
946 template<typename... Args>
947 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
948
955 constexpr void init_from_str_other(s_str other) {
956 if (other.length()) {
957 K* ptr = d().init(other.length());
958 traits::copy(ptr, other.symbols(), other.length());
959 ptr[other.length()] = 0;
960 } else
961 d().create_empty();
962 }
963
971 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
972 size_t l = pattern.length(), allLen = l * repeat;
973 if (allLen) {
974 K* ptr = d().init(allLen);
975 for (size_t i = 0; i < repeat; i++) {
976 traits::copy(ptr, pattern.symbols(), l);
977 ptr += l;
978 }
979 *ptr = 0;
980 } else
981 d().create_empty();
982 }
983
991 constexpr void init_symb_repeat(size_t count, K pad) {
992 if (count) {
993 K* str = d().init(count);
994 traits::assign(str, count, pad);
995 str[count] = 0;
996 } else
997 d().create_empty();
998 }
999
1011 template<StrExprForType<K> A>
1012 constexpr void init_str_expr(const A& expr) {
1013 size_t len = expr.length();
1014 if (len)
1015 *expr.place((typename A::symb_type*)d().init(len)) = 0;
1016 else
1017 d().create_empty();
1018 }
1019
1033 template<StrType<K> From>
1034 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
1035 auto findes = f.find_all(pattern, offset, maxCount);
1036 if (!findes.size()) {
1037 new (this) my_type{f};
1038 return;
1039 }
1040 size_t srcLen = f.length();
1041 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
1042
1043 if (!newSize) {
1044 new (this) my_type{};
1045 return;
1046 }
1047
1048 K* ptr = d().init(newSize);
1049 const K* src = f.symbols();
1050 size_t from = 0;
1051 for (const auto& s: findes) {
1052 size_t copyLen = s - from;
1053 if (copyLen) {
1054 traits::copy(ptr, src + from, copyLen);
1055 ptr += copyLen;
1056 }
1057 if (repl.len) {
1058 traits::copy(ptr, repl.str, repl.len);
1059 ptr += repl.len;
1060 }
1061 from = s + pattern.len;
1062 }
1063 srcLen -= from;
1064 if (srcLen) {
1065 traits::copy(ptr, src + from, srcLen);
1066 ptr += srcLen;
1067 }
1068 *ptr = 0;
1069 }
1070
1071 template<StrType<K> From, typename Op1, typename... Args>
1072 requires std::is_constructible_v<allocator_t, Args...>
1073 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
1074 my_type result{std::forward<Args>(args)...};
1075 size_t len = f.length();
1076 if (len) {
1077 const K* source = f.symbols();
1078 K* destination = result.init(len);
1079 while(len--) {
1080 *destination++ = opMakeNeedCase(*source++);
1081 }
1082 *destination = 0;
1083 }
1084 return result;
1085 }
1086 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
1087 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
1088 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
1089 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
1090 template<typename T, bool Dummy = true>
1091 struct ChangeCase {
1092 template<typename From, typename Op1, typename... Args>
1093 requires std::is_constructible_v<allocator_t, Args...>
1094 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1095 my_type result{std::forward<Args>(args)...};
1096 size_t len = f.length();
1097 if (len) {
1098 opChangeCase(f.symbols(), len, result.init(len));
1099 }
1100 return result;
1101 }
1102 };
1103 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
1104 // For utf8 we will make a separate specification, since changing the register may change the length of the string
1105 template<bool Dummy>
1106 struct ChangeCase<u8s, Dummy> {
1107 template<typename From, typename Op1, typename... Args>
1108 requires std::is_constructible_v<allocator_t, Args...>
1109 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1110 my_type result{std::forward<Args>(args)...};
1111 ;
1112 size_t len = f.length();
1113 if (len) {
1114 const K* ptr = f.symbols();
1115 K* pWrite = result.init(len);
1116
1117 const u8s* source = ptr;
1118 u8s* dest = pWrite;
1119 size_t newLen = opChangeCase(source, len, dest, len);
1120 if (newLen < len) {
1121 // Строка просто укоротилась
1122 // The string was simply shortened
1123 result.set_size(newLen);
1124 } else if (newLen > len) {
1125 // Строка не влезла в буфер.
1126 // The string did not fit into the buffer.
1127 size_t readed = static_cast<size_t>(source - ptr);
1128 size_t writed = static_cast<size_t>(dest - pWrite);
1129 pWrite = result.set_size(newLen);
1130 dest = pWrite + writed;
1131 opChangeCase(source, len - readed, dest, newLen - writed);
1132 }
1133 pWrite[newLen] = 0;
1134 }
1135 return result;
1136 }
1137 };
1138
1139public:
1140
1141 inline static constexpr bool is_str_storable = true;
1148 constexpr operator const K*() const noexcept {
1149 return d().symbols();
1150 }
1151
1159 constexpr s_str_nt to_nts(size_t from = 0) const {
1160 size_t len = d().length();
1161 if (from >= len) {
1162 from = len;
1163 }
1164 return {d().symbols() + from, len - from};
1165 }
1166
1172 constexpr operator s_str_nt() const {
1173 return {d().symbols(), d().length()};
1174 }
1175
1213 template<typename T, typename... Args>
1214 requires std::is_constructible_v<allocator_t, Args...>
1215 static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
1216 my_type result(std::forward<Args>(args)...);
1217 if (strings.size()) {
1218 if (strings.size() == 1 && (!delimiter.length() || !tail)) {
1219 result = strings.front();
1220 } else {
1221 size_t commonLen = 0;
1222 for (const auto& t: strings) {
1223 size_t len = t.length();
1224 if (len > 0 || !skip_empty) {
1225 if (commonLen > 0) {
1226 commonLen += delimiter.len;
1227 }
1228 commonLen += len;
1229 }
1230 }
1231 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1232 if (commonLen) {
1233 K* ptr = result.init(commonLen);
1234 K* write = ptr;
1235 for (const auto& t: strings) {
1236 size_t copyLen = t.length();
1237 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1238 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1239 write += delimiter.len;
1240 }
1241 ch_traits<K>::copy(write, t.symbols(), copyLen);
1242 write += copyLen;
1243 }
1244 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1245 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1246 write += delimiter.len;
1247 }
1248 *write = 0;
1249 } else {
1250 result.create_empty();
1251 }
1252 }
1253 }
1254 return result;
1255 }
1256
1264 template<StrType<K> From, typename... Args>
1265 requires std::is_constructible_v<allocator_t, Args...>
1266 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
1267 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
1268 }
1269
1277 template<StrType<K> From, typename... Args>
1278 requires std::is_constructible_v<allocator_t, Args...>
1279 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
1280 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
1281 }
1282
1294 template<StrType<K> From, typename... Args>
1295 requires std::is_constructible_v<allocator_t, Args...>
1296 static my_type upperred_from(const From& f, Args&&... args) {
1297 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
1298 }
1299
1311 template<StrType<K> From, typename... Args>
1312 requires std::is_constructible_v<allocator_t, Args...>
1313 static my_type lowered_from(const From& f, Args&&... args) {
1314 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
1315 }
1316
1332 template<StrType<K> From, typename... Args>
1333 requires std::is_constructible_v<allocator_t, Args...>
1334 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
1335 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1336 }
1337};
1338
1343template<typename A>
1344concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
1345 { a.allocate(size) } -> std::same_as<void*>;
1346 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
1347};
1348
1349struct printf_selector {
1350 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
1351 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
1352 if constexpr (sizeof(K) == 1) {
1353 #ifndef _WIN32
1354 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1355 #else
1356 // Поддерживает позиционные параметры
1357 // Supports positional parameters
1358 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1359 #endif
1360 } else {
1361 #ifndef _WIN32
1362 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1363 #else
1364 // Поддерживает позиционные параметры
1365 // Supports positional parameters
1366 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1367 #endif
1368 }
1369 }
1370 template<typename K> requires (is_one_of_std_char_v<K>)
1371 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
1372 if constexpr (std::is_same_v<K, u8s>) {
1373 #ifndef _WIN32
1374 return std::vsnprintf(buffer, count, format, args);
1375 #else
1376 // Поддерживает позиционные параметры
1377 // Supports positional parameters
1378 return _vsprintf_p(buffer, count, format, args);
1379 #endif
1380 } else {
1381 #ifndef _WIN32
1382 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1383 #else
1384 // Поддерживает позиционные параметры
1385 // Supports positional parameters
1386 return _vswprintf_p(buffer, count, format, args);
1387 #endif
1388 }
1389 }
1390};
1391
1392inline size_t grow2(size_t ret, size_t currentCapacity) {
1393 return ret <= currentCapacity ? ret : ret * 2;
1394}
1395
1434template<typename K, typename Impl>
1436public:
1437 using my_type = Impl;
1438
1439private:
1440 Impl& d() {
1441 return *static_cast<Impl*>(this);
1442 }
1443 const Impl& d() const {
1444 return *static_cast<const Impl*>(this);
1445 }
1446 size_t _len() const noexcept {
1447 return d().length();
1448 }
1449 const K* _str() const noexcept {
1450 return d().symbols();
1451 }
1452 using str_piece = simple_str<K>;
1453 using symb_type = K;
1454 using traits = ch_traits<K>;
1455 using uni = unicode_traits<K>;
1456 using uns_type = std::make_unsigned_t<K>;
1457
1458 template<typename Op>
1459 Impl& make_trim_op(const Op& op) {
1460 str_piece me = d(), pos = op(me);
1461 if (me.length() != pos.length()) {
1462 if (me.symbols() != pos.symbols())
1463 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
1464 d().set_size(pos.length());
1465 }
1466 return d();
1467 }
1468
1469 template<auto Op>
1470 Impl& commonChangeCase() {
1471 size_t len = _len();
1472 if (len)
1473 Op(_str(), len, str());
1474 return d();
1475 }
1476 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
1477 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
1478 // GCC still does not allow full specialization of a nested class within a class,
1479 // only partial. Resources additive unused parameter template.
1480 template<typename T, bool Dummy = true>
1481 struct CaseTraits {
1482 static Impl& upper(Impl& obj) {
1483 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1484 }
1485 static Impl& lower(Impl& obj) {
1486 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1487 }
1488 };
1489
1490 template<auto Op>
1491 Impl& utf8CaseChange() {
1492 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1493 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1494 size_t len = _len();
1495 if (len) {
1496 u8s* writePos = str();
1497 const u8s *startData = writePos, *readPos = writePos;
1498 size_t newLen = Op(readPos, len, writePos, len);
1499 if (newLen < len) {
1500 // Строка просто укоротилась
1501 // The string was simply shortened
1502 d().set_size(newLen);
1503 } else if (newLen > len) {
1504 // Строка не влезла в буфер.
1505 // The string did not fit into the buffer.
1506 size_t readed = static_cast<size_t>(readPos - startData);
1507 size_t writed = static_cast<size_t>(writePos - startData);
1508 d().set_size(newLen);
1509 startData = str(); // при изменении размера могло изменится | may change when resizing
1510 readPos = startData + readed;
1511 writePos = const_cast<u8s*>(startData) + writed;
1512 Op(readPos, len - readed, writePos, newLen - writed);
1513 }
1514 }
1515 return d();
1516 }
1517 template<bool Dummy>
1518 struct CaseTraits<u8s, Dummy> {
1519 static Impl& upper(Impl& obj) {
1520 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1521 }
1522 static Impl& lower(Impl& obj) {
1523 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1524 }
1525 };
1526
1527 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1528 Impl& makeTrim(T&& pattern) {
1529 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1530 }
1531
1532 template<TrimSides S, bool withSpaces>
1533 Impl& makeTrim(str_piece pattern) {
1534 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1535 }
1536
1537public:
1544 K* str() noexcept {
1545 return d().str();
1546 }
1547
1553 explicit operator K*() noexcept {
1554 return str();
1555 }
1556
1562 Impl& trim() {
1563 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1564 }
1565
1571 Impl& trim_left() {
1572 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1573 }
1574
1580 Impl& trim_right() {
1581 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1582 }
1583
1591 template<typename T, size_t N = const_lit_for<K, T>::Count>
1592 requires is_const_pattern<N>
1593 Impl& trim(T&& pattern) {
1594 return makeTrim<TrimSides::TrimAll, false>(pattern);
1595 }
1596
1604 template<typename T, size_t N = const_lit_for<K, T>::Count>
1605 requires is_const_pattern<N>
1606 Impl& trim_left(T&& pattern) {
1607 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1608 }
1609
1617 template<typename T, size_t N = const_lit_for<K, T>::Count>
1618 requires is_const_pattern<N>
1619 Impl& trim_right(T&& pattern) {
1620 return makeTrim<TrimSides::TrimRight, false>(pattern);
1621 }
1622
1630 template<typename T, size_t N = const_lit_for<K, T>::Count>
1631 requires is_const_pattern<N>
1632 Impl& trim_with_spaces(T&& pattern) {
1633 return makeTrim<TrimSides::TrimAll, true>(pattern);
1634 }
1635
1643 template<typename T, size_t N = const_lit_for<K, T>::Count>
1644 requires is_const_pattern<N>
1645 Impl& trim_left_with_spaces(T&& pattern) {
1646 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1647 }
1648
1656 template<typename T, size_t N = const_lit_for<K, T>::Count>
1657 requires is_const_pattern<N>
1658 Impl& trim_right_with_wpaces(T&& pattern) {
1659 return makeTrim<TrimSides::TrimRight, true>(pattern);
1660 }
1661
1669 Impl& trim(str_piece pattern) {
1670 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1671 }
1672
1680 Impl& trim_left(str_piece pattern) {
1681 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1682 }
1683
1691 Impl& trim_right(str_piece pattern) {
1692 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1693 }
1694
1702 Impl& trim_with_spaces(str_piece pattern) {
1703 return makeTrim<TrimSides::TrimAll, true>(pattern);
1704 }
1705
1713 Impl& trim_left_with_spaces(str_piece pattern) {
1714 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1715 }
1716
1724 Impl& trim_right_with_spaces(str_piece pattern) {
1725 return makeTrim<TrimSides::TrimRight, true>(pattern);
1726 }
1727
1734 K* ptr = str();
1735 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1736 K s = *ptr;
1737 if (isAsciiLower(s))
1738 *ptr = s & ~0x20;
1739 }
1740 return d();
1741 }
1742
1749 K* ptr = str();
1750 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1751 K s = *ptr;
1752 if (isAsciiUpper(s))
1753 *ptr = s | 0x20;
1754 }
1755 return d();
1756 }
1757
1767 Impl& upper() {
1768 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1769 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1770 return CaseTraits<K>::upper(d());
1771 }
1772
1782 Impl& lower() {
1783 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1784 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1785 return CaseTraits<K>::lower(d());
1786 }
1787
1788private:
1789 template<typename T>
1790 Impl& changeImpl(size_t from, size_t len, T expr) {
1791 size_t myLen = _len();
1792 if (from > myLen) {
1793 from = myLen;
1794 }
1795 if (from + len > myLen) {
1796 len = myLen - from;
1797 }
1798 K* buffer = str();
1799 size_t otherLen = expr.length();
1800 if (len == otherLen) {
1801 expr.place(buffer + from);
1802 } else {
1803 size_t tailLen = myLen - from - len;
1804 if (len > otherLen) {
1805 expr.place(buffer + from);
1806 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1807 d().set_size(myLen - (len - otherLen));
1808 } else {
1809 buffer = d().set_size(myLen + otherLen - len);
1810 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1811 expr.place(buffer + from);
1812 }
1813 }
1814 return d();
1815 }
1816
1817 template<typename T>
1818 Impl& appendImpl(T expr) {
1819 if (size_t len = expr.length(); len) {
1820 size_t size = _len();
1821 expr.place(d().set_size(size + len) + size);
1822 }
1823 return d();
1824 }
1825
1826 template<typename T>
1827 Impl& appendFromImpl(size_t pos, T expr) {
1828 if (pos > _len())
1829 pos = _len();
1830 if (size_t len = expr.length())
1831 expr.place(d().set_size(pos + len) + pos);
1832 else
1833 d().set_size(pos);
1834 return d();
1835 }
1836
1837public:
1838 inline static constexpr bool is_str_mutable = true;
1847 Impl& append(str_piece other) {
1848 return appendImpl<str_piece>(other);
1849 }
1850
1858 template<StrExprForType<K> A>
1859 Impl& append(const A& expr) {
1860 return appendImpl<const A&>(expr);
1861 }
1862
1870 Impl& operator+=(str_piece other) {
1871 return appendImpl<str_piece>(other);
1872 }
1873
1881 template<StrExprForType<K> A>
1882 Impl& operator+=(const A& expr) {
1883 return appendImpl<const A&>(expr);
1884 }
1885
1899 Impl& append_in(size_t pos, str_piece other) {
1900 return appendFromImpl<str_piece>(pos, other);
1901 }
1902
1916 template<StrExprForType<K> A>
1917 Impl& append_in(size_t pos, const A& expr) {
1918 return appendFromImpl<const A&>(pos, expr);
1919 }
1920
1932 Impl& change(size_t from, size_t len, str_piece other) {
1933 return changeImpl<str_piece>(from, len, other);
1934 }
1935
1947 template<StrExprForType<K> A>
1948 Impl& change(size_t from, size_t len, const A& expr) {
1949 return changeImpl<const A&>(from, len, expr);
1950 }
1951
1961 Impl& insert(size_t to, str_piece other) {
1962 return changeImpl<str_piece>(to, 0, other);
1963 }
1964
1974 template<StrExprForType<K> A>
1975 Impl& insert(size_t to, const A& expr) {
1976 return changeImpl<const A&>(to, 0, expr);
1977 }
1978
1988 Impl& remove(size_t from, size_t len) {
1989 return changeImpl<const empty_expr<K>&>(from, len, {});
1990 }
1991
1999 Impl& prepend(str_piece other) {
2000 return changeImpl<str_piece>(0, 0, other);
2001 }
2002
2010 template<StrExprForType<K> A>
2011 Impl& prepend(const A& expr) {
2012 return changeImpl<const A&>(0, 0, expr);
2013 }
2014
2028 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2029 offset = d().find(pattern, offset);
2030 if (offset == str::npos) {
2031 return d();
2032 }
2033 if (!maxCount)
2034 maxCount--;
2035 size_t replLength = repl.length(), patternLength = pattern.length();
2036
2037 if (patternLength == replLength) {
2038 // Заменяем inplace на подстроку такой же длины
2039 // Replace inplace with a substring of the same length
2040 K* ptr = str();
2041 while (maxCount--) {
2042 traits::copy(ptr + offset, repl.symbols(), replLength);
2043 offset = d().find(pattern, offset + replLength);// replLength == patternLength
2044 if (offset == str::npos)
2045 break;
2046 }
2047 } else if (patternLength > replLength) {
2048 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
2049 // Replace with a shorter piece, the length of the text will decrease, go from left to right
2050 K* ptr = str();
2051 traits::copy(ptr + offset, repl.symbols(), replLength);
2052 size_t posWrite = offset + replLength;
2053 offset += patternLength;
2054
2055 while (--maxCount) {
2056 size_t idx = d().find(pattern, offset);
2057 if (idx == str::npos)
2058 break;
2059 size_t lenOfPiece = idx - offset;
2060 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
2061 posWrite += lenOfPiece;
2062 traits::copy(ptr + posWrite, repl.symbols(), replLength);
2063 posWrite += replLength;
2064 offset = idx + patternLength;
2065 }
2066 size_t tailLen = _len() - offset;
2067 traits::move(ptr + posWrite, ptr + offset, tailLen);
2068 d().set_size(posWrite + tailLen);
2069 } else {
2070 struct replace_grow_helper {
2071 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
2072 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
2073 my_type& source;
2074 const str_piece pattern;
2075 const str_piece repl;
2076 size_t maxCount;
2077 const size_t delta;
2078 size_t all_delta{};
2079 K* reserve_for_copy{};
2080 size_t end_of_piece{};
2081 size_t total_length{};
2082
2083 void replace(size_t offset) {
2084 size_t found[16] = {offset};
2085 maxCount--;
2086 offset += pattern.length();
2087 all_delta += delta;
2088 size_t idx = 1;
2089 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
2090 found[idx] = source.find(pattern, offset);
2091 if (found[idx] == str::npos) {
2092 break;
2093 }
2094 offset = found[idx] + pattern.length();
2095 all_delta += delta;
2096 }
2097 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
2098 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
2099 }
2100 // Теперь делаем свои замены
2101 // Now we make our replacements
2102 if (!reserve_for_copy) {
2103 // Только начинаем
2104 // Just getting started
2105 end_of_piece = source.length();
2106 total_length = end_of_piece + all_delta;
2107 reserve_for_copy = source.alloc_for_copy(total_length);
2108 }
2109 K* dst_start = reserve_for_copy;
2110 const K* src_start = source.symbols();
2111 while(idx-- > 0) {
2112 size_t pos = found[idx] + pattern.length();
2113 size_t lenOfPiece = end_of_piece - pos;
2114 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2115 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
2116 all_delta -= delta;
2117 end_of_piece = found[idx];
2118 }
2119 if (!all_delta && reserve_for_copy != src_start) {
2120 ch_traits<K>::copy(dst_start, src_start, found[0]);
2121 }
2122 }
2123 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
2124 helper.replace(offset);
2125 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2126 }
2127 return d();
2128 }
2129
2145 template<StrType<K> From>
2146 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2147 if (pattern.length() >= repl.length()) {
2148 K* dst = d().reserve_no_preserve(f.length());
2149 const K* src = f.symbols();
2150 size_t delta = 0;
2151 if (maxCount == 0) {
2152 maxCount--;
2153 }
2154 size_t src_length = f.length(), start = 0;
2155 while (maxCount--) {
2156 offset = f.find(pattern, offset);
2157 if (offset == str::npos) {
2158 break;
2159 }
2160 size_t piece_len = offset - start;
2161 if (piece_len) {
2162 ch_traits<K>::copy(dst, src + start, piece_len);
2163 dst += piece_len;
2164 }
2165 if (repl.length()) {
2166 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
2167 dst += repl.length();
2168 }
2169 delta += pattern.length() - repl.length();
2170 offset += pattern.length();
2171 start = offset;
2172 }
2173 if (start < src_length) {
2174 ch_traits<K>::copy(dst, src + start, src_length - start);
2175 }
2176 d().set_size(src_length - delta);
2177 } else {
2178 d() = f;
2179 replace(pattern, repl, offset, maxCount);
2180 }
2181 return d();
2182 }
2183
2207 template<typename Op>
2208 Impl& fill(size_t from, const Op& fillFunction) {
2209 size_t size = _len();
2210 if (from > size)
2211 from = size;
2212 size_t capacity = d().capacity();
2213 K* ptr = str();
2214 capacity -= from;
2215 for (;;) {
2216 size_t needSize = static_cast<size_t>(fillFunction(ptr + from, capacity));
2217 if (capacity >= needSize) {
2218 d().set_size(from + needSize);
2219 break;
2220 }
2221 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2222 capacity = d().capacity() - from;
2223 }
2224 return d();
2225 }
2226
2234 template<typename Op>
2235 requires std::is_invocable_v<Op, K*, size_t>
2236 Impl& operator<<(const Op& fillFunction) {
2237 return fill(0, fillFunction);
2238 }
2239
2247 template<typename Op>
2248 requires std::is_invocable_v<Op, K*, size_t>
2249 Impl& operator<<=(const Op& fillFunction) {
2250 return fill(_len(), fillFunction);
2251 }
2252
2260 template<typename Op>
2261 requires std::is_invocable_v<Op, my_type&>
2262 Impl& operator<<(const Op& fillFunction) {
2263 fillFunction(d());
2264 return d();
2265 }
2266
2280 template<typename... T> requires (is_one_of_std_char_v<K>)
2281 Impl& printf_from(size_t from, const K* format, T&&... args) {
2282 size_t size = _len();
2283 if (from > size)
2284 from = size;
2285 size_t capacity = d().capacity();
2286 K* ptr = str();
2287 capacity -= from;
2288
2289 int result = 0;
2290 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
2291 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
2292 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
2293 // Here's a dirty hack for u8s and wide_char. u8s version of snprintf immediately returns the size of the required buffer if it is small
2294 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
2295 // Therefore, for them you need to stupidly increase the buffer at random until it fits
2296 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
2297 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2298 if (result > (int)capacity) {
2299 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2300 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
2301 }
2302 } else {
2303 for (;;) {
2304 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2305 if (result < 0) {
2306 // Не хватило буфера или ошибка конвертации.
2307 // Попробуем увеличить буфер в два раза
2308 // Not enough buffer or conversion error.
2309 // Let's try to double the buffer
2310 capacity *= 2;
2311 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2312 } else
2313 break;
2314 }
2315 }
2316 if (result < 0)
2317 d().set_size(static_cast<size_t>(traits::length(_str())));
2318 else
2319 d().set_size(from + result);
2320 return d();
2321 }
2322
2334 template<typename... T> requires (is_one_of_std_char_v<K>)
2335 Impl& printf(const K* format, T&&... args) {
2336 return printf_from(0, format, std::forward<T>(args)...);
2337 }
2338
2350 template<typename... T> requires (is_one_of_std_char_v<K>)
2351 Impl& append_printf(const K* format, T&&... args) {
2352 return printf_from(_len(), format, std::forward<T>(args)...);
2353 }
2354
2355 struct writer {
2356 my_type* store;
2357 K* ptr;
2358 const K* end;
2359 size_t max_write;
2360 size_t writed = 0;
2361 inline static K pad;
2362 K& operator*() const {
2363 return *ptr;
2364 }
2365 writer& operator++() {
2366 if (writed < max_write) {
2367 ++ptr;
2368 if (ptr == end) {
2369 size_t l = ptr - store->begin();
2370 store->set_size(l);
2371 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
2372 end = store->end();
2373 }
2374 } else {
2375 ptr = &pad;
2376 }
2377 return *this;
2378 }
2379 writer operator++(int) {
2380 auto ret = *this;
2381 operator++();
2382 return ret;
2383 }
2384
2385 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2386 writer() = default;
2387 writer(const writer&) = delete;
2388 writer& operator=(const writer&) noexcept = delete;
2389 writer(writer&&) noexcept = default;
2390 writer& operator=(writer&&) noexcept = default;
2391 using difference_type = int;
2392 };
2393 using fmt_type = to_std_char_t<K>;
2408 template<typename... T> requires (is_one_of_std_char_v<K>)
2409 Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
2410 size_t size = _len();
2411 if (from > size)
2412 from = size;
2413 size_t capacity = d().capacity();
2414 K* ptr = str();
2415
2416 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2417 std::forward<decltype(format)>(format), std::forward<T>(args)...);
2418 d().set_size(result.ptr - _str());
2419 return d();
2420 }
2421
2437 template<typename... T> requires (is_one_of_std_char_v<K>)
2438 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
2439 size_t size = _len();
2440 if (from > size)
2441 from = size;
2442 size_t capacity = d().capacity();
2443 K* ptr = str();
2444
2445 if constexpr (std::is_same_v<K, u8s>) {
2446 auto result = std::vformat_to(
2447 writer{d(), ptr + from, ptr + capacity, max_write},
2448 std::basic_string_view<K>{format.symbols(), format.length()},
2449 std::make_format_args(args...));
2450 d().set_size(result.ptr - _str());
2451 } else {
2452 auto result = std::vformat_to(
2453 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2454 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
2455 std::make_wformat_args(std::forward<T>(args)...));
2456 d().set_size(result.ptr - _str());
2457 }
2458 return d();
2459 }
2460
2472 template<typename... T> requires (is_one_of_std_char_v<K>)
2473 Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
2474 return format_from(0, pattern, std::forward<T>(args)...);
2475 }
2476
2488 template<typename... T> requires (is_one_of_std_char_v<K>)
2489 Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
2490 return format_from(_len(), format, std::forward<T>(args)...);
2491 }
2492
2504 template<typename... T> requires (is_one_of_std_char_v<K>)
2505 Impl& vformat(str_piece format, T&&... args) {
2506 return vformat_from(0, -1, format, std::forward<T>(args)...);
2507 }
2508
2520 template<typename... T> requires (is_one_of_std_char_v<K>)
2521 Impl& append_vformatted(str_piece format, T&&... args) {
2522 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
2523 }
2524
2538 template<typename... T> requires (is_one_of_std_char_v<K>)
2539 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
2540 return vformat_from(0, max_write, format, std::forward<T>(args)...);
2541 }
2542
2556 template<typename... T> requires (is_one_of_std_char_v<K>)
2557 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
2558 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
2559 }
2560
2570 template<typename Op, typename... Args>
2571 Impl& with(const Op& fillFunction, Args&&... args) {
2572 fillFunction(d(), std::forward<Args>(args)...);
2573 return d();
2574 }
2575};
2576
2577template<typename K>
2578struct SharedStringData {
2579 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
2580
2581 SharedStringData() {
2582 ref_ = 1;
2583 }
2584 K* str() const {
2585 return (K*)(this + 1);
2586 }
2587 void incr() {
2588 ref_.fetch_add(1, std::memory_order_relaxed);
2589 }
2590 void decr(Allocatorable auto& allocator) {
2591 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2592 if (val == 1) {
2593 allocator.deallocate(this);
2594 }
2595 }
2596 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
2597 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
2598 return new (allocator.allocate(size)) SharedStringData();
2599 }
2600 static SharedStringData<K>* from_str(const K* p) {
2601 return (SharedStringData<K>*)p - 1;
2602 }
2603 K* place(K* p, size_t len) {
2604 ch_traits<K>::copy(p, str(), len);
2605 return p + len;
2606 }
2607};
2608
2609// Дефолтный аллокатор для строк, может работать статически
2610// Default allocator for strings, can work statically
2611class string_common_allocator {
2612public:
2613 void* allocate(size_t bytes) {
2614 return new char[bytes];
2615 }
2616 void deallocate(void* address) noexcept {
2617 delete [] static_cast<char*>(address);
2618 }
2619};
2620
2621string_common_allocator default_string_allocator_selector(...);
2622// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
2623// объявите функцию
2624// ваш_тип_аллокатора default_string_allocator_selector(int);
2625// If you want to set your default allocator for strings, before including sstring.h
2626// declare a function
2627// your_allocator_type default_string_allocator_selector(int);
2628using allocator_string = decltype(default_string_allocator_selector(int(0)));
2629
2630template<typename K, Allocatorable Allocator>
2631class sstring;
2632
2633/*
2634* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
2635* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
2636* Укажем ему явно.
2637* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
2638* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
2639* Let's tell him explicitly.
2640*/
2641
2662template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
2663class decl_empty_bases lstring :
2664 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2665 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2666 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2667 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2668 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2669public:
2670 using symb_type = K;
2672 using allocator_t = Allocator;
2673
2674 enum : size_t {
2676 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
2677 };
2678
2679protected:
2680 enum : size_t {
2681 extra = forShared ? sizeof(SharedStringData<K>) : 0,
2682 };
2683
2684 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
2685 using base_storable = str_storable<K, my_type, Allocator>;
2686 using base_mutable = str_mutable<K, my_type>;
2687 using base_utf = from_utf_convertible<K, my_type>;
2688 using traits = ch_traits<K>;
2689 using s_str = base_storable::s_str;
2690
2691 friend base_storable;
2692 friend base_mutable;
2693 friend base_utf;
2694 friend class sstring<K, Allocator>;
2695
2696 K* data_;
2697 size_t size_;
2698
2699 union {
2700 size_t capacity_;
2701 K local_[LocalCapacity + 1];
2702 };
2703
2704 constexpr void create_empty() {
2705 data_ = local_;
2706 size_ = 0;
2707 local_[0] = 0;
2708 }
2709 constexpr static size_t calc_capacity(size_t s) {
2710 const int al = alignof(std::max_align_t) < 16 ? 16 : alignof(std::max_align_t);
2711 size_t real_need = (s + 1) * sizeof(K) + extra;
2712 size_t aligned_alloced = (real_need + al - 1) / al * al;
2713 return (aligned_alloced - extra) / sizeof(K) - 1;
2714 }
2715
2716 constexpr K* init(size_t s) {
2717 size_t need_cap = s;
2718 if (need_cap > LocalCapacity) {
2719 need_cap = calc_capacity(s);
2720 data_ = alloc_place(need_cap);
2721 capacity_ = need_cap;
2722 } else {
2723 data_ = local_;
2724 }
2725 size_ = s;
2726 return str();
2727 }
2728 // Методы для себя | Methods for yourself
2729 constexpr bool is_alloced() const noexcept {
2730 return data_ != local_;
2731 }
2732
2733 constexpr void dealloc() {
2734 if (is_alloced()) {
2735 base_storable::allocator().deallocate(to_real_address(data_));
2736 data_ = local_;
2737 }
2738 }
2739
2740 constexpr static K* to_real_address(void* ptr) {
2741 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
2742 }
2743 constexpr static K* from_real_address(void* ptr) {
2744 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
2745 }
2746
2747 constexpr K* alloc_place(size_t newSize) {
2748 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
2749 }
2750 // Вызывается при replace, когда меняют на более длинную замену
2751 // Called on replace when changing to a longer replacement
2752 constexpr K* alloc_for_copy(size_t newSize) {
2753 if (capacity() >= newSize) {
2754 // Замена войдёт в текущий буфер
2755 // Replacement will go into the current buffer
2756 return data_;
2757 }
2758 return alloc_place(calc_capacity(newSize));
2759 }
2760 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
2761 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
2762 constexpr void set_from_copy(K* ptr, size_t newSize) {
2763 if (ptr != data_) {
2764 // Да, копировали в новый буфер
2765 // Yes, copied to a new buffer
2766 dealloc();
2767 data_ = ptr;
2768 capacity_ = calc_capacity(newSize);
2769 }
2770 size_ = newSize;
2771 data_[newSize] = 0;
2772 }
2773 constexpr void copy_from_another(K* buf, const K* src, size_t size) {
2774 // Реальный размер буфера всегда кратен sizeof(void*), поэтому копируя по sizeof(void*) байтов, мы не выйдем за пределы буфера
2775 // The actual buffer size is always a multiple of sizeof(void*), so by copying sizeof(void*) bytes at a time, we won't go beyond the buffer's limits.
2776 size_t need_copy_bytes = (size + 1) * sizeof(K);
2777 size_t cnt = (need_copy_bytes + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*);
2778 traits::copy(buf, src, cnt / sizeof(K));
2779 }
2780public:
2787 template<typename... Args>
2788 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
2789 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2790 : base_storable(std::forward<Args>(args)...) {
2791 create_empty();
2792 }
2793
2802 template<typename... Args>
2803 requires std::is_constructible_v<allocator_t, Args...>
2804 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2806 }
2807
2817 template<typename... Args>
2818 requires std::is_constructible_v<allocator_t, Args...>
2819 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2820 base_storable::init_str_repeat(repeat, pattern);
2821 }
2822
2832 template<typename... Args>
2833 requires std::is_constructible_v<allocator_t, Args...>
2834 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2836 }
2837
2851 template<typename... Args>
2852 requires std::is_constructible_v<allocator_t, Args...>
2853 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2855 }
2856
2872 template<StrType<K> From, typename... Args>
2873 requires std::is_constructible_v<allocator_t, Args...>
2874 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2875 : base_storable(std::forward<Args>(args)...) {
2876 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
2877 }
2878
2879 constexpr lstring() {
2880 create_empty();
2881 }
2882
2883 constexpr ~lstring() {
2884 dealloc();
2885 }
2886
2893 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
2894 struct copy{uint64_t p[2];};
2895 constexpr size_t short_str = sizeof(copy) / sizeof(K);
2896 if constexpr (LocalCapacity >= short_str - 1) {
2897 if (other.size_ < short_str) {
2898 data_ = local_;
2899 size_ = other.size_;
2900 *(copy*)local_ = *(const copy*)other.local_;
2901 return;
2902 }
2903 }
2904 if (size_t size = other.size_) {
2905 copy_from_another(init(size), other.symbols(), size);
2906 } else {
2907 create_empty();
2908 }
2909 }
2910
2918 template<typename... Args>
2919 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2920 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2921 if (other.size_) {
2922 copy_from_another(init(other.size_), other.symbols(), other.size_);
2923 } else {
2924 create_empty();
2925 }
2926 }
2927
2935 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
2936 requires std::is_constructible_v<allocator_t, Args...>
2937 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2938 if constexpr (I > 1) {
2939 K* ptr = init(I - 1);
2940 traits::copy(ptr, (const K*)value, I - 1);
2941 ptr[I - 1] = K{};
2942 } else {
2943 create_empty();
2944 }
2945 }
2946
2952 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
2953 if (other.size_) {
2954 size_ = other.size_;
2955 if (other.is_alloced()) {
2956 data_ = other.data_;
2957 other.data_ = other.local_;
2958 capacity_ = other.capacity_;
2959 } else {
2960 data_ = local_;
2961 copy_from_another(data_, other.local_, size_);
2962 }
2963 other.size_ = 0;
2964 other.local_[0] = 0;
2965 } else {
2966 create_empty();
2967 }
2968 }
2969
2977 template<typename Op, typename... Args>
2978 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
2979 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2980 create_empty();
2981 this->operator<<(op);
2982 }
2983 template<typename O>
2984 requires(!std::is_same_v<O, K>)
2985 lstring(simple_str<O> init) {
2986 this->init_from_utf_convertible(init);
2987 }
2988
2989 template<typename O, typename I, bool M>
2990 requires(!std::is_same_v<O, K>)
2991 lstring(const str_algs<O, simple_str<O>, I, M>& init) {
2992 this->init_from_utf_convertible(init.to_str());
2993 }
2994
2995 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
2996 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
2997 // copy and swap for assignments is not very applicable here, since for strings with a large local buffer, an extra copy, even by moving, will be expensive
2998 // Therefore, we implement the copy and move assignment separately
2999
3008 my_type& operator=(const my_type& other) {
3009 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3010 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3011 if (&other != this) {
3012 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
3013 size_ = other.size_;
3014 }
3015 return *this;
3016 }
3017
3025 my_type& operator=(my_type&& other) noexcept {
3026 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3027 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3028 if (&other != this) {
3029 dealloc();
3030 if (other.is_alloced()) {
3031 data_ = other.data_;
3032 capacity_ = other.capacity_;
3033 } else {
3034 traits::copy(data_, other.local_, other.size_ + 1);
3035 }
3036 base_storable::allocator() = std::move(other.allocator());
3037 size_ = other.size_;
3038 other.create_empty();
3039 }
3040 return *this;
3041 }
3042
3043 my_type& assign(const K* other, size_t len) {
3044 if (len) {
3045 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3046 if (isIntersect) {
3047 // Особый случай, нам пытаются присвоить кусок нашей же строки.
3048 // Просто переместим текст в буфере, и установим новый размер
3049 // A special case, they are trying to assign us a piece of our own string.
3050 // Just move the text in the buffer and set a new size
3051 if (other > data_) {
3052 traits::move(data_, other, len);
3053 }
3054 } else {
3055 traits::copy(reserve_no_preserve(len), other, len);
3056 }
3057 }
3058 size_ = len;
3059 data_[size_] = 0;
3060 return *this;
3061 }
3070 my_type& operator=(simple_str<K> other) {
3071 return assign(other.str, other.len);
3072 }
3073
3081 template<typename T, size_t S = const_lit_for<K, T>::Count>
3082 my_type& operator=(T&& other) {
3083 return assign((const K*)other, S - 1);
3084 }
3085
3095 my_type& operator=(const StrExprForType<K> auto& expr) {
3096 size_t newLen = expr.length();
3097 if (newLen) {
3098 expr.place(reserve_no_preserve(newLen));
3099 }
3100 size_ = newLen;
3101 data_[size_] = 0;
3102 return *this;
3103 }
3104
3105 constexpr size_t length() const noexcept {
3106 return size_;
3107 }
3108
3109 constexpr const K* symbols() const noexcept {
3110 return data_;
3111 }
3112
3113 constexpr K* str() noexcept {
3114 return data_;
3115 }
3116
3117 constexpr bool is_empty() const noexcept {
3118 return size_ == 0;
3119 }
3120
3121 constexpr bool empty() const noexcept {
3122 return size_ == 0;
3123 }
3124
3125 constexpr size_t capacity() const noexcept {
3126 return is_alloced() ? capacity_ : LocalCapacity;
3127 }
3128
3140 constexpr K* reserve_no_preserve(size_t newSize) {
3141 if (newSize > capacity()) {
3142 newSize = calc_capacity(newSize);
3143 K* newData = alloc_place(newSize);
3144 dealloc();
3145 data_ = newData;
3146 capacity_ = newSize;
3147 }
3148 return data_;
3149 }
3150
3162 constexpr K* reserve(size_t newSize) {
3163 if (newSize > capacity()) {
3164 newSize = calc_capacity(newSize);
3165 K* newData = alloc_place(newSize);
3166 traits::copy(newData, data_, size_);
3167 dealloc();
3168 data_ = newData;
3169 capacity_ = newSize;
3170 }
3171 return data_;
3172 }
3173
3185 constexpr K* set_size(size_t newSize) {
3186 size_t cap = capacity();
3187 if (newSize > cap) {
3188 size_t needPlace = newSize;
3189 if (needPlace < (cap + 1) * 2) {
3190 needPlace = (cap + 1) * 2 - 1;
3191 }
3192 reserve(needPlace);
3193 }
3194 size_ = newSize;
3195 data_[newSize] = 0;
3196 return data_;
3197 }
3198
3202 constexpr bool is_local() const noexcept {
3203 return !is_alloced();
3204 }
3205
3211 constexpr void define_size() {
3212 size_t cap = capacity();
3213 for (size_t i = 0; i < cap; i++) {
3214 if (data_[i] == 0) {
3215 size_ = i;
3216 return;
3217 }
3218 }
3219 size_ = cap;
3220 data_[size_] = 0;
3221 }
3222
3228 constexpr void shrink_to_fit() {
3229 size_t need_capacity = calc_capacity(size_);
3230 if (is_alloced() && capacity_ > need_capacity) {
3231 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
3232 traits::copy(newData, data_, size_ + 1);
3233 base_storable::allocator().deallocate(to_real_address(data_));
3234 data_ = newData;
3235
3236 if (size_ > LocalCapacity) {
3237 capacity_ = need_capacity;
3238 }
3239 }
3240 }
3241
3242 constexpr void clear() {
3243 set_size(0);
3244 }
3245
3246 constexpr void reset() {
3247 dealloc();
3248 local_[0] = 0;
3249 size_ = 0;
3250 }
3251};
3252
3253template<size_t N = 15>
3254using lstringa = lstring<u8s, N>;
3255template<size_t N = 15>
3256using lstringb = lstring<ubs, N>;
3257template<size_t N = 15>
3258using lstringw = lstring<wchar_t, N>;
3259template<size_t N = 15>
3260using lstringu = lstring<u16s, N>;
3261template<size_t N = 15>
3262using lstringuu = lstring<u32s, N>;
3263
3264template<size_t N = 15>
3265using lstringsa = lstring<u8s, N, true>;
3266template<size_t N = 15>
3267using lstringsb = lstring<ubs, N, true>;
3268template<size_t N = 15>
3269using lstringsw = lstring<wchar_t, N, true>;
3270template<size_t N = 15>
3271using lstringsu = lstring<u16s, N, true>;
3272template<size_t N = 15>
3273using lstringsuu = lstring<u32s, N, true>;
3274
3275
3276template<typename T, typename K = typename const_lit<T>::symb_type>
3277auto getLiteralType(T&&) {
3278 return K{};
3279};
3280
3281template<size_t Arch, size_t L>
3282inline constexpr const size_t _local_count = 0;
3283
3284template<>
3285inline constexpr const size_t _local_count<8, 1> = 23;
3286template<>
3287inline constexpr const size_t _local_count<8, 2> = 15;
3288template<>
3289inline constexpr const size_t _local_count<8, 4> = 7;
3290template<>
3291inline constexpr const size_t _local_count<4, 1> = 15;
3292template<>
3293inline constexpr const size_t _local_count<4, 2> = 11;
3294template<>
3295inline constexpr const size_t _local_count<4, 4> = 5;
3296
3297template<typename T>
3298constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
3299
3352template<typename K, Allocatorable Allocator = allocator_string>
3353class decl_empty_bases sstring :
3354 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3355 public str_storable<K, sstring<K, Allocator>, Allocator>,
3356 public null_terminated<K, sstring<K, Allocator>>,
3357 public from_utf_convertible<K, sstring<K, Allocator>> {
3358public:
3359 using symb_type = K;
3360 using uns_type = std::make_unsigned_t<K>;
3361 using my_type = sstring<K, Allocator>;
3362 using allocator_t = Allocator;
3363
3364 enum { LocalCount = local_count<K> };
3365
3366protected:
3367 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
3368 using base_storable = str_storable<K, my_type, Allocator>;
3369 using base_utf = from_utf_convertible<K, my_type>;
3370 using traits = ch_traits<K>;
3371 using uni = unicode_traits<K>;
3372 using s_str = base_storable::s_str;
3373
3374 friend base_storable;
3375 friend base_utf;
3376
3377 enum Types { Local, Constant, Shared };
3378
3379 union {
3380 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
3381 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
3382 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
3383 // и в итоге после символов строки получается 0, как и надо!
3384 // When we have a short string, it lies in the object itself, and in localRemain
3385 // writes how many more characters can be entered. When a string takes up everything
3386 // possible location, then localRemain becomes 0, type in this case is also 0,
3387 // and as a result, after the characters of the string we get 0, as it should!
3388 struct {
3389 K buf_[LocalCount]; // Локальный буфер строки | Local string buffer
3390 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3391 uns_type type_ : 2;
3392 };
3393 struct {
3394 union {
3395 // Указатель на конcтантную строку | Pointer to a constant string
3396 const K* cstr_;
3397 // Указатель на строку, перед которой лежит SharedStringData
3398 // Pointer to the string preceded by SharedStringData
3399 const K* sstr_;
3400 };
3401 size_t bigLen_; // Длина не локальной строки | Non-local string length
3402 };
3403 };
3404
3405 constexpr void create_empty() {
3406 type_ = Local;
3407 localRemain_ = LocalCount;
3408 buf_[0] = 0;
3409 }
3410 constexpr K* init(size_t s) {
3411 if (s > LocalCount) {
3412 type_ = Shared;
3413 localRemain_ = 0;
3414 bigLen_ = s;
3415 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
3416 return (K*)sstr_;
3417 } else {
3418 type_ = Local;
3419 localRemain_ = LocalCount - s;
3420 return buf_;
3421 }
3422 }
3423
3424 K* set_size(size_t newSize) {
3425 // Вызывается при создании строки при необходимости изменить размер.
3426 // Других ссылок на shared buffer нет.
3427 // Called when a string is created and needs to be resized.
3428 // There are no other references to the shared buffer.
3429 size_t size = length();
3430 if (newSize != size) {
3431 if (type_ == Constant) {
3432 bigLen_ = newSize;
3433 } else {
3434 if (newSize <= LocalCount) {
3435 if (type_ == Shared) {
3436 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
3437 traits::copy(buf_, sstr_, newSize);
3438 str_buf->decr(base_storable::allocator());
3439 }
3440 type_ = Local;
3441 localRemain_ = LocalCount - newSize;
3442 } else {
3443 if (type_ == Shared) {
3444 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
3445 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3446 traits::copy(newStr, sstr_, newSize);
3447 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3448 sstr_ = newStr;
3449 }
3450 } else if (type_ == Local) {
3451 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3452 if (size)
3453 traits::copy(newStr, buf_, size);
3454 sstr_ = newStr;
3455 type_ = Shared;
3456 localRemain_ = 0;
3457 }
3458 bigLen_ = newSize;
3459 }
3460 }
3461 }
3462 K* str = type_ == Local ? buf_ : (K*)sstr_;
3463 str[newSize] = 0;
3464 return str;
3465 }
3466
3467public:
3468
3469 sstring() {
3470 create_empty();
3471 }
3472
3479 template<typename... Args>
3480 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
3481 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3482 : base_storable(std::forward<Args>(args)...) {
3483 create_empty();
3484 }
3485
3494 template<typename... Args>
3495 requires std::is_constructible_v<allocator_t, Args...>
3496 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3498 }
3499
3509 template<typename... Args>
3510 requires std::is_constructible_v<allocator_t, Args...>
3511 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3512 base_storable::init_str_repeat(repeat, pattern);
3513 }
3514
3524 template<typename... Args>
3525 requires std::is_constructible_v<allocator_t, Args...>
3526 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3528 }
3529
3543 template<typename... Args>
3544 requires std::is_constructible_v<allocator_t, Args...>
3545 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3547 }
3548
3564 template<StrType<K> From, typename... Args>
3565 requires std::is_constructible_v<allocator_t, Args...>
3566 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3567 : base_storable(std::forward<Args>(args)...) {
3568 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3569 }
3570
3571 static const sstring<K> empty_str;
3573 constexpr ~sstring() {
3574 if (type_ == Shared) {
3575 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3576 }
3577 }
3578
3584 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
3585 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3586 if (type_ == Shared)
3587 SharedStringData<K>::from_str(sstr_)->incr();
3588 }
3589
3595 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3596 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3597 other.create_empty();
3598 }
3599
3610 template<size_t N>
3611 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
3612 size_t size = src.length();
3613 if (size) {
3614 if (src.is_alloced()) {
3615 // Там динамический буфер, выделенный с запасом для SharedStringData.
3616 // There is a dynamic buffer allocated with a reserve for SharedStringData.
3617 K* str = src.str();
3618 if (size > LocalCount) {
3619 // Просто присвоим его себе.
3620 // Let's just assign it to ourselves.
3621 sstr_ = str;
3622 bigLen_ = size;
3623 type_ = Shared;
3624 localRemain_ = 0;
3625 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
3626 } else {
3627 // Скопируем локально
3628 // Copy locally
3629 type_ = Local;
3630 localRemain_ = LocalCount - size;
3631 traits::copy(buf_, str, size + 1);
3632 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
3633 // Let's free that buffer; a local string's buffer is not shared with others
3634 src.dealloc();
3635 }
3636 } else {
3637 // Копируем из локального буфера
3638 // Copy from local buffer
3639 K* str = init(src.size_);
3640 traits::copy(str, src.symbols(), size + 1);
3641 }
3642 src.create_empty();
3643 } else
3644 create_empty();
3645 }
3646
3657 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
3658 requires std::is_constructible_v<allocator_t, Args...>
3659 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3660 type_ = Constant;
3661 localRemain_ = 0;
3662 cstr_ = (const K*)s;
3663 bigLen_ = N - 1;
3664 }
3665
3671 template<typename O> requires(!std::is_same_v<O, K>)
3673 this->init_from_utf_convertible(init);
3674 }
3675
3676 template<typename O, typename I, bool M> requires(!std::is_same_v<O, K>)
3677 sstring(const str_algs<O, simple_str<O>, I, M>& init) {
3678 this->init_from_utf_convertible(init.to_str());
3679 }
3680
3681 constexpr void swap(my_type&& other) noexcept {
3682 char buf[sizeof(buf_) + sizeof(K)];
3683 memcpy(buf, buf_, sizeof(buf));
3684 memcpy(buf_, other.buf_, sizeof(buf));
3685 memcpy(other.buf_, buf, sizeof(buf));
3686
3687 std::swap(base_storable::allocator(), other.allocator());
3688 }
3697 constexpr my_type& operator=(my_type other) noexcept {
3698 swap(std::move(other));
3699 return *this;
3700 }
3701
3709 constexpr my_type& operator=(simple_str<K> other) {
3710 return operator=(my_type{other, base_storable::allocator()});
3711 }
3712
3720 template<typename T, size_t N = const_lit_for<K, T>::Count>
3721 constexpr my_type& operator=(T&& other) {
3722 return operator=(my_type{std::forward<T>(other), base_storable::allocator()});
3723 }
3724
3732 template<size_t N, bool forShared, typename A>
3733 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
3734 return operator=(my_type{other.to_str(), base_storable::allocator()});
3735 }
3736
3744 template<size_t N>
3745 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
3746 return operator=(my_type{std::move(other)});
3747 }
3748
3758 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
3759 return operator=(my_type{expr, base_storable::allocator()});
3760 }
3761
3767 constexpr my_type& make_empty() noexcept {
3768 if (type_ == Shared)
3769 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3770 create_empty();
3771 return *this;
3772 }
3773
3774 constexpr const K* symbols() const noexcept {
3775 return type_ == Local ? buf_ : cstr_;
3776 }
3777
3778 constexpr size_t length() const noexcept {
3779 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
3780 }
3781
3782 constexpr bool is_empty() const noexcept {
3783 return length() == 0;
3784 }
3785
3786 constexpr bool empty() const noexcept {
3787 return is_empty();
3788 }
3789
3801 template<typename... T>
3802 static my_type printf(const K* pattern, T&&... args) {
3803 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
3804 }
3805
3815 template<typename... T>
3816 static my_type format(const FmtString<to_std_char_t<K>, T...>& fmtString, T&&... args) {
3817 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
3818 }
3819
3829 template<typename... T>
3830 static my_type vformat(simple_str<K> fmtString, T&&... args) {
3831 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
3832 }
3833};
3834
3835template<typename K, Allocatorable Allocator>
3836inline const sstring<K> sstring<K, Allocator>::empty_str{};
3837
3838struct no_alloc{};
3839
3840template<typename K, size_t N>
3841class decl_empty_bases cestring :
3842 public str_algs<K, simple_str<K>, cestring<K, N>, true>,
3843 public str_storable<K, cestring<K, N>, no_alloc>,
3844 public null_terminated<K, lstring<K, N>>
3845 //, public from_utf_convertible<K, lstring<K, N, forShared, Allocator>>
3846{
3847public:
3848 using symb_type = K;
3849 using my_type = cestring<K, N>;
3850
3851 enum : size_t {
3853 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
3854 };
3855
3856protected:
3857
3858 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
3859 using base_storable = str_storable<K, my_type, no_alloc>;
3860 //using base_utf = from_utf_convertible<K, my_type>;
3861 using traits = ch_traits<K>;
3862 using s_str = base_storable::s_str;
3863
3864 friend base_storable;
3865 //friend base_utf;
3866 const K* cstr_{};
3867 size_t size_{};
3868 bool is_cstr_{};
3869 K local_[LocalCapacity + 1]{};
3870
3871 constexpr void create_empty() {
3872 is_cstr_ = false;
3873 size_ = 0;
3874 local_[0] = 0;
3875 }
3876
3877 constexpr K* init(size_t s) {
3878 size_ = s;
3879 if (size_ > LocalCapacity) {
3880 throw std::bad_alloc{};
3881 }
3882 is_cstr_ = false;
3883 return local_;
3884 }
3885public:
3887 constexpr size_t length() const noexcept {
3888 return size_;
3889 }
3891 constexpr const K* symbols() const noexcept {
3892 return is_cstr_ ? cstr_ : local_;
3893 }
3895 constexpr bool is_empty() const noexcept {
3896 return size_ == 0;
3897 }
3899 constexpr bool empty() const noexcept {
3900 return size_ == 0;
3901 }
3903 constexpr size_t capacity() const noexcept {
3904 return LocalCapacity;
3905 }
3910 constexpr cestring() noexcept = default;
3911
3918 constexpr cestring(s_str other) : base_storable() {
3919 base_storable::init_from_str_other(other);
3920 }
3929 constexpr cestring(size_t repeat, s_str pattern) : base_storable() {
3930 base_storable::init_str_repeat(repeat, pattern);
3931 }
3940 constexpr cestring(size_t count, K pad) : base_storable() {
3941 base_storable::init_symb_repeat(count, pad);
3942 }
3955 constexpr cestring(const StrExprForType<K> auto& expr) : base_storable() {
3956 base_storable::init_str_expr(expr);
3957 }
3972 template<StrType<K> From>
3973 constexpr cestring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0)
3974 : base_storable() {
3975 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3976 }
3977
3979 constexpr ~cestring() {}
3980
3989 template<typename T, size_t M = const_lit_for<K, T>::Count>
3990 constexpr cestring(T&& s) : base_storable(), cstr_((const K*)s), size_(M - 1), is_cstr_(true), local_{0} {}
3991};
3992
3993template<typename K>
3994consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
3995 if constexpr (std::is_same_v<K, u8s>)
3996 return s8;
3997 if constexpr (std::is_same_v<K, ubs>)
3998 return sb;
3999 if constexpr (std::is_same_v<K, uws>)
4000 return sw;
4001 if constexpr (std::is_same_v<K, u16s>)
4002 return s16;
4003 if constexpr (std::is_same_v<K, u32s>)
4004 return s32;
4005}
4006
4007#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
4008
4009template<typename K, template<typename C> typename H>
4010struct StoreType {
4011 simple_str<K> str;
4012 size_t hash;
4013 char node[sizeof(sstring<K>)];
4014
4015 const simple_str_nt<K>& to_nt() const noexcept {
4016 return static_cast<const simple_str_nt<K>&>(str);
4017 }
4018 const sstring<K>& to_str() const noexcept {
4019 return *reinterpret_cast<const sstring<K>*>(node);
4020 }
4021 bool operator==(const StoreType& other) const {
4022 return hash == other.hash && typename H<K>::eql{}(*this, other);
4023 }
4024};
4025
4026template<bool Wide>
4027struct fnv_const {
4028 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
4029 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
4030};
4031
4032template<>
4033struct fnv_const<false> {
4034 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
4035 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
4036};
4037
4038using fnv = fnv_const<sizeof(size_t) == 8>;
4039
4040template<typename K>
4041inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
4042 size_t h = fnv::basis;
4043 for (size_t i = 0; i < l; i++) {
4044 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
4045 }
4046 return h;
4047};
4048
4049template<typename K>
4050inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
4051 size_t h = fnv::basis;
4052 for (size_t i = 0; i < l; i++) {
4053 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
4054 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4055 }
4056 return h;
4057};
4058
4059template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4060inline constexpr size_t fnv_hash(T&& value) {
4061 size_t h = fnv::basis;
4062 for (size_t i = 0; i < N - 1; i++) {
4063 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
4064 }
4065 return h;
4066};
4067
4068template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4069inline constexpr size_t fnv_hash_ia(T&& value) {
4070 size_t h = fnv::basis;
4071 for (size_t i = 0; i < N - 1; i++) {
4072 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
4073 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4074 }
4075 return h;
4076};
4077
4078template<typename K>
4079inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
4080 return fnv_hash(ptr, l);
4081};
4082
4083template<typename K>
4084inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
4085 return fnv_hash_ia(ptr, l);
4086};
4087
4088template<typename K>
4089struct str_exact;
4090
4091static_assert(std::is_trivially_copyable_v<StoreType<u8s, str_exact>>, "Store type must be trivially copyable");
4092
4093
4094template<typename K, typename V>
4095std::allocator<std::pair<K, V>> default_hashstrmap_allocator_selector(...);
4096// Если вы хотите задать свой дефолтный аллокатор для hashStrmap, перед включение sstring.h
4097// объявите функцию
4098// template<typename K, typename V>
4099// ваш_тип_аллокатора default_hashstrmap_allocator_selector(int);
4100// If you want to set your default allocator for hashStrMap, before including sstring.h
4101// declare a function
4102// template<typename K, typename V>
4103// your_allocator_type default_hashstrmap_allocator_selector(int);
4104template<typename K, template<typename C> typename H, typename V>
4105using allocator_hashstrmap = decltype(default_hashstrmap_allocator_selector<const StoreType<K, H>, V>(int(0)));
4106
4201template<typename K, typename T, template<typename C> typename HE = str_exact, typename A = allocator_hashstrmap<K, HE, T>>
4202class hashStrMap : public std::unordered_map<StoreType<K, HE>, T, typename HE<K>::hash, typename HE<K>::eql, A> {
4203protected:
4204 using InStore = StoreType<K, HE>;
4205
4206public:
4207 using hasher = typename HE<K>::hash;
4208 using comparator = typename HE<K>::eql;
4209 using my_type = hashStrMap<K, T, HE, A>;
4210 using hash_t = std::unordered_map<StoreType<K, HE>, T, hasher, comparator, A>;
4211
4212 hashStrMap() = default;
4213 hashStrMap(const my_type& other) : hash_t(other) {
4214 for (const auto& [k, v] : *this) {
4215 InStore& stored = const_cast<InStore&>(k);
4216 sstring<K> tmp = *(sstring<K>*)stored.node;
4217 new (stored.node) sstring<K>(std::move(tmp));
4218 stored.str.str = stored.to_str().symbols();
4219 }
4220 }
4221 ~hashStrMap() {
4222 for (auto& k: *this)
4223 ((sstring<K>*)k.first.node)->~sstring();
4224 }
4225
4226 hashStrMap(my_type&& o) = default;
4227
4228 my_type& operator=(const my_type& other) {
4229 hash_t::operator=(other);
4230 for (const auto& [k, v] : *this) {
4231 InStore& stored = const_cast<InStore&>(k);
4232 sstring<K> tmp = *(sstring<K>*)stored.node;
4233 new (stored.node) sstring<K>(std::move(tmp));
4234 stored.str.str = stored.to_str().symbols();
4235 }
4236 return *this;
4237 };
4238 my_type& operator=(my_type&&) = default;
4239
4240 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4241 for (const auto& e: init)
4242 emplace(e.first, e.second);
4243 }
4244
4245 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4246
4247 hashStrMap(init_str&& init) {
4248 for (const auto& e: init)
4249 emplace(e.first, e.second);
4250 }
4251
4252 // При входе хэш должен быть уже посчитан
4253 // When entering, the hash must already be calculated
4254 template<typename... ValArgs>
4255 auto try_emplace(const InStore& key, ValArgs&&... args) {
4256 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4257 if (it.second) {
4258 InStore& stored = const_cast<InStore&>(it.first->first);
4259 new (stored.node) sstring<K>(key.str);
4260 stored.str.str = stored.to_str().symbols();
4261 }
4262 return it;
4263 }
4264
4265 static InStore toStoreType(simple_str<K> key) {
4266 return {key, hasher{}(key)};
4267 }
4268
4269 template<typename Key, typename... ValArgs>
4270 requires(std::is_convertible_v<Key, simple_str<K>>)
4271 auto try_emplace(Key&& key, ValArgs&&... args) {
4272 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4273 if (it.second) {
4274 InStore& stored = const_cast<InStore&>(it.first->first);
4275 new (stored.node) sstring<K>(std::forward<Key>(key));
4276 stored.str.str = stored.to_str().symbols();
4277 }
4278 return it;
4279 }
4280
4281 template<typename... ValArgs>
4282 auto emplace(const InStore& key, ValArgs&&... args) {
4283 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4284 if (!it.second) {
4285 it.first->second = T(std::forward<ValArgs>(args)...);
4286 }
4287 return it;
4288 }
4289
4290 template<typename Key, typename... ValArgs>
4291 requires(std::is_convertible_v<Key, simple_str<K>>)
4292 auto emplace(Key&& key, ValArgs&&... args) {
4293 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4294 if (!it.second) {
4295 it.first->second = T(std::forward<ValArgs>(args)...);
4296 }
4297 return it;
4298 }
4299
4300 auto& operator[](const InStore& key) {
4301 return try_emplace(key).first->second;
4302 }
4303
4304 template<typename Key>
4305 requires(std::is_convertible_v<Key, simple_str<K>>)
4306 auto& operator[](Key&& key) {
4307 return try_emplace(std::forward<Key>(key)).first->second;
4308 }
4309
4310 decltype(auto) at(const InStore& key) {
4311 return hash_t::at(key);
4312 }
4313 decltype(auto) at(const InStore& key) const {
4314 return hash_t::at(key);
4315 }
4316
4317 decltype(auto) at(simple_str<K> key) {
4318 return hash_t::at(toStoreType(key));
4319 }
4320 decltype(auto) at(simple_str<K> key) const {
4321 return hash_t::at(toStoreType(key));
4322 }
4323
4324 auto find(const InStore& key) const {
4325 return hash_t::find(key);
4326 }
4327
4328 auto find(simple_str<K> key) const {
4329 return find(toStoreType(key));
4330 }
4331
4332 auto find(const InStore& key) {
4333 return hash_t::find(key);
4334 }
4335
4336 auto find(simple_str<K> key) {
4337 return find(toStoreType(key));
4338 }
4339
4340 auto erase(typename hash_t::const_iterator it) {
4341 if (it != hash_t::end()) {
4342 ((sstring<K>*)it->first.node)->~sstring();
4343 }
4344 return hash_t::erase(it);
4345 }
4346
4347 auto erase(const InStore& key) {
4348 auto it = hash_t::find(key);
4349 if (it != hash_t::end()) {
4350 ((sstring<K>*)it->first.node)->~sstring();
4351 hash_t::erase(it);
4352 return 1;
4353 }
4354 return 0;
4355 }
4356
4357 auto erase(simple_str<K> key) {
4358 return erase(toStoreType(key));
4359 }
4360
4361 bool lookup(simple_str<K> txt, T& val) const {
4362 auto it = find(txt);
4363 if (it != hash_t::end()) {
4364 val = it->second;
4365 return true;
4366 }
4367 return false;
4368 }
4369
4370 void clear() {
4371 for (auto& k: *this)
4372 ((sstring<K>*)k.first.node)->~sstring();
4373 hash_t::clear();
4374 }
4375 bool contains(const InStore& key) const {
4376 return hash_t::find(key) != this->end();
4377 }
4378
4379 bool contains(simple_str<K> key) const {
4380 return find(toStoreType(key)) != this->end();
4381 }
4382};
4383
4384template<typename K>
4385struct str_exact {
4386 struct eql {
4387 bool operator()(const StoreType<K, str_exact>& _Left, const StoreType<K, str_exact>& _Right) const {
4388 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4389 }
4390 };
4391 struct hash { // hash functor for basic_string
4392 size_t operator()(simple_str<K> _Keyval) const {
4393 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4394 }
4395 size_t operator()(const StoreType<K, str_exact>& _Keyval) const {
4396 return _Keyval.hash;
4397 }
4398 };
4399};
4400
4401template<typename K>
4402struct str_eqlia {
4403 struct eql {
4404 bool operator()(const StoreType<K, str_eqlia>& _Left, const StoreType<K, str_eqlia>& _Right) const {
4405 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4406 }
4407 };
4408 struct hash {
4409 size_t operator()(simple_str<K> _Keyval) const {
4410 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4411 }
4412 size_t operator()(const StoreType<K, str_eqlia>& _Keyval) const {
4413 return _Keyval.hash;
4414 }
4415 };
4416};
4417
4418template<typename K>
4419struct str_eqliu {
4420 struct eql {
4421 bool operator()(const StoreType<K, str_eqliu>& _Left, const StoreType<K, str_eqliu>& _Right) const {
4422 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4423 }
4424 };
4425 struct hash {
4426 size_t operator()(simple_str<K> _Keyval) const {
4427 using bc = to_base_char_t<K>;
4428 return unicode_traits<bc>::hashiu((const bc*)_Keyval.symbols(), _Keyval.length());
4429 }
4430 size_t operator()(const StoreType<K, str_eqliu>& _Keyval) const {
4431 return _Keyval.hash;
4432 }
4433 };
4434};
4435
4450template<typename K>
4451class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
4452 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
4453 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
4454 K* write{}; // Текущая позиция записи | Current write position
4455 size_t len{}; // Общая длина | Total length
4456 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
4457 size_t align{1024};
4458
4459public:
4460 using my_type = chunked_string_builder<K>;
4461 using symb_type = K;
4462 chunked_string_builder() = default;
4463 chunked_string_builder(size_t a) : align(a){};
4464 chunked_string_builder(const my_type&) = delete;
4465 chunked_string_builder(my_type&& other) noexcept
4466 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4467 other.len = other.remain = 0;
4468 other.write = nullptr;
4469 }
4470 my_type& operator=(my_type other) noexcept {
4471 chunks.swap(other.chunks);
4472 write = other.write;
4473 len = other.len;
4474 remain = other.remain;
4475 align = other.align;
4476 other.len = other.remain = 0;
4477 other.write = nullptr;
4478 return *this;
4479 }
4480
4483 if (data.len) {
4484 len += data.len;
4485 if (data.len <= remain) {
4486 // Добавляемые данные влезают в выделенный блок, просто скопируем их
4487 // The added data fits into the selected block, just copy it
4488 ch_traits<K>::copy(write, data.str, data.len);
4489 write += data.len; // Сдвинем позицию записи | Let's move the recording position
4490 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4491 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
4492 } else {
4493 // Не влезают | They don't fit
4494 if (remain) {
4495 // Сначала запишем сколько влезет
4496 // First, write down as much as we can
4497 ch_traits<K>::copy(write, data.str, remain);
4498 data.len -= remain;
4499 data.str += remain;
4500 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4501 }
4502 // Выделим новый блок и впишем в него данные
4503 // Рассчитаем размер блока, кратного заданному выравниванию
4504 // Select a new block and write data into it
4505 // Calculate the block size that is a multiple of the given alignment
4506 size_t blockSize = (data.len + align - 1) / align * align;
4507 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
4508 write = chunks.back().first.get();
4509 ch_traits<K>::copy(write, data.str, data.len);
4510 write += data.len;
4511 remain = blockSize - data.len;
4512 }
4513 }
4514 return *this;
4515 }
4516
4517 my_type& operator<<(const StrExprForType<K> auto& expr) {
4518 size_t l = expr.length();
4519 if (l) {
4520 if (l < remain) {
4521 write = expr.place(write);
4522 chunks.back().second += l;
4523 len += l;
4524 remain -= l;
4525 } else if (!remain) {
4526 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
4527 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4528 write = expr.place(chunks.back().first.get());
4529 len += l;
4530 remain = blockSize - l;
4531 } else {
4532 auto store = std::make_unique<K[]>(l);
4533 expr.place(store.get());
4534 return operator<<({store.get(), l});
4535 }
4536 }
4537 return *this;
4538 }
4539
4540 template<typename T>
4541 my_type& operator<<(T data)
4542 requires std::is_same_v<T, K>
4543 {
4544 return operator<<(expr_char<K>(data));
4545 }
4546
4547 constexpr size_t length() const noexcept {
4548 return len;
4549 }
4550
4551 void reset() {
4552 if (chunks.empty()) {
4553 return;
4554 }
4555 if (chunks.size() > 1) {
4556 remain = 0;
4557 chunks.resize(1);
4558 }
4559 remain += chunks[0].second;
4560 chunks[0].second = 0;
4561 len = 0;
4562 write = chunks[0].first.get();
4563 }
4564
4565 constexpr K* place(K* p) const noexcept {
4566 for (const auto& block: chunks) {
4567 ch_traits<K>::copy(p, block.first.get(), block.second);
4568 p += block.second;
4569 }
4570 return p;
4571 }
4580 template<typename Op>
4581 void out(const Op& o) const {
4582 for (const auto& block: chunks)
4583 o(block.first.get(), block.second);
4584 }
4585
4589 bool is_continuous() const {
4590 if (chunks.size()) {
4591 const K* ptr = chunks.front().first.get();
4592 for (const auto& chunk: chunks) {
4593 if (chunk.first.get() != ptr)
4594 return false;
4595 ptr += chunk.second;
4596 }
4597 }
4598 return true;
4599 }
4600
4606 const K* begin() const {
4607 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4608 }
4609
4613 void clear() {
4614 chunks.clear();
4615 write = nullptr;
4616 len = 0;
4617 remain = 0;
4618 }
4619
4624 typename decltype(chunks)::const_iterator it, end;
4625 size_t writedFromCurrentChunk;
4630 bool is_end() {
4631 return it == end;
4632 }
4633
4643 size_t store(K* buffer, size_t size) {
4644 size_t writed = 0;
4645 while (size && !is_end()) {
4646 size_t remain = it->second - writedFromCurrentChunk;
4647 size_t write = std::min(size, remain);
4648 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4649 writed += write;
4650 remain -= write;
4651 size -= write;
4652 if (!remain) {
4653 ++it;
4654 writedFromCurrentChunk = 0;
4655 } else
4656 writedFromCurrentChunk += write;
4657 }
4658 return writed;
4659 }
4660 };
4661
4668 return {chunks.begin(), chunks.end(), 0};
4669 }
4670
4676 const auto& data() const {
4677 return chunks;
4678 }
4679};
4680
4681using stringa = sstring<u8s>;
4682using stringb = sstring<ubs>;
4683using stringw = sstring<wchar_t>;
4684using stringu = sstring<u16s>;
4685using stringuu = sstring<u32s>;
4686static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
4687
4692template<typename T, typename A = allocator_hashstrmap<u8s, str_exact, T>>
4698template<typename T, typename A = allocator_hashstrmap<u8s, str_eqlia, T>>
4704template<typename T, typename A = allocator_hashstrmap<u8s, str_eqliu, T>>
4706
4711template<typename T, typename A = allocator_hashstrmap<ubs, str_exact, T>>
4717template<typename T, typename A = allocator_hashstrmap<ubs, str_eqlia, T>>
4723template<typename T, typename A = allocator_hashstrmap<ubs, str_eqliu, T>>
4725
4730template<typename T, typename A = allocator_hashstrmap<wchar_t, str_exact, T>>
4732
4737template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqlia, T>>
4739
4744template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqliu, T>>
4746
4751template<typename T, typename A = allocator_hashstrmap<u16s, str_exact, T>>
4757template<typename T, typename A = allocator_hashstrmap<u16s, str_eqlia, T>>
4763template<typename T, typename A = allocator_hashstrmap<u16s, str_eqliu, T>>
4765
4770template<typename T, typename A = allocator_hashstrmap<u32s, str_exact, T>>
4776template<typename T, typename A = allocator_hashstrmap<u32s, str_eqlia, T>>
4782template<typename T, typename A = allocator_hashstrmap<u32s, str_eqliu, T>>
4784
4785inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
4786
4787inline namespace literals {
4788
4799SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4800 return simple_str_nt<u8s>{ptr, l};
4801}
4802
4812SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4813 return simple_str_nt<ubs>{ptr, l};
4814}
4815
4825SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4826 return simple_str_nt<uws>{ptr, l};
4827}
4828
4838SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4839 return simple_str_nt<u16s>{ptr, l};
4840}
4841
4852SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4853 return simple_str_nt<u32s>{ptr, l};
4854}
4855
4856template<typename K> using HashKey = StoreType<K, str_exact>;
4857template<typename K> using HashKeyIA = StoreType<K, str_eqlia>;
4858template<typename K> using HashKeyIU = StoreType<K, str_eqliu>;
4859
4870consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
4871 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4872}
4873
4884consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
4885 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4886}
4887
4898inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
4899 return HashKeyIU<u8s>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{ptr, l})};
4900}
4901
4912consteval HashKey<ubs> operator""_h(const ubs* ptr, size_t l) {
4913 return HashKey<ubs>{{ptr, l}, fnv_hash_compile(ptr, l)};
4914}
4915
4926consteval HashKeyIA<ubs> operator""_ia(const ubs* ptr, size_t l) {
4927 return HashKeyIA<ubs>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4928}
4929
4940inline HashKeyIU<ubs> operator""_iu(const ubs* ptr, size_t l) {
4941 return HashKeyIU<ubs>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{(const u8s*)ptr, l})};
4942}
4943
4954consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
4955 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4956}
4957
4968consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
4969 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4970}
4971
4982inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
4983 return HashKeyIU<u16s>{{ptr, l}, str_eqliu<u16s>::hash{}(simple_str<u16s>{ptr, l})};
4984}
4985
4996consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
4997 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4998}
4999
5010consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
5011 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
5012}
5013
5024inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
5025 return HashKeyIU<u32s>{{ptr, l}, str_eqliu<u32s>::hash{}(simple_str<u32s>{ptr, l})};
5026}
5027
5038consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
5039 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
5040}
5041
5052consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
5053 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
5054}
5055
5066inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
5067 return HashKeyIU<uws>{{ptr, l}, str_eqliu<uws>::hash{}(simple_str<uws>{ptr, l})};
5068}
5069} // namespace literals
5070
5071template<is_one_of_char_v K, bool upper>
5072struct expr_change_case : expr_to_std_string<expr_change_case<K, upper>>{
5073 using symb_type = K;
5074
5075 str_src<K> src_;
5076 mutable size_t len_{};
5077
5078 template<StrSource S>
5079 expr_change_case(S&& s) : src_(std::forward<S>(s)){}
5080
5081 constexpr size_t length() const noexcept {
5082 if constexpr (sizeof(K) > 1) {
5083 return src_.length();
5084 } else {
5085 if constexpr (upper) {
5086 len_ = unicode_traits<K>::upper_len(src_.str, src_.len);
5087 } else {
5088 len_ = unicode_traits<K>::lower_len(src_.str, src_.len);
5089 }
5090 return len_;
5091 }
5092 }
5093 constexpr K* place(K* ptr) const noexcept {
5094 if constexpr (sizeof(K) > 1) {
5095 if constexpr (upper) {
5096 unicode_traits<K>::upper(src_.str, src_.len, ptr);
5097 } else {
5098 unicode_traits<K>::lower(src_.str, src_.len, ptr);
5099 }
5100 return ptr + src_.len;
5101 } else {
5102 const K* src = src_.str;
5103 if constexpr (upper) {
5104 unicode_traits<K>::upper(src, src_.len, ptr, len_);
5105 } else {
5106 unicode_traits<K>::lower(src, src_.len, ptr, len_);
5107 }
5108 return ptr;
5109 }
5110 }
5111};
5112
5127template<is_one_of_char_v K>
5128struct e_upper : expr_change_case<K, true> {
5129 using base = expr_change_case<K, true>;
5130 using base::base;
5131};
5132
5133template<StrSource S>
5135
5150template<is_one_of_char_v K>
5151struct e_lower : expr_change_case<K, false> {
5152 using base = expr_change_case<K, false>;
5153 using base::base;
5154};
5155
5156template<StrSource S>
5158
5169inline std::ostream& operator<<(std::ostream& stream, ssa text) {
5170 return stream << std::string_view{text.symbols(), text.length()};
5171}
5172
5183inline std::wostream& operator<<(std::wostream& stream, ssw text) {
5184 return stream << std::wstring_view{text.symbols(), text.length()};
5185}
5186
5197inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
5198 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5199}
5200
5211inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
5212 return stream << std::string_view{text.symbols(), text.length()};
5213}
5214
5225inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
5226 return stream << std::wstring_view{text.symbols(), text.length()};
5227}
5228
5239inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
5240 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5241}
5242
5253template<size_t N, bool S, simstr::Allocatorable A>
5254inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
5255 return stream << std::string_view{text.symbols(), text.length()};
5256}
5257
5268template<size_t N, bool S, simstr::Allocatorable A>
5269inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
5270 return stream << std::wstring_view{text.symbols(), text.length()};
5271}
5272
5283template<size_t N, bool S, simstr::Allocatorable A>
5284inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
5285 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5286}
5287
5288} // namespace simstr
5289
5294template<typename K>
5295struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5296 // Define format() by calling the base class implementation with the wrapped value
5297 template<typename FormatContext>
5298 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
5299 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5300 }
5301};
5302
5307template<typename K>
5308struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5309 // Define format() by calling the base class implementation with the wrapped value
5310 template<typename FormatContext>
5311 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
5312 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5313 }
5314};
5315
5320template<typename K>
5321struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5322 // Define format() by calling the base class implementation with the wrapped value
5323 template<typename FormatContext>
5324 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
5325 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5326 }
5327};
5328
5333template<typename K, size_t N, bool S, typename A>
5334struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
5335 // Define format() by calling the base class implementation with the wrapped value
5336 template<typename FormatContext>
5337 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
5338 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5339 }
5340};
5341
5346template<>
5347struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5348 // Define format() by calling the base class implementation with the wrapped value
5349 template<typename FormatContext>
5350 auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
5351 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5352 }
5353};
5354
5359template<>
5360struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5361 // Define format() by calling the base class implementation with the wrapped value
5362 template<typename FormatContext>
5363 auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
5364 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5365 }
5366};
5367
5372template<>
5373struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5374 // Define format() by calling the base class implementation with the wrapped value
5375 template<typename FormatContext>
5376 auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
5377 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5378 }
5379};
5380
5385template<size_t N, bool S, typename A>
5386struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5387 // Define format() by calling the base class implementation with the wrapped value
5388 template<typename FormatContext>
5389 auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
5390 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5391 }
5392};
5393
5398template<>
5399struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5400 // Define format() by calling the base class implementation with the wrapped value
5401 template<typename FormatContext>
5402 auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
5403 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5404 }
5405};
5406
5411template<>
5412struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5413 // Define format() by calling the base class implementation with the wrapped value
5414 template<typename FormatContext>
5415 auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
5416 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5417 }
5418};
5419
5424template<>
5425struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5426 // Define format() by calling the base class implementation with the wrapped value
5427 template<typename FormatContext>
5428 auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
5429 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5430 }
5431};
5432
5437template<size_t N, bool S, typename A>
5438struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5439 // Define format() by calling the base class implementation with the wrapped value
5440 template<typename FormatContext>
5441 auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
5442 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5443 }
5444};
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3234
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:4482
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:4667
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:4547
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:4541
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:4551
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:4613
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:4517
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:4589
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:4581
const auto & data() const
Get internal data buffers.
Definition sstring.h:4676
const K * begin() const
Get a pointer to the beginning of the first buffer. It makes sense to apply only if is_continuous tru...
Definition sstring.h:4606
Container for more efficient searching by string keys.
Definition sstring.h:4202
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:2668
constexpr void define_size()
Determine the length of the string. Searches for the character 0 in the string buffer to its capacity...
Definition sstring.h:3211
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:2937
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3082
constexpr void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:3246
@ LocalCapacity
Definition sstring.h:2676
constexpr bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:3202
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:3025
constexpr size_t length() const noexcept
String length.
Definition sstring.h:3105
constexpr lstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:2804
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:2874
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:2819
constexpr lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:2893
constexpr lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:2920
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:3095
constexpr K * reserve_no_preserve(size_t newSize)
Definition sstring.h:3140
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:3008
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3117
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:2979
constexpr K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:3185
constexpr lstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:2834
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:2853
constexpr const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:3109
constexpr void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:3242
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:3070
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3121
constexpr void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:3228
constexpr lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:2952
constexpr K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:3162
constexpr K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:3113
constexpr size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:3125
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:2789
Immutable owning string class.
Definition sstring.h:3357
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:3733
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3786
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3782
sstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:3496
sstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:3526
constexpr const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:3774
constexpr sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:3584
static my_type format(const FmtString< to_std_char_t< K >, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:3816
constexpr my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3721
sstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:3511
constexpr my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:3697
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:3802
constexpr sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:3611
constexpr size_t length() const noexcept
string length.
Definition sstring.h:3778
constexpr sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:3595
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:3830
sstring(simple_str< O > init)
Initialization from a string source with a different character type. Converts via UTF.
Definition sstring.h:3672
constexpr my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:3709
constexpr my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:3767
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:3758
sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:3659
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Assignment operator to a movable string of type lstring with a compatible buffer.
Definition sstring.h:3745
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:3545
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Constructor for the empty string.
Definition sstring.h:3481
constexpr ~sstring()
String destructor.
Definition sstring.h:3573
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:3566
A class with additional constant string algorithms.
Definition sstring.h:193
bool starts_with_iu(str_piece prefix) const noexcept
Whether the string starts with the given substring, case-insensitive Unicode characters of the first ...
Definition sstring.h:266
int compare_iu(str_piece text) const noexcept
Compare strings character by character without taking into account the case of Unicode characters of ...
Definition sstring.h:230
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:252
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Whether the string ends with the specified substring, case-insensitive Unicode characters of the firs...
Definition sstring.h:281
R upperred() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:293
constexpr std::optional< R > strip_prefix_iu(str_piece prefix) const
Get a string without a prefix if it starts with it, insensitive to Unicode characters up to 0xFFFF.
Definition sstring.h:389
constexpr R trimmed_prefix_iu(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix,...
Definition sstring.h:427
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:305
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:315
constexpr std::optional< R > strip_suffix_iu(str_piece suffix) const
Get a string without a suffix if it ends with one in case-insensitive Unicode characters up to 0xFFFF...
Definition sstring.h:408
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:373
bool equal_iu(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of the Unicode chara...
Definition sstring.h:241
constexpr R trimmed_suffix_iu(str_piece suffix) const
Returns a string with the end of the string removed if it ends with the specified suffix of case-inse...
Definition sstring.h:450
void as_number(double &t) const
Convert string to double.
Definition sstring.h:356
Base class for working with mutable strings.
Definition sstring.h:1435
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:1975
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:2249
Impl & append(const A &expr)
Add a string expression to the end of the string.
Definition sstring.h:1859
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:2262
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the string.
Definition sstring.h:1669
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:1733
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:1748
Impl & trim_left()
Remove whitespace at the beginning of a string.
Definition sstring.h:1571
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the string.
Definition sstring.h:2351
Impl & trim_right_with_wpaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the end of a string.
Definition sstring.h:1658
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the string.
Definition sstring.h:1680
Impl & trim_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning and end of th...
Definition sstring.h:1632
Impl & prepend(str_piece other)
Add another string to the beginning of the string.
Definition sstring.h:1999
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the string.
Definition sstring.h:2489
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Definition sstring.h:2473
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:2335
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:2571
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line, writing no more than the specified numb...
Definition sstring.h:2557
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:2236
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:2505
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:1948
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:2208
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:1932
Impl & remove(size_t from, size_t len)
Remove part of a string.
Definition sstring.h:1988
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a string.
Definition sstring.h:1645
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the string.
Definition sstring.h:1606
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the string.
Definition sstring.h:1619
Impl & trim_right_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace at the end of the string.
Definition sstring.h:1724
Impl & append_in(size_t pos, str_piece other)
Add another string starting at the given position.
Definition sstring.h:1899
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Appends std::vformat formatted output starting at the specified position.
Definition sstring.h:2438
Impl & trim_left_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace, at the beginning of the strin...
Definition sstring.h:1713
Impl & insert(size_t to, str_piece other)
Insert a string at the specified position.
Definition sstring.h:1961
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Formats a string using std::vformat up to the specified size.
Definition sstring.h:2539
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:2281
Impl & operator+=(str_piece other)
Add another string to the end of the string.
Definition sstring.h:1870
Impl & trim_right()
Remove whitespace from the end of a string.
Definition sstring.h:1580
Impl & append(str_piece other)
Add another string to the end of the string.
Definition sstring.h:1847
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:1691
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:1702
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:1544
Impl & prepend(const A &expr)
Add a string expression to the beginning of a string.
Definition sstring.h:2011
Impl & trim()
Remove whitespace from the beginning and end of a string.
Definition sstring.h:1562
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:1782
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Replace occurrences of a substring with another string.
Definition sstring.h:2028
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:1767
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Copy the source string, replacing occurrences of substrings with another string.
Definition sstring.h:2146
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the string.
Definition sstring.h:2521
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:1917
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Definition sstring.h:2409
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the string.
Definition sstring.h:1593
Impl & operator+=(const A &expr)
Add a string expression to the end of the string.
Definition sstring.h:1882
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3422
constexpr size_t size() const
Definition strexpr.h:3363
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3448
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4227
constexpr str_storable(Args &&... args)
Create an empty object.
Definition sstring.h:947
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Initialization from string source with replacement.
Definition sstring.h:1034
static my_type upperred_from(const From &f, Args &&... args)
Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1296
constexpr void init_from_str_other(s_str other)
Initialization from another string object.
Definition sstring.h:955
constexpr allocator_t & allocator()
Get the allocator.
Definition sstring.h:924
constexpr void init_symb_repeat(size_t count, K pad)
Character repetition initialization.
Definition sstring.h:991
constexpr void init_str_expr(const A &expr)
Initialization from a string expression.
Definition sstring.h:1012
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Create a string copy of the passed in uppercase ASCII characters.
Definition sstring.h:1266
static my_type lowered_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1313
constexpr void init_str_repeat(size_t repeat, s_str pattern)
String repetition initialization.
Definition sstring.h:971
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase ASCII characters.
Definition sstring.h:1279
static my_type join(const T &strings, s_str delimiter, bool tail=false, bool skip_empty=false, Args &&... args)
Concatenate strings from the container into one string.
Definition sstring.h:1215
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Create a copy of the passed string with substrings replaced.
Definition sstring.h:1334
constexpr s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:1159
Concept of a memory management type.
Definition sstring.h:1344
The concept of a string expression compatible with a given character type.
Definition strexpr.h:525
A type concept that cannot modify a stored string.
Definition sstring.h:866
A type concept that can modify a stored string.
Definition sstring.h:859
A type concept that can store a string.
Definition sstring.h:849
Small namespace for standard string methods.
Definition strexpr.h:1600
Library namespace.
Definition sstring.cpp:12
hashStrMap< u16s, T, str_eqlia, A > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4758
hashStrMap< ubs, T, str_exact, A > hashStrMapB
Type of hash dictionary for char8_t strings, case sensitive search.
Definition sstring.h:4712
hashStrMap< wchar_t, T, str_eqliu, A > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4745
hashStrMap< u16s, T, str_exact, A > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:4752
hashStrMap< u8s, T, str_exact, A > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:4693
hashStrMap< u32s, T, str_exact, A > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:4771
hashStrMap< ubs, T, str_eqliu, A > hashStrMapBIU
Hash dictionary type for char8_t strings, case-insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4724
hashStrMap< wchar_t, T, str_eqlia, A > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4738
hashStrMap< u32s, T, str_eqliu, A > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4783
expr_utf< From, To > e_utf(simple_str< From > from)
Returns a string expression that converts a string of one character type to another type,...
Definition sstring.h:840
hashStrMap< ubs, T, str_eqlia, A > hashStrMapBIA
Type of hash dictionary for char8_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4718
hashStrMap< u8s, T, str_eqliu, A > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:4705
hashStrMap< wchar_t, T, str_exact, A > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:4731
hashStrMap< u32s, T, str_eqlia, A > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4777
hashStrMap< u8s, T, str_eqlia, A > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4699
hashStrMap< u16s, T, str_eqliu, A > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4764
Some methods for working with standard strings.
Definition strexpr.h:7505
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:4623
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:4630
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:4643
Definition sstring.h:5151
Generates a string based on the original one, replacing all lowercase letters of the first Unicode pl...
Definition sstring.h:5128
Base class for converting string expressions to standard strings.
Definition strexpr.h:659
String expression to convert strings to different UTF types.
Definition sstring.h:809
A class that claims to refer to a null-terminated string.
Definition sstring.h:617
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition sstring.h:680
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:669
constexpr simple_str_nt(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:651
constexpr simple_str_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:657
constexpr simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:642
The simplest immutable non-owning string class.
Definition sstring.h:481
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:497
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:561
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:585
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:518
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:525
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition sstring.h:572
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition sstring.h:513
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:502
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:508
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:532
constexpr bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:541
constexpr bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:550
The simplest class of an immutable non-owning string.
Definition strexpr.h:5057
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5104