simstr 1.6.6
Yet another strings library
 
Загрузка...
Поиск...
Не найдено
sstring.h
1/*
2* ver. 1.6.6
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 lenStr, u8s*& dest, size_t lenBuf);
90 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
91
92 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
93
94 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
95 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
96};
97
98template<>
99struct unicode_traits<u16s> {
100 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
101 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
102
103 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
104 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
105 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
106};
107
108template<>
109struct unicode_traits<u32s> {
110 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
111 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
112
113 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
114 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
115 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
116};
117
118template<>
119struct unicode_traits<wchar_t> {
120 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
121 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
122 }
123 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
124 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
125 }
126
127 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
128 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
129 }
130 static size_t hashia(const wchar_t* src, size_t s) {
131 return unicode_traits<wchar_type>::hashia(to_w(src), s);
132 }
133 static size_t hashiu(const wchar_t* src, size_t s) {
134 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
135 }
136};
137
138
139#if defined(_MSC_VER) && _MSC_VER <= 1933
140template<typename K, typename... Args>
141using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
142#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
143template<typename K, typename... Args>
144using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
145#else
146template<typename K, typename... Args>
147using FmtString = std::basic_string_view<K>;
148#endif
149
150template<typename K>
151SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
152
165template<typename K, typename StrRef, typename Impl, bool Mutable>
166class str_algs : public str_src_algs<K, StrRef, Impl, Mutable> {
167 constexpr const Impl& d() const noexcept {
168 return *static_cast<const Impl*>(this);
169 }
170 constexpr size_t _len() const noexcept {
171 return d().length();
172 }
173 constexpr const K* _str() const noexcept {
174 return d().symbols();
175 }
176 constexpr bool _is_empty() const noexcept {
177 return d().is_empty();
178 }
179
180public:
181 using symb_type = K;
182 using str_piece = StrRef;
183 using traits = ch_traits<K>;
184 using uni = unicode_traits<K>;
185 using uns_type = std::make_unsigned_t<K>;
186 using my_type = Impl;
187 using base = str_src_algs<K, StrRef, Impl, Mutable>;
188 str_algs() = default;
189
190 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
191 if (!len)
192 return _is_empty() ? 0 : 1;
193 return uni::compareiu(_str(), _len(), text, len);
194 }
203 int compare_iu(str_piece text) const noexcept { // NOLINT
204 return compare_iu(text.symbols(), text.length());
205 }
206
214 bool equal_iu(str_piece text) const noexcept { // NOLINT
215 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
216 }
217
225 bool less_iu(str_piece text) const noexcept { // NOLINT
226 return compare_iu(text.symbols(), text.length()) < 0;
227 }
228 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
229 // Does this string begin with the specified substring, insensitive to unicode case
230 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
231 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
232 }
239 bool starts_with_iu(str_piece prefix) const noexcept {
240 return starts_with_iu(prefix.symbols(), prefix.length());
241 }
242 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
243 // Whether the string ends with the specified substring, case insensitive UNICODE
244 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
245 size_t myLen = _len();
246 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
247 }
254 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
255 return ends_with_iu(suffix.symbols(), suffix.length());
256 }
257
265 template<typename R = my_type>
266 R upperred() const {
267 return R::upperred_from(d());
268 }
269
277 template<typename R = my_type>
278 R lowered() const {
279 return R::lowered_from(d());
280 }
281
287 template<bool SkipWS = true, bool AllowPlus = true>
288 std::optional<double> to_double() const noexcept {
289 size_t len = _len();
290 const K* ptr = _str();
291 if constexpr (SkipWS) {
292 while (len && uns_type(*ptr) <= ' ') {
293 len--;
294 ptr++;
295 }
296 }
297 if constexpr (AllowPlus) {
298 if (len && *ptr == K('+')) {
299 ptr++;
300 len--;
301 }
302 }
303 if (!len) {
304 return {};
305 }
306 #ifdef __linux__
307 if constexpr(sizeof(K) == 1) {
308 double d{};
309 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
310 return d;
311 }
312 return {};
313 }
314 #endif
315 if constexpr (sizeof(K) == 1) {
316 return impl_to_double((const char*)ptr, (const char*)ptr + len);
317 } else if constexpr (sizeof(K) == 2) {
318 return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
319 } else {
320 return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
321 }
322 }
323
329 void as_number(double& t) const {
330 auto res = to_double();
331 t = res ? *res : std::nan("0");
332 }
333
345 template<ToIntNumber T>
346 constexpr void as_number(T& t) const {
348 }
349};
350
351/*
352* Базовая структура с информацией о строке.
353* Это структура для не владеющих строк.
354* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
355* в результате размер класса не увеличивается
356* Basic structure with string information.
357* This is the structure for non-owning strings.
358* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
359* as a result the class size does not increase
360*/
361
372template<typename K>
373struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
374 using symb_type = K;
375 using my_type = simple_str<K>;
376
377 const symb_type* str;
378 size_t len;
379
380 constexpr simple_str() = default;
381
382 constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
383
388 template<typename T, size_t N = const_lit_for<K, T>::Count>
389 constexpr simple_str(T&& v) noexcept : str((const K*)v), len(N - 1) {}
394 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
399 template<typename A>
400 constexpr simple_str(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
405 constexpr simple_str(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
410 constexpr size_t length() const noexcept {
411 return len;
412 }
413
417 constexpr const symb_type* symbols() const noexcept {
418 return str;
419 }
420
424 constexpr bool is_empty() const noexcept {
425 return len == 0;
426 }
427
433 constexpr bool is_same(simple_str<K> other) const noexcept {
434 return str == other.str && len == other.len;
435 }
436
442 constexpr bool is_part_of(simple_str<K> other) const noexcept {
443 return str >= other.str && str + len <= other.str + other.len;
444 }
445
453 constexpr K operator[](size_t idx) const {
454 return str[idx];
455 }
456
464 constexpr my_type& remove_prefix(size_t delta) {
465 str += delta;
466 len -= delta;
467 return *this;
468 }
469
477 constexpr my_type& remove_suffix(size_t delta) {
478 len -= delta;
479 return *this;
480 }
481};
482
483template<typename K>
484struct simple_str_selector {
485 using type = simple_str<K>;
486};
487
508template<typename K>
509struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
510 using symb_type = K;
511 using my_type = simple_str_nt<K>;
512 using base = simple_str<K>;
513
514 constexpr static const K empty_string[1] = {0};
515
516 simple_str_nt() = default;
533 template<typename T> requires is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
534 constexpr explicit simple_str_nt(T&& p) noexcept {
535 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
536 base::str = base::len ? p : empty_string;
537 }
538
542 template<typename T, size_t N = const_lit_for<K, T>::Count>
543 constexpr simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
544
549 constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
550
551 template<StrType<K> T>
552 constexpr simple_str_nt(T&& t) {
553 base::str = t.symbols();
554 base::len = t.length();
555 }
560 template<typename A>
561 constexpr simple_str_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
562
563 static const my_type empty_str;
572 constexpr my_type to_nts(size_t from) {
573 if (from > base::len) {
574 from = base::len;
575 }
576 return {base::str + from, base::len - from};
577 }
578};
579
580template<typename K>
581inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
582
583template<typename K>
584using Splitter = SplitterBase<K, simple_str<K>>;
585
586using ssa = simple_str<u8s>;
587using ssb = simple_str<ubs>;
588using ssw = simple_str<wchar_t>;
589using ssu = simple_str<u16s>;
590using ssuu = simple_str<u32s>;
591using stra = simple_str_nt<u8s>;
592using strb = simple_str_nt<ubs>;
593using strw = simple_str_nt<wchar_t>;
594using stru = simple_str_nt<u16s>;
595using struu = simple_str_nt<u32s>;
596
597template<typename Src, typename Dest>
598struct utf_convert_selector;
599
600template<typename Src>
601struct utf_convert_selector<Src, Src> {
602 static size_t need_len(const Src* src, size_t srcLen) {
603 return srcLen;
604 }
605 static size_t convert(const Src* src, size_t srcLen, Src* dest) {
606 ch_traits<Src>::copy(dest, src, srcLen + 1);
607 return srcLen;
608 }
609};
610
611template<>
612struct utf_convert_selector<u8s, u16s> {
613 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
614 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
615};
616
617template<>
618struct utf_convert_selector<u8s, u32s> {
619 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
620 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
621};
622
623template<>
624struct utf_convert_selector<u16s, u8s> {
625 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
626 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
627};
628
629template<>
630struct utf_convert_selector<u16s, u32s> {
631 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
632 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
633};
634
635template<>
636struct utf_convert_selector<u32s, u8s> {
637 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
638 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
639};
640
641template<>
642struct utf_convert_selector<u32s, u16s> {
643 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
644 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
645};
646
661template<typename K, typename Impl>
662class from_utf_convertible {
663protected:
664 from_utf_convertible() = default;
665 using my_type = Impl;
666 /*
667 Эти методы должен реализовать класс-наследник.
668 вызывается только при создании объекта
669 init(size_t size)
670 set_size(size_t size)
671 */
672 template<typename O>
673 requires(!std::is_same_v<O, K>)
674 void init_from_utf_convertible(simple_str<O> init) {
675 using from_t = to_base_char_t<O>;
676 using to_t = to_base_char_t<K>;
677
678 using worker = utf_convert_selector<from_t, to_t>;
679 Impl* d = static_cast<Impl*>(this);
680 size_t len = init.length();
681 if (!len)
682 d->create_empty();
683 else {
684 size_t need = worker::need_len((const from_t*)init.symbols(), len);
685 K* str = d->init(need);
686 str[need] = 0;
687 worker::convert((const from_t*)init.symbols(), len, (to_t*)str);
688 }
689 }
690};
691
700template<typename From, typename To> requires (!std::is_same_v<From, To>)
701struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
702 using symb_type = To;
703 using from_t = to_base_char_t<From>;
704 using to_t = to_base_char_t<To>;
705 using worker = utf_convert_selector<from_t, to_t>;
706
707 simple_str<From> source_;
708
709 constexpr expr_utf(simple_str<From> source) : source_(source){}
710
711 size_t length() const noexcept {
712 return worker::need_len((const from_t*)source_.symbols(), source_.length());
713 }
714 To* place(To* ptr) const noexcept {
715 return ptr + worker::convert((const from_t*)source_.symbols(), source_.length(), (to_t*)ptr);
716 }
717};
718
731template<typename To, typename From> requires (!std::is_same_v<From, To>)
733 return {from};
734}
735
740template<typename A, typename K>
741concept storable_str = requires {
742 A::is_str_storable == true;
743 std::is_same_v<typename A::symb_type, K>;
744};
745
750template<typename A, typename K>
751concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
752
757template<typename A, typename K>
759
802template<typename K, typename Impl, typename Allocator>
803class str_storable : protected Allocator {
804public:
805 using my_type = Impl;
806 using traits = ch_traits<K>;
807 using allocator_t = Allocator;
808 using s_str = simple_str<K>;
809 using s_str_nt = simple_str_nt<K>;
810
811protected:
816 constexpr allocator_t& allocator() {
817 return *static_cast<Allocator*>(this);
818 }
819 constexpr const allocator_t& allocator() const {
820 return *static_cast<const Allocator*>(this);
821 }
822
823 using uni = unicode_traits<K>;
824
825 constexpr Impl& d() noexcept {
826 return *static_cast<Impl*>(this);
827 }
828 constexpr const Impl& d() const noexcept {
829 return *static_cast<const Impl*>(this);
830 }
831
838 template<typename... Args>
839 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
840
847 constexpr void init_from_str_other(s_str other) {
848 if (other.length()) {
849 K* ptr = d().init(other.length());
850 traits::copy(ptr, other.symbols(), other.length());
851 ptr[other.length()] = 0;
852 } else
853 d().create_empty();
854 }
855
863 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
864 size_t l = pattern.length(), allLen = l * repeat;
865 if (allLen) {
866 K* ptr = d().init(allLen);
867 for (size_t i = 0; i < repeat; i++) {
868 traits::copy(ptr, pattern.symbols(), l);
869 ptr += l;
870 }
871 *ptr = 0;
872 } else
873 d().create_empty();
874 }
875
883 constexpr void init_symb_repeat(size_t count, K pad) {
884 if (count) {
885 K* str = d().init(count);
886 traits::assign(str, count, pad);
887 str[count] = 0;
888 } else
889 d().create_empty();
890 }
891
903 template<StrExprForType<K> A>
904 constexpr void init_str_expr(const A& expr) {
905 size_t len = expr.length();
906 if (len)
907 *expr.place((typename A::symb_type*)d().init(len)) = 0;
908 else
909 d().create_empty();
910 }
911
925 template<StrType<K> From>
926 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
927 auto findes = f.find_all(pattern, offset, maxCount);
928 if (!findes.size()) {
929 new (this) my_type{f};
930 return;
931 }
932 size_t srcLen = f.length();
933 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
934
935 if (!newSize) {
936 new (this) my_type{};
937 return;
938 }
939
940 K* ptr = d().init(newSize);
941 const K* src = f.symbols();
942 size_t from = 0;
943 for (const auto& s: findes) {
944 size_t copyLen = s - from;
945 if (copyLen) {
946 traits::copy(ptr, src + from, copyLen);
947 ptr += copyLen;
948 }
949 if (repl.len) {
950 traits::copy(ptr, repl.str, repl.len);
951 ptr += repl.len;
952 }
953 from = s + pattern.len;
954 }
955 srcLen -= from;
956 if (srcLen) {
957 traits::copy(ptr, src + from, srcLen);
958 ptr += srcLen;
959 }
960 *ptr = 0;
961 }
962
963 template<StrType<K> From, typename Op1, typename... Args>
964 requires std::is_constructible_v<allocator_t, Args...>
965 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
966 my_type result{std::forward<Args>(args)...};
967 size_t len = f.length();
968 if (len) {
969 const K* source = f.symbols();
970 K* destination = result.init(len);
971 for (size_t l = 0; l < len; l++) {
972 destination[l] = opMakeNeedCase(source[l]);
973 }
974 }
975 return result;
976 }
977 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
978 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
979 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
980 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
981 template<typename T, bool Dummy = true>
982 struct ChangeCase {
983 template<typename From, typename Op1, typename... Args>
984 requires std::is_constructible_v<allocator_t, Args...>
985 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
986 my_type result{std::forward<Args>(args)...};
987 size_t len = f.length();
988 if (len) {
989 opChangeCase(f.symbols(), len, result.init(len));
990 }
991 return result;
992 }
993 };
994 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
995 // For utf8 we will make a separate specification, since changing the register may change the length of the string
996 template<bool Dummy>
997 struct ChangeCase<u8s, Dummy> {
998 template<typename From, typename Op1, typename... Args>
999 requires std::is_constructible_v<allocator_t, Args...>
1000 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1001 my_type result{std::forward<Args>(args)...};
1002 ;
1003 size_t len = f.length();
1004 if (len) {
1005 const K* ptr = f.symbols();
1006 K* pWrite = result.init(len);
1007
1008 const u8s* source = ptr;
1009 u8s* dest = pWrite;
1010 size_t newLen = opChangeCase(source, len, dest, len);
1011 if (newLen < len) {
1012 // Строка просто укоротилась
1013 // The string was simply shortened
1014 result.set_size(newLen);
1015 } else if (newLen > len) {
1016 // Строка не влезла в буфер.
1017 // The line did not fit into the buffer.
1018 size_t readed = static_cast<size_t>(source - ptr);
1019 size_t writed = static_cast<size_t>(dest - pWrite);
1020 pWrite = result.set_size(newLen);
1021 dest = pWrite + writed;
1022 opChangeCase(source, len - readed, dest, newLen - writed);
1023 }
1024 pWrite[newLen] = 0;
1025 }
1026 return result;
1027 }
1028 };
1029
1030public:
1031
1032 inline static constexpr bool is_str_storable = true;
1039 constexpr operator const K*() const noexcept {
1040 return d().symbols();
1041 }
1042
1050 constexpr s_str_nt to_nts(size_t from = 0) const {
1051 size_t len = d().length();
1052 if (from >= len) {
1053 from = len;
1054 }
1055 return {d().symbols() + from, len - from};
1056 }
1057
1063 constexpr operator s_str_nt() const {
1064 return {d().symbols(), d().length()};
1065 }
1066
1104 template<typename T, typename... Args>
1105 requires std::is_constructible_v<allocator_t, Args...>
1106 static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
1107 my_type result(std::forward<Args>(args)...);
1108 if (strings.size()) {
1109 if (strings.size() == 1 && (!delimiter.length() || !tail)) {
1110 result = strings.front();
1111 } else {
1112 size_t commonLen = 0;
1113 for (const auto& t: strings) {
1114 size_t len = t.length();
1115 if (len > 0 || !skip_empty) {
1116 if (commonLen > 0) {
1117 commonLen += delimiter.len;
1118 }
1119 commonLen += len;
1120 }
1121 }
1122 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1123 if (commonLen) {
1124 K* ptr = result.init(commonLen);
1125 K* write = ptr;
1126 for (const auto& t: strings) {
1127 size_t copyLen = t.length();
1128 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1129 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1130 write += delimiter.len;
1131 }
1132 ch_traits<K>::copy(write, t.symbols(), copyLen);
1133 write += copyLen;
1134 }
1135 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1136 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1137 write += delimiter.len;
1138 }
1139 *write = 0;
1140 } else {
1141 result.create_empty();
1142 }
1143 }
1144 }
1145 return result;
1146 }
1147
1155 template<StrType<K> From, typename... Args>
1156 requires std::is_constructible_v<allocator_t, Args...>
1157 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
1158 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
1159 }
1160
1168 template<StrType<K> From, typename... Args>
1169 requires std::is_constructible_v<allocator_t, Args...>
1170 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
1171 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
1172 }
1173
1185 template<StrType<K> From, typename... Args>
1186 requires std::is_constructible_v<allocator_t, Args...>
1187 static my_type upperred_from(const From& f, Args&&... args) {
1188 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
1189 }
1190
1202 template<StrType<K> From, typename... Args>
1203 requires std::is_constructible_v<allocator_t, Args...>
1204 static my_type lowered_from(const From& f, Args&&... args) {
1205 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
1206 }
1207
1223 template<StrType<K> From, typename... Args>
1224 requires std::is_constructible_v<allocator_t, Args...>
1225 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
1226 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1227 }
1228};
1229
1234template<typename A>
1235concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
1236 { a.allocate(size) } -> std::same_as<void*>;
1237 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
1238};
1239
1240struct printf_selector {
1241 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
1242 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
1243 if constexpr (sizeof(K) == 1) {
1244 #ifndef _WIN32
1245 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1246 #else
1247 // Поддерживает позиционные параметры
1248 // Supports positional parameters
1249 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1250 #endif
1251 } else {
1252 #ifndef _WIN32
1253 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1254 #else
1255 // Поддерживает позиционные параметры
1256 // Supports positional parameters
1257 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1258 #endif
1259 }
1260 }
1261 template<typename K> requires (is_one_of_std_char_v<K>)
1262 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
1263 if constexpr (std::is_same_v<K, u8s>) {
1264 #ifndef _WIN32
1265 return std::vsnprintf(buffer, count, format, args);
1266 #else
1267 // Поддерживает позиционные параметры
1268 // Supports positional parameters
1269 return _vsprintf_p(buffer, count, format, args);
1270 #endif
1271 } else {
1272 #ifndef _WIN32
1273 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1274 #else
1275 // Поддерживает позиционные параметры
1276 // Supports positional parameters
1277 return _vswprintf_p(buffer, count, format, args);
1278 #endif
1279 }
1280 }
1281};
1282
1283inline size_t grow2(size_t ret, size_t currentCapacity) {
1284 return ret <= currentCapacity ? ret : ret * 2;
1285}
1286
1325template<typename K, typename Impl>
1327public:
1328 using my_type = Impl;
1329
1330private:
1331 Impl& d() {
1332 return *static_cast<Impl*>(this);
1333 }
1334 const Impl& d() const {
1335 return *static_cast<const Impl*>(this);
1336 }
1337 size_t _len() const noexcept {
1338 return d().length();
1339 }
1340 const K* _str() const noexcept {
1341 return d().symbols();
1342 }
1343 using str_piece = simple_str<K>;
1344 using symb_type = K;
1345 using traits = ch_traits<K>;
1346 using uni = unicode_traits<K>;
1347 using uns_type = std::make_unsigned_t<K>;
1348
1349 template<typename Op>
1350 Impl& make_trim_op(const Op& op) {
1351 str_piece me = d(), pos = op(me);
1352 if (me.length() != pos.length()) {
1353 if (me.symbols() != pos.symbols())
1354 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
1355 d().set_size(pos.length());
1356 }
1357 return d();
1358 }
1359
1360 template<auto Op>
1361 Impl& commonChangeCase() {
1362 size_t len = _len();
1363 if (len)
1364 Op(_str(), len, str());
1365 return d();
1366 }
1367 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
1368 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
1369 // GCC still does not allow full specialization of a nested class within a class,
1370 // only partial. Resources additive unused parameter template.
1371 template<typename T, bool Dummy = true>
1372 struct CaseTraits {
1373 static Impl& upper(Impl& obj) {
1374 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1375 }
1376 static Impl& lower(Impl& obj) {
1377 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1378 }
1379 };
1380
1381 template<auto Op>
1382 Impl& utf8CaseChange() {
1383 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1384 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1385 size_t len = _len();
1386 if (len) {
1387 u8s* writePos = str();
1388 const u8s *startData = writePos, *readPos = writePos;
1389 size_t newLen = Op(readPos, len, writePos, len);
1390 if (newLen < len) {
1391 // Строка просто укоротилась
1392 // The string was simply shortened
1393 d().set_size(newLen);
1394 } else if (newLen > len) {
1395 // Строка не влезла в буфер.
1396 // The line did not fit into the buffer.
1397 size_t readed = static_cast<size_t>(readPos - startData);
1398 size_t writed = static_cast<size_t>(writePos - startData);
1399 d().set_size(newLen);
1400 startData = str(); // при изменении размера могло изменится | may change when resizing
1401 readPos = startData + readed;
1402 writePos = const_cast<u8s*>(startData) + writed;
1403 Op(readPos, len - readed, writePos, newLen - writed);
1404 }
1405 }
1406 return d();
1407 }
1408 template<bool Dummy>
1409 struct CaseTraits<u8s, Dummy> {
1410 static Impl& upper(Impl& obj) {
1411 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1412 }
1413 static Impl& lower(Impl& obj) {
1414 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1415 }
1416 };
1417
1418 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1419 Impl& makeTrim(T&& pattern) {
1420 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1421 }
1422
1423 template<TrimSides S, bool withSpaces>
1424 Impl& makeTrim(str_piece pattern) {
1425 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1426 }
1427
1428public:
1435 K* str() noexcept {
1436 return d().str();
1437 }
1438
1444 explicit operator K*() noexcept {
1445 return str();
1446 }
1447
1453 Impl& trim() {
1454 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1455 }
1456
1462 Impl& trim_left() {
1463 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1464 }
1465
1471 Impl& trim_right() {
1472 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1473 }
1474
1482 template<typename T, size_t N = const_lit_for<K, T>::Count>
1483 requires is_const_pattern<N>
1484 Impl& trim(T&& pattern) {
1485 return makeTrim<TrimSides::TrimAll, false>(pattern);
1486 }
1487
1495 template<typename T, size_t N = const_lit_for<K, T>::Count>
1496 requires is_const_pattern<N>
1497 Impl& trim_left(T&& pattern) {
1498 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1499 }
1500
1508 template<typename T, size_t N = const_lit_for<K, T>::Count>
1509 requires is_const_pattern<N>
1510 Impl& trim_right(T&& pattern) {
1511 return makeTrim<TrimSides::TrimRight, false>(pattern);
1512 }
1513
1521 template<typename T, size_t N = const_lit_for<K, T>::Count>
1522 requires is_const_pattern<N>
1523 Impl& trim_with_spaces(T&& pattern) {
1524 return makeTrim<TrimSides::TrimAll, true>(pattern);
1525 }
1526
1534 template<typename T, size_t N = const_lit_for<K, T>::Count>
1535 requires is_const_pattern<N>
1536 Impl& trim_left_with_spaces(T&& pattern) {
1537 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1538 }
1539
1547 template<typename T, size_t N = const_lit_for<K, T>::Count>
1548 requires is_const_pattern<N>
1549 Impl& trim_right_with_wpaces(T&& pattern) {
1550 return makeTrim<TrimSides::TrimRight, true>(pattern);
1551 }
1552
1560 Impl& trim(str_piece pattern) {
1561 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1562 }
1563
1571 Impl& trim_left(str_piece pattern) {
1572 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1573 }
1574
1582 Impl& trim_right(str_piece pattern) {
1583 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1584 }
1585
1593 Impl& trim_with_spaces(str_piece pattern) {
1594 return makeTrim<TrimSides::TrimAll, true>(pattern);
1595 }
1596
1604 Impl& trim_left_with_spaces(str_piece pattern) {
1605 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1606 }
1607
1615 Impl& trim_right_with_spaces(str_piece pattern) {
1616 return makeTrim<TrimSides::TrimRight, true>(pattern);
1617 }
1618
1625 K* ptr = str();
1626 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1627 K s = *ptr;
1628 if (isAsciiLower(s))
1629 *ptr = s & ~0x20;
1630 }
1631 return d();
1632 }
1633
1640 K* ptr = str();
1641 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1642 K s = *ptr;
1643 if (isAsciiUpper(s))
1644 *ptr = s | 0x20;
1645 }
1646 return d();
1647 }
1648
1658 Impl& upper() {
1659 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1660 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1661 return CaseTraits<K>::upper(d());
1662 }
1663
1673 Impl& lower() {
1674 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1675 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1676 return CaseTraits<K>::lower(d());
1677 }
1678
1679private:
1680 template<typename T>
1681 Impl& changeImpl(size_t from, size_t len, T expr) {
1682 size_t myLen = _len();
1683 if (from > myLen) {
1684 from = myLen;
1685 }
1686 if (from + len > myLen) {
1687 len = myLen - from;
1688 }
1689 K* buffer = str();
1690 size_t otherLen = expr.length();
1691 if (len == otherLen) {
1692 expr.place(buffer + from);
1693 } else {
1694 size_t tailLen = myLen - from - len;
1695 if (len > otherLen) {
1696 expr.place(buffer + from);
1697 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1698 d().set_size(myLen - (len - otherLen));
1699 } else {
1700 buffer = d().set_size(myLen + otherLen - len);
1701 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1702 expr.place(buffer + from);
1703 }
1704 }
1705 return d();
1706 }
1707
1708 template<typename T>
1709 Impl& appendImpl(T expr) {
1710 if (size_t len = expr.length(); len) {
1711 size_t size = _len();
1712 expr.place(d().set_size(size + len) + size);
1713 }
1714 return d();
1715 }
1716
1717 template<typename T>
1718 Impl& appendFromImpl(size_t pos, T expr) {
1719 if (pos > _len())
1720 pos = _len();
1721 if (size_t len = expr.length())
1722 expr.place(d().set_size(pos + len) + pos);
1723 else
1724 d().set_size(pos);
1725 return d();
1726 }
1727
1728public:
1729 inline static constexpr bool is_str_mutable = true;
1738 Impl& append(str_piece other) {
1739 return appendImpl<str_piece>(other);
1740 }
1741
1749 template<StrExprForType<K> A>
1750 Impl& append(const A& expr) {
1751 return appendImpl<const A&>(expr);
1752 }
1753
1761 Impl& operator+=(str_piece other) {
1762 return appendImpl<str_piece>(other);
1763 }
1764
1772 template<StrExprForType<K> A>
1773 Impl& operator+=(const A& expr) {
1774 return appendImpl<const A&>(expr);
1775 }
1776
1790 Impl& append_in(size_t pos, str_piece other) {
1791 return appendFromImpl<str_piece>(pos, other);
1792 }
1793
1807 template<StrExprForType<K> A>
1808 Impl& append_in(size_t pos, const A& expr) {
1809 return appendFromImpl<const A&>(pos, expr);
1810 }
1811
1823 Impl& change(size_t from, size_t len, str_piece other) {
1824 return changeImpl<str_piece>(from, len, other);
1825 }
1826
1838 template<StrExprForType<K> A>
1839 Impl& change(size_t from, size_t len, const A& expr) {
1840 return changeImpl<const A&>(from, len, expr);
1841 }
1842
1852 Impl& insert(size_t to, str_piece other) {
1853 return changeImpl<str_piece>(to, 0, other);
1854 }
1855
1865 template<StrExprForType<K> A>
1866 Impl& insert(size_t to, const A& expr) {
1867 return changeImpl<const A&>(to, 0, expr);
1868 }
1869
1879 Impl& remove(size_t from, size_t len) {
1880 return changeImpl<const empty_expr<K>&>(from, len, {});
1881 }
1882
1890 Impl& prepend(str_piece other) {
1891 return changeImpl<str_piece>(0, 0, other);
1892 }
1893
1901 template<StrExprForType<K> A>
1902 Impl& prepend(const A& expr) {
1903 return changeImpl<const A&>(0, 0, expr);
1904 }
1905
1919 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
1920 offset = d().find(pattern, offset);
1921 if (offset == str::npos) {
1922 return d();
1923 }
1924 if (!maxCount)
1925 maxCount--;
1926 size_t replLength = repl.length(), patternLength = pattern.length();
1927
1928 if (patternLength == replLength) {
1929 // Заменяем inplace на подстроку такой же длины
1930 // Replace inplace with a substring of the same length
1931 K* ptr = str();
1932 while (maxCount--) {
1933 traits::copy(ptr + offset, repl.symbols(), replLength);
1934 offset = d().find(pattern, offset + replLength);// replLength == patternLength
1935 if (offset == str::npos)
1936 break;
1937 }
1938 } else if (patternLength > replLength) {
1939 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
1940 // Replace with a shorter piece, the length of the text will decrease, go from left to right
1941 K* ptr = str();
1942 traits::copy(ptr + offset, repl.symbols(), replLength);
1943 size_t posWrite = offset + replLength;
1944 offset += patternLength;
1945
1946 while (--maxCount) {
1947 size_t idx = d().find(pattern, offset);
1948 if (idx == str::npos)
1949 break;
1950 size_t lenOfPiece = idx - offset;
1951 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
1952 posWrite += lenOfPiece;
1953 traits::copy(ptr + posWrite, repl.symbols(), replLength);
1954 posWrite += replLength;
1955 offset = idx + patternLength;
1956 }
1957 size_t tailLen = _len() - offset;
1958 traits::move(ptr + posWrite, ptr + offset, tailLen);
1959 d().set_size(posWrite + tailLen);
1960 } else {
1961 struct replace_grow_helper {
1962 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
1963 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
1964 my_type& source;
1965 const str_piece pattern;
1966 const str_piece repl;
1967 size_t maxCount;
1968 const size_t delta;
1969 size_t all_delta{};
1970 K* reserve_for_copy{};
1971 size_t end_of_piece{};
1972 size_t total_length{};
1973
1974 void replace(size_t offset) {
1975 size_t found[16] = {offset};
1976 maxCount--;
1977 offset += pattern.length();
1978 all_delta += delta;
1979 size_t idx = 1;
1980 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
1981 found[idx] = source.find(pattern, offset);
1982 if (found[idx] == str::npos) {
1983 break;
1984 }
1985 offset = found[idx] + pattern.length();
1986 all_delta += delta;
1987 }
1988 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
1989 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
1990 }
1991 // Теперь делаем свои замены
1992 // Now we make our replacements
1993 if (!reserve_for_copy) {
1994 // Только начинаем
1995 // Just getting started
1996 end_of_piece = source.length();
1997 total_length = end_of_piece + all_delta;
1998 reserve_for_copy = source.alloc_for_copy(total_length);
1999 }
2000 K* dst_start = reserve_for_copy;
2001 const K* src_start = source.symbols();
2002 while(idx-- > 0) {
2003 size_t pos = found[idx] + pattern.length();
2004 size_t lenOfPiece = end_of_piece - pos;
2005 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2006 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
2007 all_delta -= delta;
2008 end_of_piece = found[idx];
2009 }
2010 if (!all_delta && reserve_for_copy != src_start) {
2011 ch_traits<K>::copy(dst_start, src_start, found[0]);
2012 }
2013 }
2014 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
2015 helper.replace(offset);
2016 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2017 }
2018 return d();
2019 }
2020
2036 template<StrType<K> From>
2037 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2038 if (pattern.length() >= repl.length()) {
2039 K* dst = d().reserve_no_preserve(f.length());
2040 const K* src = f.symbols();
2041 size_t delta = 0;
2042 if (maxCount == 0) {
2043 maxCount--;
2044 }
2045 size_t src_length = f.length(), start = 0;
2046 while (maxCount--) {
2047 offset = f.find(pattern, offset);
2048 if (offset == str::npos) {
2049 break;
2050 }
2051 size_t piece_len = offset - start;
2052 if (piece_len) {
2053 ch_traits<K>::copy(dst, src + start, piece_len);
2054 dst += piece_len;
2055 }
2056 if (repl.length()) {
2057 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
2058 dst += repl.length();
2059 }
2060 delta += pattern.length() - repl.length();
2061 offset += pattern.length();
2062 start = offset;
2063 }
2064 if (start < src_length) {
2065 ch_traits<K>::copy(dst, src + start, src_length - start);
2066 }
2067 d().set_size(src_length - delta);
2068 } else {
2069 d() = f;
2070 replace(pattern, repl, offset, maxCount);
2071 }
2072 return d();
2073 }
2074
2098 template<typename Op>
2099 Impl& fill(size_t from, const Op& fillFunction) {
2100 size_t size = _len();
2101 if (from > size)
2102 from = size;
2103 size_t capacity = d().capacity();
2104 K* ptr = str();
2105 capacity -= from;
2106 for (;;) {
2107 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
2108 if (capacity >= needSize) {
2109 d().set_size(from + needSize);
2110 break;
2111 }
2112 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2113 capacity = d().capacity() - from;
2114 }
2115 return d();
2116 }
2117
2125 template<typename Op>
2126 requires std::is_invocable_v<Op, K*, size_t>
2127 Impl& operator<<(const Op& fillFunction) {
2128 return fill(0, fillFunction);
2129 }
2130
2138 template<typename Op>
2139 requires std::is_invocable_v<Op, K*, size_t>
2140 Impl& operator<<=(const Op& fillFunction) {
2141 return fill(_len(), fillFunction);
2142 }
2143
2151 template<typename Op>
2152 requires std::is_invocable_v<Op, my_type&>
2153 Impl& operator<<(const Op& fillFunction) {
2154 fillFunction(d());
2155 return d();
2156 }
2157
2171 template<typename... T> requires (is_one_of_std_char_v<K>)
2172 Impl& printf_from(size_t from, const K* format, T&&... args) {
2173 size_t size = _len();
2174 if (from > size)
2175 from = size;
2176 size_t capacity = d().capacity();
2177 K* ptr = str();
2178 capacity -= from;
2179
2180 int result = 0;
2181 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
2182 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
2183 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
2184 // 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
2185 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
2186 // Therefore, for them you need to stupidly increase the buffer at random until it fits
2187 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
2188 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2189 if (result > (int)capacity) {
2190 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2191 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
2192 }
2193 } else {
2194 for (;;) {
2195 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2196 if (result < 0) {
2197 // Не хватило буфера или ошибка конвертации.
2198 // Попробуем увеличить буфер в два раза
2199 // Not enough buffer or conversion error.
2200 // Let's try to double the buffer
2201 capacity *= 2;
2202 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2203 } else
2204 break;
2205 }
2206 }
2207 if (result < 0)
2208 d().set_size(static_cast<size_t>(traits::length(_str())));
2209 else
2210 d().set_size(from + result);
2211 return d();
2212 }
2213
2225 template<typename... T> requires (is_one_of_std_char_v<K>)
2226 Impl& printf(const K* format, T&&... args) {
2227 return printf_from(0, format, std::forward<T>(args)...);
2228 }
2229
2241 template<typename... T> requires (is_one_of_std_char_v<K>)
2242 Impl& append_printf(const K* format, T&&... args) {
2243 return printf_from(_len(), format, std::forward<T>(args)...);
2244 }
2245
2246 struct writer {
2247 my_type* store;
2248 K* ptr;
2249 const K* end;
2250 size_t max_write;
2251 size_t writed = 0;
2252 inline static K pad;
2253 K& operator*() const {
2254 return *ptr;
2255 }
2256 writer& operator++() {
2257 if (writed < max_write) {
2258 ++ptr;
2259 if (ptr == end) {
2260 size_t l = ptr - store->begin();
2261 store->set_size(l);
2262 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
2263 end = store->end();
2264 }
2265 } else {
2266 ptr = &pad;
2267 }
2268 return *this;
2269 }
2270 writer operator++(int) {
2271 auto ret = *this;
2272 operator++();
2273 return ret;
2274 }
2275
2276 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2277 writer() = default;
2278 writer(const writer&) = delete;
2279 writer& operator=(const writer&) noexcept = delete;
2280 writer(writer&&) noexcept = default;
2281 writer& operator=(writer&&) noexcept = default;
2282 using difference_type = int;
2283 };
2284 using fmt_type = to_std_char_t<K>;
2299 template<typename... T> requires (is_one_of_std_char_v<K>)
2300 Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
2301 size_t size = _len();
2302 if (from > size)
2303 from = size;
2304 size_t capacity = d().capacity();
2305 K* ptr = str();
2306
2307 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2308 std::forward<decltype(format)>(format), std::forward<T>(args)...);
2309 d().set_size(result.ptr - _str());
2310 return d();
2311 }
2312
2328 template<typename... T> requires (is_one_of_std_char_v<K>)
2329 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
2330 size_t size = _len();
2331 if (from > size)
2332 from = size;
2333 size_t capacity = d().capacity();
2334 K* ptr = str();
2335
2336 if constexpr (std::is_same_v<K, u8s>) {
2337 auto result = std::vformat_to(
2338 writer{d(), ptr + from, ptr + capacity, max_write},
2339 std::basic_string_view<K>{format.symbols(), format.length()},
2340 std::make_format_args(args...));
2341 d().set_size(result.ptr - _str());
2342 } else {
2343 auto result = std::vformat_to(
2344 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2345 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
2346 std::make_wformat_args(std::forward<T>(args)...));
2347 d().set_size(result.ptr - _str());
2348 }
2349 return d();
2350 }
2351
2363 template<typename... T> requires (is_one_of_std_char_v<K>)
2364 Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
2365 return format_from(0, pattern, std::forward<T>(args)...);
2366 }
2367
2379 template<typename... T> requires (is_one_of_std_char_v<K>)
2380 Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
2381 return format_from(_len(), format, std::forward<T>(args)...);
2382 }
2383
2395 template<typename... T> requires (is_one_of_std_char_v<K>)
2396 Impl& vformat(str_piece format, T&&... args) {
2397 return vformat_from(0, -1, format, std::forward<T>(args)...);
2398 }
2399
2411 template<typename... T> requires (is_one_of_std_char_v<K>)
2412 Impl& append_vformatted(str_piece format, T&&... args) {
2413 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
2414 }
2415
2429 template<typename... T> requires (is_one_of_std_char_v<K>)
2430 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
2431 return vformat_from(0, max_write, format, std::forward<T>(args)...);
2432 }
2433
2447 template<typename... T> requires (is_one_of_std_char_v<K>)
2448 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
2449 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
2450 }
2451
2461 template<typename Op, typename... Args>
2462 Impl& with(const Op& fillFunction, Args&&... args) {
2463 fillFunction(d(), std::forward<Args>(args)...);
2464 return d();
2465 }
2466};
2467
2468template<typename K>
2469struct SharedStringData {
2470 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
2471
2472 SharedStringData() {
2473 ref_ = 1;
2474 }
2475 K* str() const {
2476 return (K*)(this + 1);
2477 }
2478 void incr() {
2479 ref_.fetch_add(1, std::memory_order_relaxed);
2480 }
2481 void decr(Allocatorable auto& allocator) {
2482 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2483 if (val == 1) {
2484 allocator.deallocate(this);
2485 }
2486 }
2487 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
2488 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
2489 return new (allocator.allocate(size)) SharedStringData();
2490 }
2491 static SharedStringData<K>* from_str(const K* p) {
2492 return (SharedStringData<K>*)p - 1;
2493 }
2494 K* place(K* p, size_t len) {
2495 ch_traits<K>::copy(p, str(), len);
2496 return p + len;
2497 }
2498};
2499
2500// Дефолтный аллокатор для строк, может работать статически
2501// Default allocator for strings, can work statically
2502class string_common_allocator {
2503public:
2504 void* allocate(size_t bytes) {
2505 return new char[bytes];
2506 }
2507 void deallocate(void* address) noexcept {
2508 delete [] static_cast<char*>(address);
2509 }
2510};
2511
2512string_common_allocator default_string_allocator_selector(...);
2513// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
2514// объявите функцию
2515// ваш_тип_аллокатора default_string_allocator_selector(int);
2516// If you want to set your default allocator for strings, before including sstring.h
2517// declare a function
2518// your_allocator_type default_string_allocator_selector(int);
2519using allocator_string = decltype(default_string_allocator_selector(int(0)));
2520
2521template<typename K, Allocatorable Allocator>
2522class sstring;
2523
2524/*
2525* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
2526* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
2527* Укажем ему явно.
2528* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
2529* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
2530* Let's tell him explicitly.
2531*/
2532
2553template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
2554class decl_empty_bases lstring :
2555 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2556 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2557 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2558 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2559 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2560public:
2561 using symb_type = K;
2563 using allocator_t = Allocator;
2564
2565 enum : size_t {
2567 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
2568 };
2569
2570protected:
2571 enum : size_t {
2572 extra = forShared ? sizeof(SharedStringData<K>) : 0,
2573 };
2574
2575 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
2576 using base_storable = str_storable<K, my_type, Allocator>;
2577 using base_mutable = str_mutable<K, my_type>;
2578 using base_utf = from_utf_convertible<K, my_type>;
2579 using traits = ch_traits<K>;
2580 using s_str = base_storable::s_str;
2581
2582 friend base_storable;
2583 friend base_mutable;
2584 friend base_utf;
2585 friend class sstring<K, Allocator>;
2586
2587 K* data_;
2588 size_t size_;
2589
2590 union {
2591 size_t capacity_;
2592 K local_[LocalCapacity + 1];
2593 };
2594
2595 constexpr void create_empty() {
2596 data_ = local_;
2597 size_ = 0;
2598 local_[0] = 0;
2599 }
2600 constexpr static size_t calc_capacity(size_t s) {
2601 const int al = alignof(std::max_align_t) < 16 ? 16 : alignof(std::max_align_t);
2602 size_t real_need = (s + 1) * sizeof(K) + extra;
2603 size_t aligned_alloced = (real_need + al - 1) / al * al;
2604 return (aligned_alloced - extra) / sizeof(K) - 1;
2605 }
2606
2607 constexpr K* init(size_t s) {
2608 size_t need_cap = s;
2609 if (need_cap > LocalCapacity) {
2610 need_cap = calc_capacity(s);
2611 data_ = alloc_place(need_cap);
2612 capacity_ = need_cap;
2613 } else {
2614 data_ = local_;
2615 }
2616 size_ = s;
2617 return str();
2618 }
2619 // Методы для себя | Methods for yourself
2620 constexpr bool is_alloced() const noexcept {
2621 return data_ != local_;
2622 }
2623
2624 constexpr void dealloc() {
2625 if (is_alloced()) {
2626 base_storable::allocator().deallocate(to_real_address(data_));
2627 data_ = local_;
2628 }
2629 }
2630
2631 constexpr static K* to_real_address(void* ptr) {
2632 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
2633 }
2634 constexpr static K* from_real_address(void* ptr) {
2635 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
2636 }
2637
2638 constexpr K* alloc_place(size_t newSize) {
2639 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
2640 }
2641 // Вызывается при replace, когда меняют на более длинную замену
2642 // Called on replace when changing to a longer replacement
2643 constexpr K* alloc_for_copy(size_t newSize) {
2644 if (capacity() >= newSize) {
2645 // Замена войдёт в текущий буфер
2646 // Replacement will go into the current buffer
2647 return data_;
2648 }
2649 return alloc_place(calc_capacity(newSize));
2650 }
2651 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
2652 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
2653 constexpr void set_from_copy(K* ptr, size_t newSize) {
2654 if (ptr != data_) {
2655 // Да, копировали в новый буфер
2656 // Yes, copied to a new buffer
2657 dealloc();
2658 data_ = ptr;
2659 capacity_ = calc_capacity(newSize);
2660 }
2661 size_ = newSize;
2662 data_[newSize] = 0;
2663 }
2664
2665public:
2672 template<typename... Args>
2673 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
2674 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2675 : base_storable(std::forward<Args>(args)...) {
2676 create_empty();
2677 }
2678
2687 template<typename... Args>
2688 requires std::is_constructible_v<allocator_t, Args...>
2689 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2691 }
2692
2702 template<typename... Args>
2703 requires std::is_constructible_v<allocator_t, Args...>
2704 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2705 base_storable::init_str_repeat(repeat, pattern);
2706 }
2707
2717 template<typename... Args>
2718 requires std::is_constructible_v<allocator_t, Args...>
2719 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2721 }
2722
2736 template<typename... Args>
2737 requires std::is_constructible_v<allocator_t, Args...>
2738 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2740 }
2741
2757 template<StrType<K> From, typename... Args>
2758 requires std::is_constructible_v<allocator_t, Args...>
2759 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2760 : base_storable(std::forward<Args>(args)...) {
2761 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
2762 }
2763
2764 constexpr lstring() {
2765 create_empty();
2766 }
2767
2768 constexpr ~lstring() {
2769 dealloc();
2770 }
2771
2778 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
2779 if (other.size_) {
2780 K* buf = init(other.size_);
2781 const size_t short_str = 16 / sizeof(K);
2782 if (LocalCapacity >= short_str - 1 && other.size_ < short_str) {
2783 struct copy { char buf[16]; };
2784 *reinterpret_cast<copy*>(buf) = *reinterpret_cast<const copy*>(other.symbols());
2785 } else {
2786 traits::copy(buf, other.symbols(), other.size_ + 1);
2787 }
2788 }
2789 }
2790
2798 template<typename... Args>
2799 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2800 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2801 if (other.size_) {
2802 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2803 }
2804 }
2805
2813 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
2814 requires std::is_constructible_v<allocator_t, Args...>
2815 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2816 if constexpr (I > 1) {
2817 K* ptr = init(I - 1);
2818 traits::copy(ptr, (const K*)value, I - 1);
2819 ptr[I - 1] = 0;
2820 } else
2821 create_empty();
2822 }
2823
2829 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
2830 if (other.size_) {
2831 size_ = other.size_;
2832 if (other.is_alloced()) {
2833 data_ = other.data_;
2834 capacity_ = other.capacity_;
2835 } else {
2836 data_ = local_;
2837 traits::copy(local_, other.local_, size_ + 1);
2838 }
2839 other.data_ = other.local_;
2840 other.size_ = 0;
2841 other.local_[0] = 0;
2842 }
2843 }
2844
2852 template<typename Op, typename... Args>
2853 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
2854 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2855 create_empty();
2856 this->operator<<(op);
2857 }
2858 template<typename O>
2859 requires(!std::is_same_v<O, K>)
2860 lstring(simple_str<O> init) {
2861 this->init_from_utf_convertible(init);
2862 }
2863
2864 template<typename O, typename I, bool M>
2865 requires(!std::is_same_v<O, K>)
2866 lstring(const str_algs<O, simple_str<O>, I, M>& init) {
2867 this->init_from_utf_convertible(init.to_str());
2868 }
2869
2870 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
2871 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
2872 // 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
2873 // Therefore, we implement the copy and move assignment separately
2874
2883 my_type& operator=(const my_type& other) {
2884 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
2885 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
2886 if (&other != this) {
2887 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
2888 size_ = other.size_;
2889 }
2890 return *this;
2891 }
2892
2900 my_type& operator=(my_type&& other) noexcept {
2901 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
2902 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
2903 if (&other != this) {
2904 dealloc();
2905 if (other.is_alloced()) {
2906 data_ = other.data_;
2907 capacity_ = other.capacity_;
2908 } else {
2909 traits::copy(data_, other.local_, other.size_ + 1);
2910 }
2911 base_storable::allocator() = std::move(other.allocator());
2912 size_ = other.size_;
2913 other.create_empty();
2914 }
2915 return *this;
2916 }
2917
2918 my_type& assign(const K* other, size_t len) {
2919 if (len) {
2920 bool isIntersect = other >= data_ && other + len <= data_ + size_;
2921 if (isIntersect) {
2922 // Особый случай, нам пытаются присвоить кусок нашей же строки.
2923 // Просто переместим текст в буфере, и установим новый размер
2924 // A special case, they are trying to assign us a piece of our own string.
2925 // Just move the text in the buffer and set a new size
2926 if (other > data_) {
2927 traits::move(data_, other, len);
2928 }
2929 } else {
2930 traits::copy(reserve_no_preserve(len), other, len);
2931 }
2932 }
2933 size_ = len;
2934 data_[size_] = 0;
2935 return *this;
2936 }
2945 my_type& operator=(simple_str<K> other) {
2946 return assign(other.str, other.len);
2947 }
2948
2956 template<typename T, size_t S = const_lit_for<K, T>::Count>
2957 my_type& operator=(T&& other) {
2958 return assign((const K*)other, S - 1);
2959 }
2960
2970 my_type& operator=(const StrExprForType<K> auto& expr) {
2971 size_t newLen = expr.length();
2972 if (newLen) {
2973 expr.place(reserve_no_preserve(newLen));
2974 }
2975 size_ = newLen;
2976 data_[size_] = 0;
2977 return *this;
2978 }
2979
2980 constexpr size_t length() const noexcept {
2981 return size_;
2982 }
2983
2984 constexpr const K* symbols() const noexcept {
2985 return data_;
2986 }
2987
2988 constexpr K* str() noexcept {
2989 return data_;
2990 }
2991
2992 constexpr bool is_empty() const noexcept {
2993 return size_ == 0;
2994 }
2995
2996 constexpr bool empty() const noexcept {
2997 return size_ == 0;
2998 }
2999
3000 constexpr size_t capacity() const noexcept {
3001 return is_alloced() ? capacity_ : LocalCapacity;
3002 }
3003
3015 constexpr K* reserve_no_preserve(size_t newSize) {
3016 if (newSize > capacity()) {
3017 newSize = calc_capacity(newSize);
3018 K* newData = alloc_place(newSize);
3019 dealloc();
3020 data_ = newData;
3021 capacity_ = newSize;
3022 }
3023 return data_;
3024 }
3025
3037 constexpr K* reserve(size_t newSize) {
3038 if (newSize > capacity()) {
3039 newSize = calc_capacity(newSize);
3040 K* newData = alloc_place(newSize);
3041 traits::copy(newData, data_, size_);
3042 dealloc();
3043 data_ = newData;
3044 capacity_ = newSize;
3045 }
3046 return data_;
3047 }
3048
3060 constexpr K* set_size(size_t newSize) {
3061 size_t cap = capacity();
3062 if (newSize > cap) {
3063 size_t needPlace = newSize;
3064 if (needPlace < (cap + 1) * 2) {
3065 needPlace = (cap + 1) * 2 - 1;
3066 }
3067 reserve(needPlace);
3068 }
3069 size_ = newSize;
3070 data_[newSize] = 0;
3071 return data_;
3072 }
3073
3077 constexpr bool is_local() const noexcept {
3078 return !is_alloced();
3079 }
3080
3086 constexpr void define_size() {
3087 size_t cap = capacity();
3088 for (size_t i = 0; i < cap; i++) {
3089 if (data_[i] == 0) {
3090 size_ = i;
3091 return;
3092 }
3093 }
3094 size_ = cap;
3095 data_[size_] = 0;
3096 }
3097
3103 constexpr void shrink_to_fit() {
3104 size_t need_capacity = calc_capacity(size_);
3105 if (is_alloced() && capacity_ > need_capacity) {
3106 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
3107 traits::copy(newData, data_, size_ + 1);
3108 base_storable::allocator().deallocate(to_real_address(data_));
3109 data_ = newData;
3110
3111 if (size_ > LocalCapacity) {
3112 capacity_ = need_capacity;
3113 }
3114 }
3115 }
3116
3117 constexpr void clear() {
3118 set_size(0);
3119 }
3120
3121 constexpr void reset() {
3122 dealloc();
3123 local_[0] = 0;
3124 size_ = 0;
3125 }
3126};
3127
3128template<size_t N = 15>
3129using lstringa = lstring<u8s, N>;
3130template<size_t N = 15>
3131using lstringb = lstring<ubs, N>;
3132template<size_t N = 15>
3133using lstringw = lstring<wchar_t, N>;
3134template<size_t N = 15>
3135using lstringu = lstring<u16s, N>;
3136template<size_t N = 15>
3137using lstringuu = lstring<u32s, N>;
3138
3139template<size_t N = 15>
3140using lstringsa = lstring<u8s, N, true>;
3141template<size_t N = 15>
3142using lstringsb = lstring<ubs, N, true>;
3143template<size_t N = 15>
3144using lstringsw = lstring<wchar_t, N, true>;
3145template<size_t N = 15>
3146using lstringsu = lstring<u16s, N, true>;
3147template<size_t N = 15>
3148using lstringsuu = lstring<u32s, N, true>;
3149
3150
3151template<typename T, typename K = typename const_lit<T>::symb_type>
3152auto getLiteralType(T&&) {
3153 return K{};
3154};
3155
3156template<size_t Arch, size_t L>
3157inline constexpr const size_t _local_count = 0;
3158
3159template<>
3160inline constexpr const size_t _local_count<8, 1> = 23;
3161template<>
3162inline constexpr const size_t _local_count<8, 2> = 15;
3163template<>
3164inline constexpr const size_t _local_count<8, 4> = 7;
3165template<>
3166inline constexpr const size_t _local_count<4, 1> = 15;
3167template<>
3168inline constexpr const size_t _local_count<4, 2> = 11;
3169template<>
3170inline constexpr const size_t _local_count<4, 4> = 5;
3171
3172template<typename T>
3173constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
3174
3227template<typename K, Allocatorable Allocator = allocator_string>
3228class decl_empty_bases sstring :
3229 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3230 public str_storable<K, sstring<K, Allocator>, Allocator>,
3231 public null_terminated<K, sstring<K, Allocator>>,
3232 public from_utf_convertible<K, sstring<K, Allocator>> {
3233public:
3234 using symb_type = K;
3235 using uns_type = std::make_unsigned_t<K>;
3236 using my_type = sstring<K, Allocator>;
3237 using allocator_t = Allocator;
3238
3239 enum { LocalCount = local_count<K> };
3240
3241protected:
3242 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
3243 using base_storable = str_storable<K, my_type, Allocator>;
3244 using base_utf = from_utf_convertible<K, my_type>;
3245 using traits = ch_traits<K>;
3246 using uni = unicode_traits<K>;
3247 using s_str = base_storable::s_str;
3248
3249 friend base_storable;
3250 friend base_utf;
3251
3252 enum Types { Local, Constant, Shared };
3253
3254 union {
3255 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
3256 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
3257 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
3258 // и в итоге после символов строки получается 0, как и надо!
3259 // When we have a short string, it lies in the object itself, and in localRemain
3260 // writes how many more characters can be entered. When a line takes up everything
3261 // possible location, then localRemain becomes 0, type in this case is also 0,
3262 // and as a result, after the characters of the line we get 0, as it should!
3263 struct {
3264 K buf_[LocalCount]; // Локальный буфер строки | Local line buffer
3265 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3266 uns_type type_ : 2;
3267 };
3268 struct {
3269 union {
3270 // Указатель на конcтантную строку | Pointer to a constant string
3271 const K* cstr_;
3272 // Указатель на строку, перед которой лежит SharedStringData
3273 // Pointer to the string preceded by SharedStringData
3274 const K* sstr_;
3275 };
3276 size_t bigLen_; // Длина не локальной строки | Non-local string length
3277 };
3278 };
3279
3280 constexpr void create_empty() {
3281 type_ = Local;
3282 localRemain_ = LocalCount;
3283 buf_[0] = 0;
3284 }
3285 constexpr K* init(size_t s) {
3286 if (s > LocalCount) {
3287 type_ = Shared;
3288 localRemain_ = 0;
3289 bigLen_ = s;
3290 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
3291 return (K*)sstr_;
3292 } else {
3293 type_ = Local;
3294 localRemain_ = LocalCount - s;
3295 return buf_;
3296 }
3297 }
3298
3299 K* set_size(size_t newSize) {
3300 // Вызывается при создании строки при необходимости изменить размер.
3301 // Других ссылок на shared buffer нет.
3302 // Called when a string is created and needs to be resized.
3303 // There are no other references to the shared buffer.
3304 size_t size = length();
3305 if (newSize != size) {
3306 if (type_ == Constant) {
3307 bigLen_ = newSize;
3308 } else {
3309 if (newSize <= LocalCount) {
3310 if (type_ == Shared) {
3311 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
3312 traits::copy(buf_, sstr_, newSize);
3313 str_buf->decr(base_storable::allocator());
3314 }
3315 type_ = Local;
3316 localRemain_ = LocalCount - newSize;
3317 } else {
3318 if (type_ == Shared) {
3319 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
3320 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3321 traits::copy(newStr, sstr_, newSize);
3322 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3323 sstr_ = newStr;
3324 }
3325 } else if (type_ == Local) {
3326 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3327 if (size)
3328 traits::copy(newStr, buf_, size);
3329 sstr_ = newStr;
3330 type_ = Shared;
3331 localRemain_ = 0;
3332 }
3333 bigLen_ = newSize;
3334 }
3335 }
3336 }
3337 K* str = type_ == Local ? buf_ : (K*)sstr_;
3338 str[newSize] = 0;
3339 return str;
3340 }
3341
3342public:
3343
3344 sstring() {
3345 create_empty();
3346 }
3347
3354 template<typename... Args>
3355 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
3356 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3357 : base_storable(std::forward<Args>(args)...) {
3358 create_empty();
3359 }
3360
3369 template<typename... Args>
3370 requires std::is_constructible_v<allocator_t, Args...>
3371 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3373 }
3374
3384 template<typename... Args>
3385 requires std::is_constructible_v<allocator_t, Args...>
3386 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3387 base_storable::init_str_repeat(repeat, pattern);
3388 }
3389
3399 template<typename... Args>
3400 requires std::is_constructible_v<allocator_t, Args...>
3401 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3403 }
3404
3418 template<typename... Args>
3419 requires std::is_constructible_v<allocator_t, Args...>
3420 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3422 }
3423
3439 template<StrType<K> From, typename... Args>
3440 requires std::is_constructible_v<allocator_t, Args...>
3441 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3442 : base_storable(std::forward<Args>(args)...) {
3443 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3444 }
3445
3446 static const sstring<K> empty_str;
3448 constexpr ~sstring() {
3449 if (type_ == Shared) {
3450 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3451 }
3452 }
3453
3459 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
3460 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3461 if (type_ == Shared)
3462 SharedStringData<K>::from_str(sstr_)->incr();
3463 }
3464
3470 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3471 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3472 other.create_empty();
3473 }
3474
3485 template<size_t N>
3486 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
3487 size_t size = src.length();
3488 if (size) {
3489 if (src.is_alloced()) {
3490 // Там динамический буфер, выделенный с запасом для SharedStringData.
3491 // There is a dynamic buffer allocated with a reserve for SharedStringData.
3492 K* str = src.str();
3493 if (size > LocalCount) {
3494 // Просто присвоим его себе.
3495 // Let's just assign it to ourselves.
3496 sstr_ = str;
3497 bigLen_ = size;
3498 type_ = Shared;
3499 localRemain_ = 0;
3500 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
3501 } else {
3502 // Скопируем локально
3503 // Copy locally
3504 type_ = Local;
3505 localRemain_ = LocalCount - size;
3506 traits::copy(buf_, str, size + 1);
3507 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
3508 // Let's free that buffer; a local string's buffer is not shared with others
3509 src.dealloc();
3510 }
3511 } else {
3512 // Копируем из локального буфера
3513 // Copy from local buffer
3514 K* str = init(src.size_);
3515 traits::copy(str, src.symbols(), size + 1);
3516 }
3517 src.create_empty();
3518 } else
3519 create_empty();
3520 }
3521
3532 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
3533 requires std::is_constructible_v<allocator_t, Args...>
3534 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3535 type_ = Constant;
3536 localRemain_ = 0;
3537 cstr_ = (const K*)s;
3538 bigLen_ = N - 1;
3539 }
3540
3546 template<typename O> requires(!std::is_same_v<O, K>)
3548 this->init_from_utf_convertible(init);
3549 }
3550
3551 template<typename O, typename I, bool M> requires(!std::is_same_v<O, K>)
3552 sstring(const str_algs<O, simple_str<O>, I, M>& init) {
3553 this->init_from_utf_convertible(init.to_str());
3554 }
3555
3556 constexpr void swap(my_type&& other) noexcept {
3557 char buf[sizeof(buf_) + sizeof(K)];
3558 memcpy(buf, buf_, sizeof(buf));
3559 memcpy(buf_, other.buf_, sizeof(buf));
3560 memcpy(other.buf_, buf, sizeof(buf));
3561
3562 std::swap(base_storable::allocator(), other.allocator());
3563 }
3572 constexpr my_type& operator=(my_type other) noexcept {
3573 swap(std::move(other));
3574 return *this;
3575 }
3576
3584 constexpr my_type& operator=(simple_str<K> other) {
3585 return operator=(my_type{other, base_storable::allocator()});
3586 }
3587
3595 template<typename T, size_t N = const_lit_for<K, T>::Count>
3596 constexpr my_type& operator=(T&& other) {
3597 return operator=(my_type{std::forward<T>(other), base_storable::allocator()});
3598 }
3599
3607 template<size_t N, bool forShared, typename A>
3608 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
3609 return operator=(my_type{other.to_str(), base_storable::allocator()});
3610 }
3611
3619 template<size_t N>
3620 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
3621 return operator=(my_type{std::move(other)});
3622 }
3623
3633 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
3634 return operator=(my_type{expr, base_storable::allocator()});
3635 }
3636
3642 constexpr my_type& make_empty() noexcept {
3643 if (type_ == Shared)
3644 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3645 create_empty();
3646 return *this;
3647 }
3648
3649 constexpr const K* symbols() const noexcept {
3650 return type_ == Local ? buf_ : cstr_;
3651 }
3652
3653 constexpr size_t length() const noexcept {
3654 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
3655 }
3656
3657 constexpr bool is_empty() const noexcept {
3658 return length() == 0;
3659 }
3660
3661 constexpr bool empty() const noexcept {
3662 return is_empty();
3663 }
3664
3676 template<typename... T>
3677 static my_type printf(const K* pattern, T&&... args) {
3678 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
3679 }
3680
3690 template<typename... T>
3691 static my_type format(const FmtString<to_std_char_t<K>, T...>& fmtString, T&&... args) {
3692 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
3693 }
3694
3704 template<typename... T>
3705 static my_type vformat(simple_str<K> fmtString, T&&... args) {
3706 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
3707 }
3708};
3709
3710template<typename K, Allocatorable Allocator>
3711inline const sstring<K> sstring<K, Allocator>::empty_str{};
3712
3713struct no_alloc{};
3714
3715template<typename K, size_t N>
3716class decl_empty_bases cestring :
3717 public str_algs<K, simple_str<K>, cestring<K, N>, true>,
3718 public str_storable<K, cestring<K, N>, no_alloc>,
3719 public null_terminated<K, lstring<K, N>>
3720 //, public from_utf_convertible<K, lstring<K, N, forShared, Allocator>>
3721{
3722public:
3723 using symb_type = K;
3724 using my_type = cestring<K, N>;
3725
3726 enum : size_t {
3728 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
3729 };
3730
3731protected:
3732
3733 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
3734 using base_storable = str_storable<K, my_type, no_alloc>;
3735 //using base_utf = from_utf_convertible<K, my_type>;
3736 using traits = ch_traits<K>;
3737 using s_str = base_storable::s_str;
3738
3739 friend base_storable;
3740 //friend base_utf;
3741 const K* cstr_{};
3742 size_t size_{};
3743 bool is_cstr_{};
3744 K local_[LocalCapacity + 1]{};
3745
3746 constexpr void create_empty() {
3747 is_cstr_ = false;
3748 size_ = 0;
3749 local_[0] = 0;
3750 }
3751
3752 constexpr K* init(size_t s) {
3753 size_ = s;
3754 if (size_ > LocalCapacity) {
3755 throw std::bad_alloc{};
3756 }
3757 is_cstr_ = false;
3758 return local_;
3759 }
3760public:
3762 constexpr size_t length() const noexcept {
3763 return size_;
3764 }
3766 constexpr const K* symbols() const noexcept {
3767 return is_cstr_ ? cstr_ : local_;
3768 }
3770 constexpr bool is_empty() const noexcept {
3771 return size_ == 0;
3772 }
3774 constexpr bool empty() const noexcept {
3775 return size_ == 0;
3776 }
3778 constexpr size_t capacity() const noexcept {
3779 return LocalCapacity;
3780 }
3785 constexpr cestring() noexcept = default;
3786
3793 constexpr cestring(s_str other) : base_storable() {
3794 base_storable::init_from_str_other(other);
3795 }
3804 constexpr cestring(size_t repeat, s_str pattern) : base_storable() {
3805 base_storable::init_str_repeat(repeat, pattern);
3806 }
3815 constexpr cestring(size_t count, K pad) : base_storable() {
3816 base_storable::init_symb_repeat(count, pad);
3817 }
3830 constexpr cestring(const StrExprForType<K> auto& expr) : base_storable() {
3831 base_storable::init_str_expr(expr);
3832 }
3847 template<StrType<K> From>
3848 constexpr cestring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0)
3849 : base_storable() {
3850 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3851 }
3852
3854 constexpr ~cestring() {}
3855
3864 template<typename T, size_t M = const_lit_for<K, T>::Count>
3865 constexpr cestring(T&& s) : base_storable(), cstr_((const K*)s), size_(M - 1), is_cstr_(true), local_{0} {}
3866};
3867
3868template<typename K>
3870 if constexpr (std::is_same_v<K, u8s>)
3871 return s8;
3872 if constexpr (std::is_same_v<K, ubs>)
3873 return sb;
3874 if constexpr (std::is_same_v<K, uws>)
3875 return sw;
3876 if constexpr (std::is_same_v<K, u16s>)
3877 return s16;
3878 if constexpr (std::is_same_v<K, u32s>)
3879 return s32;
3880}
3881
3882#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
3883
3884template<typename K, typename H>
3885struct StoreType {
3886 simple_str<K> str;
3887 size_t hash;
3888 char node[sizeof(sstring<K>)];
3889
3890 const simple_str_nt<K>& to_nt() const noexcept {
3891 return static_cast<const simple_str_nt<K>&>(str);
3892 }
3893 const sstring<K>& to_str() const noexcept {
3894 return *reinterpret_cast<const sstring<K>*>(node);
3895 }
3896};
3897
3898template<bool Wide>
3899struct fnv_const {
3900 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
3901 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
3902};
3903
3904template<>
3905struct fnv_const<false> {
3906 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
3907 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
3908};
3909
3910using fnv = fnv_const<sizeof(size_t) == 8>;
3911
3912template<typename K>
3913inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
3914 size_t h = fnv::basis;
3915 for (size_t i = 0; i < l; i++) {
3916 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
3917 }
3918 return h;
3919};
3920
3921template<typename K>
3922inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
3923 size_t h = fnv::basis;
3924 for (size_t i = 0; i < l; i++) {
3925 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
3926 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
3927 }
3928 return h;
3929};
3930
3931template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
3932inline constexpr size_t fnv_hash(T&& value) {
3933 size_t h = fnv::basis;
3934 for (size_t i = 0; i < N - 1; i++) {
3935 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
3936 }
3937 return h;
3938};
3939
3940template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
3941inline constexpr size_t fnv_hash_ia(T&& value) {
3942 size_t h = fnv::basis;
3943 for (size_t i = 0; i < N - 1; i++) {
3944 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
3945 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
3946 }
3947 return h;
3948};
3949
3950template<typename K>
3951inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
3952 return fnv_hash(ptr, l);
3953};
3954
3955template<typename K>
3956inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
3957 return fnv_hash_ia(ptr, l);
3958};
3959
3960static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>, "Store type must be trivially copyable");
3961
3962template<typename K>
3963struct streql;
3964template<typename K>
3965struct strhash;
3966
4061template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
4062class hashStrMap : public std::unordered_map<StoreType<K, H>, T, H, E> {
4063protected:
4064 using InStore = StoreType<K, H>;
4065
4066public:
4067 using my_type = hashStrMap<K, T, H, E>;
4068 using hash_t = std::unordered_map<InStore, T, H, E>;
4069 using hasher = H;
4070
4071 hashStrMap() = default;
4072 hashStrMap(const my_type& other) : hash_t(other) {
4073 for (const auto& [k, v] : *this) {
4074 InStore& stored = const_cast<InStore&>(k);
4075 sstring<K> tmp = *(sstring<K>*)stored.node;
4076 new (stored.node) sstring<K>(std::move(tmp));
4077 stored.str.str = stored.to_str().symbols();
4078 }
4079 }
4080 ~hashStrMap() {
4081 for (auto& k: *this)
4082 ((sstring<K>*)k.first.node)->~sstring();
4083 }
4084
4085 hashStrMap(my_type&& o) = default;
4086
4087 my_type& operator=(const my_type& other) {
4088 hash_t::operator=(other);
4089 for (const auto& [k, v] : *this) {
4090 InStore& stored = const_cast<InStore&>(k);
4091 sstring<K> tmp = *(sstring<K>*)stored.node;
4092 new (stored.node) sstring<K>(std::move(tmp));
4093 stored.str.str = stored.to_str().symbols();
4094 }
4095 return *this;
4096 };
4097 my_type& operator=(my_type&&) = default;
4098
4099 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4100 for (const auto& e: init)
4101 emplace(e.first, e.second);
4102 }
4103
4104 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4105
4106 hashStrMap(init_str&& init) {
4107 for (const auto& e: init)
4108 emplace(e.first, e.second);
4109 }
4110
4111 // При входе хэш должен быть уже посчитан
4112 // When entering, the hash must already be calculated
4113 template<typename... ValArgs>
4114 auto try_emplace(const InStore& key, ValArgs&&... args) {
4115 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4116 if (it.second) {
4117 InStore& stored = const_cast<InStore&>(it.first->first);
4118 new (stored.node) sstring<K>(key.str);
4119 stored.str.str = stored.to_str().symbols();
4120 }
4121 return it;
4122 }
4123
4124 static InStore toStoreType(simple_str<K> key) {
4125 return {key, H{}(key)};
4126 }
4127
4128 template<typename Key, typename... ValArgs>
4129 requires(std::is_convertible_v<Key, simple_str<K>>)
4130 auto try_emplace(Key&& key, ValArgs&&... args) {
4131 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4132 if (it.second) {
4133 InStore& stored = const_cast<InStore&>(it.first->first);
4134 new (stored.node) sstring<K>(std::forward<Key>(key));
4135 stored.str.str = stored.to_str().symbols();
4136 }
4137 return it;
4138 }
4139
4140 template<typename... ValArgs>
4141 auto emplace(const InStore& key, ValArgs&&... args) {
4142 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4143 if (!it.second) {
4144 it.first->second = T(std::forward<ValArgs>(args)...);
4145 }
4146 return it;
4147 }
4148
4149 template<typename Key, typename... ValArgs>
4150 requires(std::is_convertible_v<Key, simple_str<K>>)
4151 auto emplace(Key&& key, ValArgs&&... args) {
4152 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4153 if (!it.second) {
4154 it.first->second = T(std::forward<ValArgs>(args)...);
4155 }
4156 return it;
4157 }
4158
4159 auto& operator[](const InStore& key) {
4160 return try_emplace(key).first->second;
4161 }
4162
4163 template<typename Key>
4164 requires(std::is_convertible_v<Key, simple_str<K>>)
4165 auto& operator[](Key&& key) {
4166 return try_emplace(std::forward<Key>(key)).first->second;
4167 }
4168
4169 decltype(auto) at(const InStore& key) {
4170 return hash_t::at(key);
4171 }
4172 decltype(auto) at(const InStore& key) const {
4173 return hash_t::at(key);
4174 }
4175
4176 decltype(auto) at(simple_str<K> key) {
4177 return hash_t::at(toStoreType(key));
4178 }
4179 decltype(auto) at(simple_str<K> key) const {
4180 return hash_t::at(toStoreType(key));
4181 }
4182
4183 auto find(const InStore& key) const {
4184 return hash_t::find(key);
4185 }
4186
4187 auto find(simple_str<K> key) const {
4188 return find(toStoreType(key));
4189 }
4190
4191 auto find(const InStore& key) {
4192 return hash_t::find(key);
4193 }
4194
4195 auto find(simple_str<K> key) {
4196 return find(toStoreType(key));
4197 }
4198
4199 auto erase(typename hash_t::const_iterator it) {
4200 if (it != hash_t::end()) {
4201 ((sstring<K>*)it->first.node)->~sstring();
4202 }
4203 return hash_t::erase(it);
4204 }
4205
4206 auto erase(const InStore& key) {
4207 auto it = hash_t::find(key);
4208 if (it != hash_t::end()) {
4209 ((sstring<K>*)it->first.node)->~sstring();
4210 hash_t::erase(it);
4211 return 1;
4212 }
4213 return 0;
4214 }
4215
4216 auto erase(simple_str<K> key) {
4217 return erase(toStoreType(key));
4218 }
4219
4220 bool lookup(simple_str<K> txt, T& val) const {
4221 auto it = find(txt);
4222 if (it != hash_t::end()) {
4223 val = it->second;
4224 return true;
4225 }
4226 return false;
4227 }
4228
4229 void clear() {
4230 for (auto& k: *this)
4231 ((sstring<K>*)k.first.node)->~sstring();
4232 hash_t::clear();
4233 }
4234 bool contains(const InStore& key) const {
4235 return hash_t::find(key) != this->end();
4236 }
4237
4238 bool contains(simple_str<K> key) const {
4239 return find(toStoreType(key)) != this->end();
4240 }
4241};
4242
4243template<typename K>
4244struct streql {
4245 template<typename H>
4246 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4247 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4248 }
4249};
4250
4251template<typename K>
4252struct strhash { // hash functor for basic_string
4253 size_t operator()(simple_str<K> _Keyval) const {
4254 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4255 }
4256 size_t operator()(const StoreType<K, strhash<K>>& _Keyval) const {
4257 return _Keyval.hash;
4258 }
4259};
4260
4261template<typename K>
4262struct streqlia {
4263 template<typename H>
4264 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4265 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4266 }
4267};
4268
4269template<typename K>
4270struct strhashia {
4271 size_t operator()(simple_str<K> _Keyval) const {
4272 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4273 }
4274 size_t operator()(const StoreType<K, strhashia<K>>& _Keyval) const {
4275 return _Keyval.hash;
4276 }
4277};
4278
4279template<typename K>
4280struct streqliu {
4281 template<typename H>
4282 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4283 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4284 }
4285};
4286
4287template<typename K>
4288struct strhashiu {
4289 size_t operator()(simple_str<K> _Keyval) const {
4290 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
4291 }
4292 size_t operator()(const StoreType<K, strhashiu<K>>& _Keyval) const {
4293 return _Keyval.hash;
4294 }
4295};
4296
4311template<typename K>
4312class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
4313 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
4314 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
4315 K* write{}; // Текущая позиция записи | Current write position
4316 size_t len{}; // Общая длина | Total length
4317 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
4318 size_t align{1024};
4319
4320public:
4321 using my_type = chunked_string_builder<K>;
4322 using symb_type = K;
4323 chunked_string_builder() = default;
4324 chunked_string_builder(size_t a) : align(a){};
4325 chunked_string_builder(const my_type&) = delete;
4326 chunked_string_builder(my_type&& other) noexcept
4327 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4328 other.len = other.remain = 0;
4329 other.write = nullptr;
4330 }
4331 my_type& operator=(my_type other) noexcept {
4332 chunks.swap(other.chunks);
4333 write = other.write;
4334 len = other.len;
4335 remain = other.remain;
4336 align = other.align;
4337 other.len = other.remain = 0;
4338 other.write = nullptr;
4339 return *this;
4340 }
4341
4344 if (data.len) {
4345 len += data.len;
4346 if (data.len <= remain) {
4347 // Добавляемые данные влезают в выделенный блок, просто скопируем их
4348 // The added data fits into the selected block, just copy it
4349 ch_traits<K>::copy(write, data.str, data.len);
4350 write += data.len; // Сдвинем позицию записи | Let's move the recording position
4351 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4352 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
4353 } else {
4354 // Не влезают | They don't fit
4355 if (remain) {
4356 // Сначала запишем сколько влезет
4357 // First, write down as much as we can
4358 ch_traits<K>::copy(write, data.str, remain);
4359 data.len -= remain;
4360 data.str += remain;
4361 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4362 }
4363 // Выделим новый блок и впишем в него данные
4364 // Рассчитаем размер блока, кратного заданному выравниванию
4365 // Select a new block and write data into it
4366 // Calculate the block size that is a multiple of the given alignment
4367 size_t blockSize = (data.len + align - 1) / align * align;
4368 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
4369 write = chunks.back().first.get();
4370 ch_traits<K>::copy(write, data.str, data.len);
4371 write += data.len;
4372 remain = blockSize - data.len;
4373 }
4374 }
4375 return *this;
4376 }
4377
4378 my_type& operator<<(const StrExprForType<K> auto& expr) {
4379 size_t l = expr.length();
4380 if (l) {
4381 if (l < remain) {
4382 write = expr.place(write);
4383 chunks.back().second += l;
4384 len += l;
4385 remain -= l;
4386 } else if (!remain) {
4387 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
4388 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4389 write = expr.place(chunks.back().first.get());
4390 len += l;
4391 remain = blockSize - l;
4392 } else {
4393 auto store = std::make_unique<K[]>(l);
4394 expr.place(store.get());
4395 return operator<<({store.get(), l});
4396 }
4397 }
4398 return *this;
4399 }
4400
4401 template<typename T>
4402 my_type& operator<<(T data)
4403 requires std::is_same_v<T, K>
4404 {
4405 return operator<<(expr_char<K>(data));
4406 }
4407
4408 constexpr size_t length() const noexcept {
4409 return len;
4410 }
4411
4412 void reset() {
4413 if (chunks.empty()) {
4414 return;
4415 }
4416 if (chunks.size() > 1) {
4417 remain = 0;
4418 chunks.resize(1);
4419 }
4420 remain += chunks[0].second;
4421 chunks[0].second = 0;
4422 len = 0;
4423 write = chunks[0].first.get();
4424 }
4425
4426 constexpr K* place(K* p) const noexcept {
4427 for (const auto& block: chunks) {
4428 ch_traits<K>::copy(p, block.first.get(), block.second);
4429 p += block.second;
4430 }
4431 return p;
4432 }
4441 template<typename Op>
4442 void out(const Op& o) const {
4443 for (const auto& block: chunks)
4444 o(block.first.get(), block.second);
4445 }
4446
4450 bool is_continuous() const {
4451 if (chunks.size()) {
4452 const K* ptr = chunks.front().first.get();
4453 for (const auto& chunk: chunks) {
4454 if (chunk.first.get() != ptr)
4455 return false;
4456 ptr += chunk.second;
4457 }
4458 }
4459 return true;
4460 }
4461
4467 const K* begin() const {
4468 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4469 }
4470
4474 void clear() {
4475 chunks.clear();
4476 write = nullptr;
4477 len = 0;
4478 remain = 0;
4479 }
4480
4485 typename decltype(chunks)::const_iterator it, end;
4486 size_t writedFromCurrentChunk;
4491 bool is_end() {
4492 return it == end;
4493 }
4494
4504 size_t store(K* buffer, size_t size) {
4505 size_t writed = 0;
4506 while (size && !is_end()) {
4507 size_t remain = it->second - writedFromCurrentChunk;
4508 size_t write = std::min(size, remain);
4509 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4510 writed += write;
4511 remain -= write;
4512 size -= write;
4513 if (!remain) {
4514 ++it;
4515 writedFromCurrentChunk = 0;
4516 } else
4517 writedFromCurrentChunk += write;
4518 }
4519 return writed;
4520 }
4521 };
4522
4529 return {chunks.begin(), chunks.end(), 0};
4530 }
4531
4537 const auto& data() const {
4538 return chunks;
4539 }
4540};
4541
4542using stringa = sstring<u8s>;
4543using stringb = sstring<ubs>;
4544using stringw = sstring<wchar_t>;
4545using stringu = sstring<u16s>;
4546using stringuu = sstring<u32s>;
4547static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
4548
4553template<typename T>
4559template<typename T>
4565template<typename T>
4567
4572template<typename T>
4574
4579template<typename T>
4581
4586template<typename T>
4588
4593template<typename T>
4595template<typename T>
4605template<typename T>
4607
4612template<typename T>
4618template<typename T>
4624template<typename T>
4626
4627inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
4628
4629inline namespace literals {
4630
4641SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4642 return simple_str_nt<u8s>{ptr, l};
4643}
4644
4654SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4655 return simple_str_nt<ubs>{ptr, l};
4656}
4657
4667SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4668 return simple_str_nt<uws>{ptr, l};
4669}
4670
4680SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4681 return simple_str_nt<u16s>{ptr, l};
4682}
4683
4694SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4695 return simple_str_nt<u32s>{ptr, l};
4696}
4697
4698template<typename K> using HashKey = StoreType<K, strhash<K>>;
4699template<typename K> using HashKeyIA = StoreType<K, strhashia<K>>;
4700template<typename K> using HashKeyIU = StoreType<K, strhashiu<K>>;
4701
4712consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
4713 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4714}
4715
4726consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
4727 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4728}
4729
4740inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
4741 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
4742}
4743
4754consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
4755 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4756}
4757
4768consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
4769 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4770}
4771
4782inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
4783 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
4784}
4785
4796consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
4797 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4798}
4799
4810consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
4811 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4812}
4813
4824inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
4825 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
4826}
4827
4838consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
4839 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
4840}
4841
4852consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
4853 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4854}
4855
4866inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
4867 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(simple_str<uws>{ptr, l})};
4868}
4869} // namespace literals
4870
4881inline std::ostream& operator<<(std::ostream& stream, ssa text) {
4882 return stream << std::string_view{text.symbols(), text.length()};
4883}
4884
4895inline std::wostream& operator<<(std::wostream& stream, ssw text) {
4896 return stream << std::wstring_view{text.symbols(), text.length()};
4897}
4898
4909inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
4910 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4911}
4912
4923inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
4924 return stream << std::string_view{text.symbols(), text.length()};
4925}
4926
4937inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
4938 return stream << std::wstring_view{text.symbols(), text.length()};
4939}
4940
4951inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
4952 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4953}
4954
4965template<size_t N, bool S, simstr::Allocatorable A>
4966inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
4967 return stream << std::string_view{text.symbols(), text.length()};
4968}
4969
4980template<size_t N, bool S, simstr::Allocatorable A>
4981inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
4982 return stream << std::wstring_view{text.symbols(), text.length()};
4983}
4984
4995template<size_t N, bool S, simstr::Allocatorable A>
4996inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
4997 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4998}
4999
5000} // namespace simstr
5001
5006template<typename K>
5007struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5008 // Define format() by calling the base class implementation with the wrapped value
5009 template<typename FormatContext>
5010 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
5011 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5012 }
5013};
5014
5019template<typename K>
5020struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5021 // Define format() by calling the base class implementation with the wrapped value
5022 template<typename FormatContext>
5023 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
5024 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5025 }
5026};
5027
5032template<typename K>
5033struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5034 // Define format() by calling the base class implementation with the wrapped value
5035 template<typename FormatContext>
5036 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
5037 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5038 }
5039};
5040
5045template<typename K, size_t N, bool S, typename A>
5046struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
5047 // Define format() by calling the base class implementation with the wrapped value
5048 template<typename FormatContext>
5049 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
5050 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5051 }
5052};
5053
5058template<>
5059struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5060 // Define format() by calling the base class implementation with the wrapped value
5061 template<typename FormatContext>
5062 auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
5063 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5064 }
5065};
5066
5071template<>
5072struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5073 // Define format() by calling the base class implementation with the wrapped value
5074 template<typename FormatContext>
5075 auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
5076 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5077 }
5078};
5079
5084template<>
5085struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5086 // Define format() by calling the base class implementation with the wrapped value
5087 template<typename FormatContext>
5088 auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
5089 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5090 }
5091};
5092
5097template<size_t N, bool S, typename A>
5098struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5099 // Define format() by calling the base class implementation with the wrapped value
5100 template<typename FormatContext>
5101 auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
5102 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5103 }
5104};
5105
5110template<>
5111struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5112 // Define format() by calling the base class implementation with the wrapped value
5113 template<typename FormatContext>
5114 auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
5115 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5116 }
5117};
5118
5123template<>
5124struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5125 // Define format() by calling the base class implementation with the wrapped value
5126 template<typename FormatContext>
5127 auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
5128 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5129 }
5130};
5131
5136template<>
5137struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5138 // Define format() by calling the base class implementation with the wrapped value
5139 template<typename FormatContext>
5140 auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
5141 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5142 }
5143};
5144
5149template<size_t N, bool S, typename A>
5150struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5151 // Define format() by calling the base class implementation with the wrapped value
5152 template<typename FormatContext>
5153 auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
5154 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5155 }
5156};
Класс для последовательного получения подстрок по заданному разделителю.
Определения strexpr.h:3232
my_type & operator<<(simple_str< K > data)
Добавление порции данных.
Определения sstring.h:4343
portion_store get_portion() const
Получить portion_store, через который можно последовательно извлекать данные во внешний буфер.
Определения sstring.h:4528
constexpr size_t length() const noexcept
Длина сохранённого текста.
Определения sstring.h:4408
my_type & operator<<(T data)
Добавление символа.
Определения sstring.h:4402
void reset()
Сбрасывает содержимое, но при этом не удаляет первый буфер, чтобы потом избежать аллокации.
Определения sstring.h:4412
void clear()
Очистить объект, освободив все выделенные буфера.
Определения sstring.h:4474
my_type & operator<<(const StrExprForType< K > auto &expr)
Добавление строкового выражения.
Определения sstring.h:4378
bool is_continuous() const
Проверяет, расположен ли весь текст одним непрерывным куском в памяти.
Определения sstring.h:4450
void out(const Op &o) const
Применяет функтор к каждому сохранённому буферу.
Определения sstring.h:4442
const auto & data() const
Получить внутренние буфера с данными.
Определения sstring.h:4537
const K * begin() const
Получить указатель на начало первого буфера. Имеет смысл применять только если is_continuous true.
Определения sstring.h:4467
Контейнер для более эффективного поиска по строковым ключам.
Определения sstring.h:4062
Класс мутабельной, владеющей строки. Содержит внутренний буфер для строк заданного размера.
Определения sstring.h:2559
constexpr void define_size()
Определить длину строки. Ищет символ 0 в буфере строки до его ёмкости, после чего устаналивает длину ...
Определения sstring.h:3086
constexpr lstring(T &&value, Args &&... args)
Конструктор из строкового литерала.
Определения sstring.h:2815
my_type & operator=(T &&other)
Оператор присваивания строкового литерала.
Определения sstring.h:2957
constexpr void reset()
Делает строку пустой и освобождает внешний буфер, если он был.
Определения sstring.h:3121
@ LocalCapacity
Определения sstring.h:2567
constexpr bool is_local() const noexcept
Узнать, локальный или внешний буфер используется для символов.
Определения sstring.h:3077
my_type & operator=(my_type &&other) noexcept
Оператор присваивания перемещением из строки такого же типа.
Определения sstring.h:2900
constexpr size_t length() const noexcept
Длина строки.
Определения sstring.h:2980
constexpr lstring(s_str other, Args &&... args)
Конструктор из другого строкового объекта.
Определения sstring.h:2689
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Конструктор из строкового источника с заменой.
Определения sstring.h:2759
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
Конструктор повторения строки.
Определения sstring.h:2704
constexpr lstring(const my_type &other)
Копирование из другой строки такого же типа.
Определения sstring.h:2778
constexpr lstring(const my_type &other, Args &&... args)
Копирование из другой строки такого же типа, но с другим аллокатором.
Определения sstring.h:2800
my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присваивания строкового выражения.
Определения sstring.h:2970
constexpr K * reserve_no_preserve(size_t newSize)
Определения sstring.h:3015
my_type & operator=(const my_type &other)
Оператор присваивания копией из строки такого же типа.
Определения sstring.h:2883
constexpr bool is_empty() const noexcept
Пустая ли строка.
Определения sstring.h:2992
lstring(const Op &op, Args &&... args)
Конструктор заполнения с помощью функтора (см. str_mutable::fill).
Определения sstring.h:2854
constexpr K * set_size(size_t newSize)
Устанавливает размер текущей строки, при необходимости выделяя место.
Определения sstring.h:3060
constexpr lstring(size_t count, K pad, Args &&... args)
Конструктор повторения символа.
Определения sstring.h:2719
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Конструктор из строкового выражения.
Определения sstring.h:2738
constexpr const K * symbols() const noexcept
Указатель на константные символы.
Определения sstring.h:2984
constexpr void clear()
Делает строку пустой, не меняя буфер строки.
Определения sstring.h:3117
my_type & operator=(simple_str< K > other)
Оператор присваивания из simple_str.
Определения sstring.h:2945
constexpr bool empty() const noexcept
Пустая ли строка, для совместимости с std::string.
Определения sstring.h:2996
constexpr void shrink_to_fit()
Уменьшает размер внешнего буфера до минимально возможного для хранения строки. Если строка уместится ...
Определения sstring.h:3103
constexpr lstring(my_type &&other) noexcept
Конструктор перемещения из строки такого же типа.
Определения sstring.h:2829
constexpr K * reserve(size_t newSize)
Выделить буфер, достаточный для размещения newSize символов плюс завершающий ноль.
Определения sstring.h:3037
constexpr K * str() noexcept
Указатель на буфер строки.
Определения sstring.h:2988
constexpr size_t capacity() const noexcept
Текущая ёмкость буфера строки.
Определения sstring.h:3000
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Создать пустой объект.
Определения sstring.h:2674
Класс иммутабельной владеющей строки.
Определения sstring.h:3232
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Оператор присвоения другой строки типа lstring.
Определения sstring.h:3608
constexpr bool empty() const noexcept
Пустая ли строка, для совместимости с std::string.
Определения sstring.h:3661
constexpr bool is_empty() const noexcept
Пустая ли строка.
Определения sstring.h:3657
sstring(s_str other, Args &&... args)
Конструктор из другого строкового объекта.
Определения sstring.h:3371
sstring(size_t count, K pad, Args &&... args)
Конструктор повторения символа.
Определения sstring.h:3401
constexpr const K * symbols() const noexcept
Указатель на символы строки.
Определения sstring.h:3649
constexpr sstring(const my_type &other) noexcept
Конструктор копирования строки.
Определения sstring.h:3459
static my_type format(const FmtString< to_std_char_t< K >, T... > &fmtString, T &&... args)
Получить строку, отформатированную с помощью std::format.
Определения sstring.h:3691
constexpr my_type & operator=(T &&other)
Оператор присвоения строкового литерала.
Определения sstring.h:3596
sstring(size_t repeat, s_str pattern, Args &&... args)
Конструктор повторения строки.
Определения sstring.h:3386
constexpr my_type & operator=(my_type other) noexcept
Оператор присвоения другой строки того же типа.
Определения sstring.h:3572
static my_type printf(const K *pattern, T &&... args)
Получить строку, отформатированную с помощью std::sprintf.
Определения sstring.h:3677
constexpr sstring(lstring< K, N, true, Allocator > &&src)
Конструктор перемещения из lstring с совместимым с sstring внешним буфером.
Определения sstring.h:3486
constexpr size_t length() const noexcept
Длина строки.
Определения sstring.h:3653
constexpr sstring(my_type &&other) noexcept
Конструктор перемещения.
Определения sstring.h:3470
static my_type vformat(simple_str< K > fmtString, T &&... args)
Получить строку, отформатированную с помощью std::vformat.
Определения sstring.h:3705
sstring(simple_str< O > init)
Инициализация из строкового источника с другим типом символов. Конвертирует через UTF.
Определения sstring.h:3547
constexpr my_type & operator=(simple_str< K > other)
Оператор присвоения другой строки другого типа.
Определения sstring.h:3584
constexpr my_type & make_empty() noexcept
Сделать строку пустой.
Определения sstring.h:3642
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присвоения строкового выражения.
Определения sstring.h:3633
sstring(T &&s, Args &&... args)
Инициализация из строкового литерала.
Определения sstring.h:3534
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Оператор присвоения перемещаемой строки типа lstring с совместимым буфером.
Определения sstring.h:3620
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Конструктор из строкового выражения.
Определения sstring.h:3420
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Конструктор пустой строки.
Определения sstring.h:3356
constexpr ~sstring()
Деструктор строки.
Определения sstring.h:3448
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Конструктор из строкового источника с заменой.
Определения sstring.h:3441
Класс с дополнительными константными строковыми алгоритмами.
Определения sstring.h:166
bool starts_with_iu(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:239
int compare_iu(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:203
bool less_iu(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра Unicode символов первой плоскости (<0xF...
Определения sstring.h:225
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра Unicode символов первой плоскости (<0...
Определения sstring.h:254
R upperred() const
Получить копию строки в верхнем регистре Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:266
R lowered() const
Получить копию строки в нижнем регистре Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:278
std::optional< double > to_double() const noexcept
Преобразовать строку в double.
Определения sstring.h:288
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения sstring.h:346
bool equal_iu(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:214
void as_number(double &t) const
Преобразовать строку в double.
Определения sstring.h:329
Базовый класс работы с изменяемыми строками
Определения sstring.h:1326
Impl & insert(size_t to, const A &expr)
Вставить строковое выражение в указанную позицию.
Определения sstring.h:1866
Impl & operator<<=(const Op &fillFunction)
Заполняет строку методом fill после конца строки.
Определения sstring.h:2140
Impl & append(const A &expr)
Добавить строковое выражение в конец строки.
Определения sstring.h:1750
Impl & operator<<(const Op &fillFunction)
Вызывает переданный функтор, передав ссылку на себя.
Определения sstring.h:2153
Impl & trim(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале и в конце строки.
Определения sstring.h:1560
Impl & upper_only_ascii()
Преобразовать в верхний регистр ASCII символы.
Определения sstring.h:1624
Impl & lower_only_ascii()
Преобразовать в нижний регистр ASCII символы.
Определения sstring.h:1639
Impl & trim_left()
Удалить пробельные символы в начале строки.
Определения sstring.h:1462
Impl & append_printf(const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод в конец строки.
Определения sstring.h:2242
Impl & trim_right_with_wpaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в конце строки.
Определения sstring.h:1549
Impl & trim_left(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале строки.
Определения sstring.h:1571
Impl & trim_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:1523
Impl & prepend(str_piece other)
Добавить другую строку в начало строки.
Определения sstring.h:1890
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Добавляет отформатированный с помощью std::format вывод в конец строки.
Определения sstring.h:2380
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Определения sstring.h:2364
Impl & printf(const K *format, T &&... args)
Форматирует строку помощью sprintf.
Определения sstring.h:2226
Impl & with(const Op &fillFunction, Args &&... args)
Вызов функтора со строкой и переданными аргументами.
Определения sstring.h:2462
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки, записывая не более указанног...
Определения sstring.h:2448
Impl & operator<<(const Op &fillFunction)
Заполняет строку методом fill с нулевой позиции.
Определения sstring.h:2127
Impl & vformat(str_piece format, T &&... args)
Форматирует строку с помощью std::vformat.
Определения sstring.h:2396
Impl & change(size_t from, size_t len, const A &expr)
Заменить кусок строки на строковое выражение.
Определения sstring.h:1839
Impl & fill(size_t from, const Op &fillFunction)
Заполнение буфера строки с помощью функтора.
Определения sstring.h:2099
Impl & change(size_t from, size_t len, str_piece other)
Заменить кусок строки на другую строку.
Определения sstring.h:1823
Impl & remove(size_t from, size_t len)
Удалить часть строки.
Определения sstring.h:1879
Impl & trim_left_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале строки.
Определения sstring.h:1536
Impl & trim_left(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале строки.
Определения sstring.h:1497
Impl & trim_right(T &&pattern)
Удалить символы, входящие в строковый литерал, в конце строки.
Определения sstring.h:1510
Impl & trim_right_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в конце строки.
Определения sstring.h:1615
Impl & append_in(size_t pos, str_piece other)
Добавить другую строку, начиная с заданной позиции.
Определения sstring.h:1790
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод, начиная с указанной позиции.
Определения sstring.h:2329
Impl & trim_left_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале строки.
Определения sstring.h:1604
Impl & insert(size_t to, str_piece other)
Вставить строку в указанную позицию.
Определения sstring.h:1852
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Форматирует строку с помощью std::vformat не более указанного размера.
Определения sstring.h:2430
Impl & printf_from(size_t from, const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод, начиная с указанной позиции.
Определения sstring.h:2172
Impl & operator+=(str_piece other)
Добавить другую строку в конец строки.
Определения sstring.h:1761
Impl & trim_right()
Удалить пробельные символы в конце строки.
Определения sstring.h:1471
Impl & append(str_piece other)
Добавить другую строку в конец строки.
Определения sstring.h:1738
Impl & trim_right(str_piece pattern)
Удалить символы, входящие в переданную строку, в конце строки.
Определения sstring.h:1582
Impl & trim_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:1593
K * str() noexcept
Получить указатель на буфер строки.
Определения sstring.h:1435
Impl & prepend(const A &expr)
Добавить строковое выражение в начало строки.
Определения sstring.h:1902
Impl & trim()
Удалить пробельные символы в начале и в конце строки.
Определения sstring.h:1453
Impl & lower()
Преобразовать в нижний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:1673
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Заменить вхождения подстроки на другую строку.
Определения sstring.h:1919
Impl & upper()
Преобразовать в верхний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:1658
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Скопировать строку-источник, заменив вхождения подстрок на другую строку.
Определения sstring.h:2037
Impl & append_vformatted(str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки.
Определения sstring.h:2412
Impl & append_in(size_t pos, const A &expr)
Добавить строковое выражение, начиная с заданной позиции.
Определения sstring.h:1808
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Определения sstring.h:2300
Impl & trim(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале и в конце строки.
Определения sstring.h:1484
Impl & operator+=(const A &expr)
Добавить строковое выражение в конец строки.
Определения sstring.h:1773
constexpr str_piece to_str() const noexcept
Преобразовать себя в "кусок строки", включающий всю строку.
Определения strexpr.h:3420
constexpr size_t size() const
Определения strexpr.h:3361
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения strexpr.h:4217
База для объектов, владеющих строкой.
Определения sstring.h:803
constexpr str_storable(Args &&... args)
Создать пустой объект.
Определения sstring.h:839
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Инициализация из строкового источника с заменой.
Определения sstring.h:926
static my_type upperred_from(const From &f, Args &&... args)
Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF).
Определения sstring.h:1187
constexpr void init_from_str_other(s_str other)
Инициализация из другого строкового объекта.
Определения sstring.h:847
constexpr allocator_t & allocator()
Получить аллокатор.
Определения sstring.h:816
constexpr void init_symb_repeat(size_t count, K pad)
Инициализация повторением символа.
Определения sstring.h:883
constexpr void init_str_expr(const A &expr)
Инициализация из строкового выражения.
Определения sstring.h:904
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Создать строку, копию переданной в верхнем регистре символов ASCII.
Определения sstring.h:1157
static my_type lowered_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF).
Определения sstring.h:1204
constexpr void init_str_repeat(size_t repeat, s_str pattern)
Инициализация повторением строки.
Определения sstring.h:863
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов ASCII.
Определения sstring.h:1170
static my_type join(const T &strings, s_str delimiter, bool tail=false, bool skip_empty=false, Args &&... args)
Конкатенация строк из контейнера в одну строку.
Определения sstring.h:1106
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Создать копию переданной строки с заменой подстрок.
Определения sstring.h:1225
constexpr s_str_nt to_nts(size_t from=0) const
Получить simple_str_nt, начиная с заданного символа.
Определения sstring.h:1050
Концепт типа, управляющего памятью
Определения sstring.h:1235
Концепт строкового выражения, совместимого с заданным типом символов.
Определения strexpr.h:525
Концепт типа, который не может модифицировать хранимую строку.
Определения sstring.h:758
Концепт типа, который может модифицировать хранимую строку.
Определения sstring.h:751
Концепт типа, который может сохранить строку.
Определения sstring.h:741
Небольшое пространство для методов работы со стандартными строками.
Определения strexpr.h:1600
Пространство имён для объектов библиотеки
Определения sstring.cpp:12
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Тип хеш-словаря для char16_t строк, регистрозависимый поиск.
Определения sstring.h:4594
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Тип хеш-словаря для char строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4560
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4625
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4619
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4587
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4580
expr_utf< From, To > e_utf(simple_str< From > from)
Возвращает строковое выражение, преобразующую строку из одного типа символов в другой тип,...
Определения sstring.h:732
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Тип хеш-словаря для char строк, регистрозависимый поиск.
Определения sstring.h:4554
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4606
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4600
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Тип хеш-словаря для wchar_t строк, регистрозависимый поиск.
Определения sstring.h:4573
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Тип хеш-словаря для char строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4566
std::ostream & operator<<(std::ostream &stream, ssa text)
Оператор вывода в поток simple_str.
Определения sstring.h:4881
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Тип хеш-словаря для char32_t строк, регистрозависимый поиск.
Определения sstring.h:4613
Объект, позволяющий последовательно копировать содержимое в буфер заданного размера.
Определения sstring.h:4484
bool is_end()
Проверить, что данные ещё не кончились.
Определения sstring.h:4491
size_t store(K *buffer, size_t size)
Сохранить очередную порцию данных в буфер.
Определения sstring.h:4504
Базовый класс для преобразования строковых выражений в стандартные строки
Определения strexpr.h:659
Строковое выражение для конвертации строк в разные виды UTF.
Определения sstring.h:701
Класс, заявляющий, что ссылается на нуль-терминированную строку.
Определения sstring.h:509
constexpr my_type to_nts(size_t from)
Получить нуль-терминированную строку, сдвинув начало на заданное количество символов.
Определения sstring.h:572
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения sstring.h:561
constexpr simple_str_nt(T &&v) noexcept
Конструктор из строкового литерала.
Определения sstring.h:543
constexpr simple_str_nt(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения sstring.h:549
constexpr simple_str_nt(T &&p) noexcept
Явный конструктор из С-строки.
Определения sstring.h:534
Простейший класс иммутабельной не владеющей строки.
Определения sstring.h:373
constexpr simple_str(T &&v) noexcept
Конструктор из строкового литерала.
Определения sstring.h:389
constexpr K operator[](size_t idx) const
Получить символ из указанной позиции. Проверка границ не выполняется.
Определения sstring.h:453
constexpr my_type & remove_suffix(size_t delta)
Укорачивает строку на заданное количество символов.
Определения sstring.h:477
constexpr size_t length() const noexcept
Получить длину строки.
Определения sstring.h:410
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки.
Определения sstring.h:417
constexpr my_type & remove_prefix(size_t delta)
Сдвигает начало строки на заданное количество символов.
Определения sstring.h:464
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Конструктор из std::basic_string_view.
Определения sstring.h:405
constexpr simple_str(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения sstring.h:394
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения sstring.h:400
constexpr bool is_empty() const noexcept
Проверить, не пуста ли строка.
Определения sstring.h:424
constexpr bool is_same(simple_str< K > other) const noexcept
Проверить, не указывают ли два объекта на одну строку.
Определения sstring.h:433
constexpr bool is_part_of(simple_str< K > other) const noexcept
Проверить, не является ли строка частью другой строки.
Определения sstring.h:442
Простейший класс иммутабельной не владеющей строки.
Определения strexpr.h:4826
constexpr size_t length() const noexcept
Получить длину строки.
Определения strexpr.h:4866
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки.
Определения strexpr.h:4873