simstr 1.5.0
Yet another strings library
 
Loading...
Searching...
No Matches
strexpr.h
1/*
2 * ver. 1.5.0
3 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
4 * База для строковых конкатенаций через выражения времени компиляции
5 * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6 * Base for string concatenations via compile-time expressions
7 */
8#pragma once
9#include <cstddef>
10#include <cstdint>
11#include <climits>
12#include <limits>
13#include <string>
14#include <string_view>
15#include <concepts>
16#include <vector>
17#include <optional>
18#include <charconv>
19
20#if defined __has_builtin
21# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
22# define HAS_BUILTIN_OVERFLOW
23# endif
24#endif
25
26#ifdef _MSC_VER
27#define _no_unique_address msvc::no_unique_address
28#define decl_empty_bases __declspec(empty_bases)
29#else
30#define _no_unique_address no_unique_address
31#define decl_empty_bases
32#endif
33
34#ifdef _MSC_VER
35/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
36Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
37Пишут, что баг исправлен, но видимо не до конца.
38Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
39/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
40Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
41They write that the bug has been fixed, but apparently not completely.
42Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
43#define SS_CONSTEVAL constexpr
44#else
45#define SS_CONSTEVAL consteval
46#endif
47
52namespace simstr {
53
54// Выводим типы для 16 и 32 битных символов в зависимости от размера wchar_t
55// Infer types for 16 and 32 bit characters depending on the size of wchar_t
56inline constexpr bool wchar_is_u16 = sizeof(wchar_t) == 2;
57
58using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
59
60inline wchar_type* to_w(wchar_t* p) {
61 return (reinterpret_cast<wchar_type*>(p));
62}
63
64inline const wchar_type* to_w(const wchar_t* p) {
65 return (reinterpret_cast<const wchar_type*>(p));
66}
67
68inline wchar_t* from_w(wchar_type* p) {
69 return (reinterpret_cast<wchar_t*>(p));
70}
71
72inline const wchar_t* from_w(const wchar_type* p) {
73 return (reinterpret_cast<const wchar_t*>(p));
74}
75
76using u8s = char;
77using ubs = char8_t;
78using uws = wchar_t;
79using u16s = char16_t;
80using u32s = char32_t;
81
82using uu8s = std::make_unsigned<u8s>::type;
83
84template<typename T, typename K = void, typename... Types>
85struct is_one_of_type {
86 static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
87};
88template<typename T>
89struct is_one_of_type<T, void> : std::false_type {};
90
91template<typename K>
92constexpr bool is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
93
94template<typename K>
95constexpr bool is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
96
97template<typename From>
98requires (is_one_of_std_char_v<From>)
99auto to_one_of_std_char(From* from) {
100 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
101 return from;
102 } else if constexpr (std::is_same_v<From, ubs>) {
103 return reinterpret_cast<u8s*>(from);
104 } else {
105 return from_w(from);
106 }
107}
108template<typename From>
109requires (is_one_of_std_char_v<From>)
110auto to_one_of_std_char(const From* from) {
111 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
112 return from;
113 } else if constexpr (std::is_same_v<From, ubs>) {
114 return reinterpret_cast<const u8s*>(from);
115 } else {
116 return from_w(from);
117 }
118}
119
135template<typename K1, typename K2>
136constexpr bool is_equal_str_type_v = is_one_of_char_v<K1> && is_one_of_char_v<K2> && sizeof(K1) == sizeof(K2);
137
138/*
139Вспомогательные шаблоны для определения строковых литералов.
140Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
141Если пишем
142template<size_t N>
143void func(const char(&lit)[N]);
144то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
145
146// Выделили место под символы
147char buf[100];
148// Как то наполнили буфер, допустим, до половины.
149
150stringa text = buf;
151Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
152для строкового литерала, в text запишется просто указатель на buf и длина 100.
153
154Поэтому такие параметры объявляем как
155template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
156void func(T&& lit);
157
158Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
159и выражение с параметром char[100] - не скомпилируется.
160
161Helper templates for defining string literals.
162They are used to limit types in function parameters strictly as `const K(&)[N]`
163If we write
164template<size_t N>
165void func(const char(&lit)[N]);
166then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
167
168// Allocate space for symbols
169char buf[100];
170// Somehow the buffer was filled, say, to half.
171
172stringa text = buf;
173Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
174for a string literal, text will simply contain a pointer to buf and length 100.
175
176Therefore, we declare such parameters as
177template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
178void func(T&& lit);
179
180Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
181and an expression with the char[100] parameter will not compile.
182*/
183
184template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
185// Для правильных типов параметров есть определение, в виде специализации шаблона
186// There is a definition for the correct parameter types, in the form of a template specialization
187template<typename T, size_t N>
188 requires(is_one_of_char_v<T>)
189struct const_lit<const T(&)[N]> {
190 using symb_type = T;
191 constexpr static size_t Count = N;
192};
193
194template<typename T>
195concept is_const_lit_v = requires {
196 typename const_lit<T>::symb_type;
197};
198
199// Тут ещё дополнительно ограничиваем тип литерала
200// Here we further restrict the type of the literal
201template<typename K, typename T> struct const_lit_for;
202
203template<typename K, typename P, size_t N>
205struct const_lit_for<K, const P(&)[N]> {
206 constexpr static size_t Count = N;
207};
208
209template<typename K, size_t N>
210class const_lit_to_array {
211
212 template<size_t Idx>
213 constexpr size_t find(K s) const {
214 if constexpr (Idx < N) {
215 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
216 }
217 return -1;
218 }
219
220 template<size_t Idx>
221 constexpr bool exist(K s) const {
222 if constexpr (Idx < N) {
223 return s == symbols_[Idx] || exist<Idx + 1>(s);
224 }
225 return false;
226 }
227public:
228 const K (&symbols_)[N + 1];
229
230 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
231 constexpr const_lit_to_array(T&& s)
232 : symbols_(s) {}
233
234 constexpr bool contain(K s) const {
235 return exist<0>(s);
236 }
237 constexpr size_t index_of(K s) const {
238 return find<0>(s);
239 }
240};
241
263template<typename A, typename K>
264concept StrType = requires(const A& a) {
265 { a.is_empty() } -> std::same_as<bool>;
266 { a.length() } -> std::convertible_to<size_t>;
267 { a.symbols() } -> std::same_as<const K*>;
268} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
269
391
399template<typename A>
400concept StrExpr = requires(const A& a) {
401 typename A::symb_type;
402 { a.length() } -> std::convertible_to<size_t>;
403 { a.place(std::declval<typename A::symb_type*>()) } -> std::same_as<typename A::symb_type*>;
404};
405
417template<typename A, typename K>
419
420/*
421* Шаблонные классы для создания строковых выражений из нескольких источников.
422* Благодаря компиляторно-шаблонной "магии" позволяют максимально эффективно
423* получать результирующую строку - сначала вычисляется длина результирующей строки,
424* потом один раз выделяется память для результата, после символы помещаются в
425* выделенную память.
426* Для конкатенация двух объектов строковых выражений в один
427
428* Template classes for creating string expressions from multiple sources.
429* Thanks to compiler-template "magic" they allow you to maximize efficiency
430* get the resulting string - first the length of the resulting string is calculated,
431* then memory is allocated once for the result, after which the characters are placed in
432* allocated memory.
433* For concatenating two string expression objects into one.
434*/
435
436template<typename K, typename Allocator, StrExpr A>
437constexpr std::basic_string<K, std::char_traits<K>, Allocator> to_std_string(const A& expr) {
438 std::basic_string<K, std::char_traits<K>, Allocator> res;
439 if (size_t l = expr.length()) {
440 auto fill = [&](K* ptr, size_t size) -> size_t {
441 expr.place((typename A::symb_type*)ptr);
442 return l;
443 };
444 if constexpr (requires { res.resize_and_overwrite(l, fill); }) {
445 res.resize_and_overwrite(l, fill);
446 } else if constexpr (requires{ res._Resize_and_overwrite(l, fill); }) {
447 // Work in MSVC std lib before C++23
448 res._Resize_and_overwrite(l, fill);
449 } else {
450 res.resize(l); // bad, fill by 0 first.
451 expr.place((typename A::symb_type*)res.data());
452 }
453 }
454 return res;
455}
456
467template<typename Impl>
469 template<typename P, typename Allocator>
471 constexpr operator std::basic_string<P, std::char_traits<P>, Allocator>() const {
472 return to_std_string<P, Allocator>(*static_cast<const Impl*>(this));
473 }
474};
475
491template<StrExpr A, StrExprForType<typename A::symb_type> B>
492struct strexprjoin : expr_to_std_string<strexprjoin<A, B>>{
493 using symb_type = typename A::symb_type;
494 const A& a;
495 const B& b;
496 constexpr strexprjoin(const A& a_, const B& b_) : a(a_), b(b_){}
497 constexpr size_t length() const noexcept {
498 return a.length() + b.length();
499 }
500 constexpr symb_type* place(symb_type* p) const noexcept {
501 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
502 }
503};
504
528template<StrExpr A, StrExprForType<typename A::symb_type> B>
529constexpr strexprjoin<A, B> operator+(const A& a, const B& b) {
530 return {a, b};
531}
532
553template<StrExpr A, StrExprForType<typename A::symb_type> B, bool last = true>
554struct strexprjoin_c : expr_to_std_string<strexprjoin_c<A, B, last>>{
555 using symb_type = typename A::symb_type;
556 const A& a;
557 B b;
558 template<typename... Args>
559 constexpr strexprjoin_c(const A& a_, Args&&... args_) : a(a_), b(std::forward<Args>(args_)...) {}
560 constexpr size_t length() const noexcept {
561 return a.length() + b.length();
562 }
563 constexpr symb_type* place(symb_type* p) const noexcept {
564 if constexpr (last) {
565 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
566 } else {
567 return a.place((symb_type*)b.place((typename B::symb_type*)p));
568 }
569 }
570};
571
608template<typename K>
609struct empty_expr : expr_to_std_string<empty_expr<K>>{
610 using symb_type = K;
611 constexpr size_t length() const noexcept {
612 return 0;
613 }
614 constexpr symb_type* place(symb_type* p) const noexcept {
615 return p;
616 }
617};
618
624inline constexpr empty_expr<u8s> eea{};
630inline constexpr empty_expr<u8s> eeb{};
636inline constexpr empty_expr<uws> eew{};
642inline constexpr empty_expr<u16s> eeu{};
648inline constexpr empty_expr<u32s> eeuu{};
649
650template<typename K>
651struct expr_char : expr_to_std_string<expr_char<K>>{
652 using symb_type = K;
653 K value;
654 constexpr expr_char(K v) : value(v){}
655 constexpr size_t length() const noexcept {
656 return 1;
657 }
658 constexpr symb_type* place(symb_type* p) const noexcept {
659 *p++ = value;
660 return p;
661 }
662};
663
676template<typename K, StrExprForType<K> A>
677constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
678 return {a, s};
679}
680
691template<typename K>
692constexpr expr_char<K> e_char(K s) {
693 return {s};
694}
695
696template<typename K, size_t N>
697struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
698 using symb_type = K;
699 const K (&str)[N + 1];
700 constexpr expr_literal(const K (&str_)[N + 1]) : str(str_){}
701
702 constexpr size_t length() const noexcept {
703 return N;
704 }
705 constexpr symb_type* place(symb_type* p) const noexcept {
706 if constexpr (N != 0)
707 std::char_traits<K>::copy(p, str, N);
708 return p + N;
709 }
710};
711
764template<typename T, size_t N = const_lit<T>::Count>
765constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
766 return {s};
767}
768
769template<bool first, typename K, size_t N, typename A>
770struct expr_literal_join : expr_to_std_string<expr_literal_join<first, K, N, A>> {
771 using symb_type = K;
772 using atype = typename A::symb_type;
773 const K (&str)[N + 1];
774 const A& a;
775 constexpr expr_literal_join(const K (&str_)[N + 1], const A& a_) : str(str_), a(a_){}
776
777 constexpr size_t length() const noexcept {
778 return N + a.length();
779 }
780 constexpr symb_type* place(symb_type* p) const noexcept {
781 if constexpr (N != 0) {
782 if constexpr (first) {
783 std::char_traits<K>::copy(p, str, N);
784 return (symb_type*)a.place((atype*)(p + N));
785 } else {
786 p = (symb_type*)a.place((atype*)p);
787 std::char_traits<K>::copy(p, str, N);
788 return p + N;
789 }
790 } else {
791 return a.place(p);
792 }
793 }
794};
795
803template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
804constexpr expr_literal_join<false, P, (N - 1), A> operator+(const A& a, T&& s) {
805 return {s, a};
806}
807
815template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
816constexpr expr_literal_join<true, P, (N - 1), A> operator+(T&& s, const A& a) {
817 return {s, a};
818}
819
833template<typename K, size_t N, size_t S = ' '>
834struct expr_spaces : expr_to_std_string<expr_spaces<K, N>> {
835 using symb_type = K;
836 constexpr size_t length() const noexcept {
837 return N;
838 }
839 constexpr symb_type* place(symb_type* p) const noexcept {
840 if constexpr (N != 0)
841 std::char_traits<K>::assign(p, N, static_cast<K>(S));
842 return p + N;
843 }
844};
845
859template<size_t N>
861 return {};
862}
863
877template<size_t N>
879 return {};
880}
881
893template<typename K>
894struct expr_pad : expr_to_std_string<expr_pad<K>> {
895 using symb_type = K;
896 size_t len;
897 K s;
898 constexpr expr_pad(size_t len_, K s_) : len(len_), s(s_){}
899 constexpr size_t length() const noexcept {
900 return len;
901 }
902 constexpr symb_type* place(symb_type* p) const noexcept {
903 if (len)
904 std::char_traits<K>::assign(p, len, s);
905 return p + len;
906 }
907};
908
922template<typename K>
923constexpr expr_pad<K> e_c(size_t l, K s) {
924 return { l, s };
925}
926
927template<typename K, size_t N>
928struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
929 using symb_type = K;
930 size_t repeat_;
931 const K (&s)[N + 1];
932 constexpr expr_repeat_lit(size_t repeat, const K (&s_)[N + 1]) : repeat_(repeat), s(s_){}
933 constexpr size_t length() const noexcept {
934 return N * repeat_;
935 }
936 constexpr symb_type* place(symb_type* p) const noexcept {
937 if constexpr (N) {
938 for (size_t i = 0; i < repeat_; i++) {
939 std::char_traits<K>::copy(p, s, N);
940 p += N;
941 }
942 }
943 return p;
944 }
945};
946
947template<StrExpr A>
948struct expr_repeat_expr : expr_to_std_string<expr_repeat_expr<A>> {
949 using symb_type = typename A::symb_type;
950 size_t repeat_;
951 const A& expr_;
952 constexpr expr_repeat_expr(size_t repeat, const A& expr) : repeat_(repeat), expr_(expr){}
953 constexpr size_t length() const noexcept {
954 if (repeat_) {
955 return repeat_ * expr_.length();
956 }
957 return 0;
958 }
959 constexpr symb_type* place(symb_type* p) const noexcept {
960 if (repeat_) {
961 if (repeat_ == 1) {
962 return expr_.place(p);
963 }
964 symb_type* start = p;
965 p = expr_.place(p);
966 size_t len = size_t(p - start);
967 if (len) {
968 for (size_t i = 1; i < repeat_; i++) {
969 std::char_traits<symb_type>::copy(p, start, len);
970 p += len;
971 }
972 }
973 }
974 return p;
975 }
976};
977
991template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
992constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
993 return { l, s };
994}
995
1009template<StrExpr A>
1010constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
1011 return { l, s };
1012}
1013
1027template<StrExpr A, StrExprForType<typename A::symb_type> B>
1028struct expr_choice : expr_to_std_string<expr_choice<A, B>> {
1029 using symb_type = typename A::symb_type;
1030 using my_type = expr_choice<A, B>;
1031 const A& a;
1032 const B& b;
1033 bool choice;
1034
1035 constexpr expr_choice(const A& _a, const B& _b, bool _choice) : a(_a), b(_b), choice(_choice){}
1036
1037 constexpr size_t length() const noexcept {
1038 return choice ? a.length() : b.length();
1039 }
1040 constexpr symb_type* place(symb_type* ptr) const noexcept {
1041 return choice ? a.place(ptr) : (symb_type*)b.place((typename B::symb_type*)ptr);
1042 }
1043};
1044
1056template<StrExpr A>
1057struct expr_if : expr_to_std_string<expr_if<A>> {
1058 using symb_type = typename A::symb_type;
1059 using my_type = expr_if<A>;
1060 const A& a;
1061 bool choice;
1062 constexpr expr_if(const A& _a, bool _choice) : a(_a), choice(_choice){}
1063
1064 constexpr size_t length() const noexcept {
1065 return choice ? a.length() : 0;
1066 }
1067 constexpr symb_type* place(symb_type* ptr) const noexcept {
1068 return choice ? a.place(ptr) : ptr;
1069 }
1070};
1071
1118template<typename L, StrExprForType<L> A, size_t N, bool Compare>
1119struct expr_choice_one_lit : expr_to_std_string<expr_choice_one_lit<L, A, N, Compare>> {
1120 using symb_type = L;
1121 const symb_type (&str)[N + 1];
1122 const A& a;
1123 bool choice;
1124 constexpr expr_choice_one_lit(const symb_type (&_str)[N + 1], const A& _a, bool _choice) : str(_str), a(_a), choice(_choice){}
1125
1126 constexpr size_t length() const noexcept {
1127 return choice == Compare ? a.length() : N;
1128 }
1129 constexpr symb_type* place(symb_type* ptr) const noexcept {
1130 if (choice == Compare) {
1131 return (L*)a.place((typename A::symb_type*)ptr);
1132 }
1133 if constexpr (N != 0) {
1134 std::char_traits<symb_type>::copy(ptr, str, N);
1135 }
1136 return ptr + N;
1137 }
1138};
1139
1184template<typename K, size_t N, typename P, size_t M>
1185struct expr_choice_two_lit : expr_to_std_string<expr_choice_two_lit<K, N, P, M>> {
1186 using symb_type = K;
1187 const K (&str_a)[N + 1];
1188 const P (&str_b)[M + 1];
1189 bool choice;
1190 constexpr expr_choice_two_lit(const K(&_str_a)[N + 1], const P(&_str_b)[M + 1], bool _choice)
1191 : str_a(_str_a), str_b(_str_b), choice(_choice){}
1192
1193 constexpr size_t length() const noexcept {
1194 return choice ? N : M;
1195 }
1196 constexpr symb_type* place(symb_type* ptr) const noexcept {
1197 if (choice) {
1198 if constexpr (N != 0) {
1199 std::char_traits<symb_type>::copy(ptr, str_a, N);
1200 }
1201 return ptr + N;
1202 }
1203 if constexpr (M != 0) {
1204 std::char_traits<symb_type>::copy(ptr, (const K(&)[M + 1])str_b, M);
1205 }
1206 return ptr + M;
1207 }
1208};
1209
1239template<StrExpr A, StrExprForType<typename A::symb_type> B>
1240constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
1241 return {a, b, c};
1242}
1243
1249template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1250constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
1251 return {str, a, c};
1252}
1253
1259template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1260constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
1261 return {str, a, c};
1262}
1263
1268template<typename T, typename L, typename K = typename const_lit<T>::symb_type, typename P = typename const_lit<L>::symb_type,
1269 size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
1271constexpr expr_choice_two_lit<K, N -1, P, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
1272 return {str_a, str_b, c};
1273}
1274
1317template<StrExpr A>
1318constexpr expr_if<A> e_if(bool c, const A& a) {
1319 return {a, c};
1320}
1321
1326template<typename T, size_t N = const_lit<T>::Count>
1327constexpr auto e_if(bool c, T&& str) {
1328 using K = typename const_lit<T>::symb_type;
1329 const K empty[1] = {0};
1330 return expr_choice_two_lit<K, N - 1, K, 0>{str, empty, c};
1331}
1332
1333template<typename T> struct is_std_string_source : std::false_type{};
1334
1335template<typename K, typename A>
1336struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
1337
1338template<typename K>
1339struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
1340
1341template<typename T>
1342constexpr bool is_std_string_source_v = is_std_string_source<T>::value;
1343
1344template<typename T>
1345concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
1346
1356template<typename K, StdStrSource T> requires is_equal_str_type_v<K, typename T::value_type>
1357struct expr_stdstr {
1358 using symb_type = K;
1359 const T& t_;
1360
1361 expr_stdstr(const T& t) : t_(t){}
1362
1363 constexpr size_t length() const noexcept {
1364 return t_.length();
1365 }
1366 constexpr symb_type* place(symb_type* p) const noexcept {
1367 size_t s = t_.size();
1368 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
1369 return p + s;
1370 }
1371};
1372
1378template<StdStrSource T, StrExprForType<typename T::value_type> A>
1380 return {a, s};
1381}
1382
1388template<StdStrSource T, StrExprForType<typename T::value_type> A>
1390 return {a, s};
1391}
1392
1393namespace str {
1394constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
1395} // namespace str
1396
1397template<typename K>
1398struct ch_traits : std::char_traits<K>{};
1399
1400template<typename T>
1401concept FromIntNumber =
1402 is_one_of_type<std::remove_cv_t<T>, unsigned char, int, short, long, long long, unsigned, unsigned short, unsigned long, unsigned long long>::value;
1403
1404template<typename T>
1405concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1406
1407template<typename K, bool I, typename T>
1408struct need_sign { // NOLINT
1409 bool negate;
1410 std::make_unsigned_t<T> val;
1411 need_sign(T t) : negate(t < 0), val(t < 0 ? std::make_unsigned_t<T>{} - t : t) {}
1412 void after(K*& ptr) {
1413 if (negate)
1414 *--ptr = '-';
1415 }
1416};
1417
1418template<typename K, typename T>
1419struct need_sign<K, false, T> {
1420 T val;
1421 need_sign(T t) : val(t){}
1422 void after(K*&) {}
1423};
1424
1425template<typename K, typename T>
1426constexpr size_t fromInt(K* bufEnd, T val) {
1427 const char* twoDigit =
1428 "0001020304050607080910111213141516171819"
1429 "2021222324252627282930313233343536373839"
1430 "4041424344454647484950515253545556575859"
1431 "6061626364656667686970717273747576777879"
1432 "8081828384858687888990919293949596979899";
1433 if (val) {
1434 need_sign<K, std::is_signed_v<T>, T> store(val);
1435 K* itr = bufEnd;
1436 while (store.val >= 100) {
1437 const char* ptr = twoDigit + (store.val % 100) * 2;
1438 *--itr = static_cast<K>(ptr[1]);
1439 *--itr = static_cast<K>(ptr[0]);
1440 store.val /= 100;
1441 }
1442 if (store.val < 10) {
1443 *--itr = static_cast<K>('0' + store.val);
1444 } else {
1445 const char* ptr = twoDigit + store.val * 2;
1446 *--itr = static_cast<K>(ptr[1]);
1447 *--itr = static_cast<K>(ptr[0]);
1448 }
1449 store.after(itr);
1450 return size_t(bufEnd - itr);
1451 }
1452 bufEnd[-1] = '0';
1453 return 1;
1454}
1455
1456template<typename K, typename T>
1457struct expr_num : expr_to_std_string<expr_num<K, T>> {
1458 using symb_type = K;
1459 using my_type = expr_num<K, T>;
1460
1461 enum { bufSize = 24 };
1462 mutable T value;
1463 mutable K buf[bufSize];
1464
1465 constexpr expr_num(T t) : value(t) {}
1466 constexpr expr_num(expr_num<K, T>&& t) : value(t.value) {}
1467
1468 size_t length() const noexcept {
1469 value = (T)fromInt(buf + bufSize, value);
1470 return (size_t)value;
1471 }
1472 K* place(K* ptr) const noexcept {
1473 size_t len = (size_t)value;
1474 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1475 return ptr + len;
1476 }
1477};
1478
1490template<StrExpr A, FromIntNumber T>
1492 return {a, s};
1493}
1494
1506template<StrExpr A, FromIntNumber T>
1508 return {a, s};
1509}
1510
1526template<typename K, FromIntNumber T>
1527constexpr expr_num<K, T> e_num(T t) {
1528 return {t};
1529}
1530
1531template<typename K>
1532struct expr_real : expr_to_std_string<expr_real<K>> {
1533 using symb_type = K;
1534 mutable u8s buf[40];
1535 mutable size_t l;
1536 double v;
1537 constexpr expr_real(double d) : v(d) {}
1538 constexpr expr_real(float d) : v(d) {}
1539
1540 size_t length() const noexcept {
1541 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
1542 l = ec != std::errc{} ? 0 : ptr - buf;
1543 return l;
1544 }
1545 K* place(K* ptr) const noexcept {
1546 if constexpr (sizeof(K) == sizeof(buf[0])) {
1547 ch_traits<K>::copy(ptr, (K*)buf, l);
1548 } else {
1549 for (size_t i = 0; i < l; i++) {
1550 ptr[i] = buf[i];
1551 }
1552 }
1553 return ptr + l;
1554 }
1555};
1556
1568template<StrExpr A, typename R>
1569 requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
1570inline constexpr strexprjoin_c<A, expr_real<typename A::symb_type>> operator+(const A& a, R s) {
1571 return {a, s};
1572}
1573
1585template<StrExpr A, typename R>
1586 requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
1587inline constexpr strexprjoin_c<A, expr_real<typename A::symb_type>, false> operator+(R s, const A& a) {
1588 return {a, s};
1589}
1590
1602template<typename K> requires is_one_of_char_v<K>
1603inline constexpr expr_real<K> e_num(double t) {
1604 return {t};
1605}
1606
1607template<typename K, bool Ucase>
1608constexpr K hex_symbols[16] = {K('0'), K('1'), K('2'), K('3'), K('4'), K('5'), K('6'),
1609 K('7'), K('8'), K('9'), K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'),
1610 K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'), K(Ucase ? 'F' : 'f')};
1611
1612template<typename K, FromIntNumber Val, bool All, bool Ucase, bool Ox>
1613struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
1614 using symb_type = K;
1615 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
1616 mutable K buf_[sizeof(Val) * 2];
1617
1618 explicit constexpr expr_hex(Val v) : v_(v){}
1619
1620 constexpr size_t length() const noexcept {
1621 K *ptr = buf_ + std::size(buf_);
1622 size_t l = 0;
1623 for (;;) {
1624 *--ptr = hex_symbols<K, Ucase>[v_.val & 0xF];
1625 v_.val >>= 4;
1626 l++;
1627 if (v_.val) {
1628 *--ptr = hex_symbols<K, Ucase>[v_.val & 0xF];
1629 v_.val >>= 4;
1630 l++;
1631 }
1632 if (!v_.val) {
1633 if constexpr (All) {
1634 if (size_t need = sizeof(Val) * 2 - l) {
1635 ch_traits<K>::assign(buf_, need, K('0'));
1636 }
1637 l = sizeof(Val) * 2;
1638 }
1639 break;
1640 }
1641 }
1642 v_.val = l;
1643 if constexpr (std::is_signed_v<Val>) {
1644 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
1645 }
1646 return l + (Ox ? 2 : 0);
1647 }
1648 constexpr K* place(K* ptr) const noexcept {
1649 if constexpr (std::is_signed_v<Val>) {
1650 if (v_.negate) {
1651 *ptr++ = K('-');
1652 }
1653 }
1654 if constexpr (Ox) {
1655 *ptr++ = K('0');
1656 *ptr++ = K('x');
1657 }
1658 if constexpr (All) {
1659 ch_traits<K>::copy(ptr, buf_, sizeof(Val) * 2);
1660 return ptr + sizeof(Val) * 2;
1661 } else {
1662 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
1663 return ptr + v_.val;
1664 }
1665 }
1666};
1667
1668template<typename Val, bool All, bool Ucase, bool Ox>
1669requires std::is_unsigned_v<Val>
1670struct expr_hex_src {
1671 explicit constexpr expr_hex_src(Val v) : v_(v){}
1672 Val v_;
1673};
1674
1680enum HexFlags : unsigned {
1681 Short = 1,
1682 No0x = 2, //< without 0x prefix
1683 Lcase = 4, //< Use lower case
1684};
1685
1703template<unsigned Flags = 0, FromIntNumber T>
1704constexpr auto e_hex(T v) {
1705 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
1706}
1707
1717template<StrExpr A, typename Val, bool All, bool Ucase, bool Ox>
1718constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>, true> operator+(const A& a, const expr_hex_src<Val, All, Ucase, Ox>& b) {
1719 return {a, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>{b.v_}};
1720}
1721
1731template<StrExpr A, typename Val, bool All, bool Ucase, bool Ox>
1732constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>, false> operator+(const expr_hex_src<Val, All, Ucase, Ox>& b, const A& a) {
1733 return {a, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>{b.v_}};
1734}
1735
1745template<StrExpr A>
1747 return {a, (uintptr_t)b};
1748}
1749
1759template<StrExpr A>
1761 return {a, (uintptr_t)b};
1762}
1763
1764template<typename K, StrExprForType<K> A, bool Left>
1765struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
1766 using symb_type = K;
1767 K symbol_;
1768 size_t width_;
1769 const A& a_;
1770 mutable size_t alen_{};
1771 constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
1772
1773 constexpr size_t length() const noexcept {
1774 alen_ = a_.length();
1775 return std::max(alen_, width_);
1776 }
1777 constexpr K* place(K* ptr) const noexcept {
1778 if (alen_ >= width_) {
1779 return (K*)a_.place((typename A::symb_type*)ptr);
1780 }
1781 size_t w = width_ - alen_;
1782 if constexpr (Left) {
1783 ch_traits<K>::assign(ptr, w, symbol_);
1784 ptr += w;
1785 return (K*)a_.place((typename A::symb_type*)ptr);
1786 } else {
1787 ptr = (K*)a_.place((typename A::symb_type*)ptr);
1788 ch_traits<K>::assign(ptr, w, symbol_);
1789 return ptr + w;
1790 }
1791 }
1792};
1793
1815template<StrExpr A, typename K = typename A::symb_type>
1816expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
1817 return {symbol, width, a};
1818}
1819
1841template<StrExpr A, typename K = typename A::symb_type>
1842expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
1843 return {symbol, width, a};
1844}
1845
1846/*
1847* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
1848* K - тип символов строки
1849* T - тип контейнера строк (vector, list)
1850* I - длина разделителя в символах
1851* tail - добавлять разделитель после последнего элемента контейнера.
1852* Если контейнер пустой, разделитель в любом случае не добавляется
1853* skip_empty - пропускать пустые строки без добавления разделителя
1854* To create string concatenations with vectors and lists joined by a constant delimiter
1855* K is the symbols
1856* T - type of string container (vector, list)
1857* I - length of separator in characters
1858* tail - add a separator after the last element of the container.
1859* If the container is empty, the separator is not added anyway
1860* skip_empty - skip empty lines without adding a separator
1861*/
1862template<typename K, typename T, size_t I, bool tail, bool skip_empty>
1863struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
1864 using symb_type = K;
1865 using my_type = expr_join<K, T, I, tail, skip_empty>;
1866
1867 const T& s;
1868 const K* delim;
1869 constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
1870
1871 constexpr size_t length() const noexcept {
1872 size_t l = 0;
1873 for (const auto& t: s) {
1874 size_t len = t.length();
1875 if (len > 0 || !skip_empty) {
1876 if (I > 0 && l > 0) {
1877 l += I;
1878 }
1879 l += len;
1880 }
1881 }
1882 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
1883 }
1884 constexpr K* place(K* ptr) const noexcept {
1885 if (s.empty()) {
1886 return ptr;
1887 }
1888 K* write = ptr;
1889 for (const auto& t: s) {
1890 size_t copyLen = t.length();
1891 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
1892 ch_traits<K>::copy(write, delim, I);
1893 write += I;
1894 }
1895 ch_traits<K>::copy(write, t.data(), copyLen);
1896 write += copyLen;
1897 }
1898 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
1899 ch_traits<K>::copy(write, delim, I);
1900 write += I;
1901 }
1902 return write;
1903 }
1904};
1905
1919template<bool tail = false, bool skip_empty = false, typename L, typename K = typename const_lit<L>::symb_type, size_t I = const_lit<L>::Count, typename T>
1920inline constexpr auto e_join(const T& s, L&& d) {
1921 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
1922}
1923
1924template<size_t N>
1925concept is_const_pattern = N > 1 && N <= 17;
1926
1927template<typename K, size_t I>
1928struct _ascii_mask { // NOLINT
1929 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
1930};
1931
1932template<typename K>
1933struct _ascii_mask<K, 0> {
1934 constexpr static const size_t value = 0;
1935};
1936
1937template<typename K>
1938struct ascii_mask { // NOLINT
1939 using uns = std::make_unsigned_t<K>;
1940 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
1941 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
1942};
1943
1944template<typename K>
1945constexpr inline bool isAsciiUpper(K k) {
1946 return k >= 'A' && k <= 'Z';
1947}
1948
1949template<typename K>
1950constexpr inline bool isAsciiLower(K k) {
1951 return k >= 'a' && k <= 'z';
1952}
1953
1954template<typename K>
1955constexpr inline K makeAsciiLower(K k) {
1956 return isAsciiUpper(k) ? k | 0x20 : k;
1957}
1958
1959template<typename K>
1960constexpr inline K makeAsciiUpper(K k) {
1961 return isAsciiLower(k) ? k & ~0x20 : k;
1962}
1963
1964enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
1965template<TrimSides S, typename K, size_t N, bool withSpaces = false>
1966struct trim_operator;
1967
1968template<size_t I>
1969struct digits_selector {
1970 using wider_type = uint16_t;
1971};
1972
1973template<>
1974struct digits_selector<2> {
1975 using wider_type = uint32_t;
1976};
1977
1978template<>
1979struct digits_selector<4> {
1980 using wider_type = uint64_t;
1981};
1982
1993
1994template<bool CanNegate, bool CheckOverflow, typename T>
1995struct result_type_selector { // NOLINT
1996 using type = T;
1997};
1998
1999template<typename T>
2000struct result_type_selector<true, false, T> {
2001 using type = std::make_unsigned_t<T>;
2002};
2003
2004template<unsigned Base>
2005constexpr unsigned digit_width() {
2006 if (Base <=2) {
2007 return 1;
2008 }
2009 if (Base <= 4) {
2010 return 2;
2011 }
2012 if (Base <= 8) {
2013 return 3;
2014 }
2015 if (Base <= 16) {
2016 return 4;
2017 }
2018 if (Base <= 32) {
2019 return 5;
2020 }
2021 return 6;
2022}
2023
2024template<typename T, unsigned Base>
2025constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
2026
2027template<typename T>
2028struct convert_result {
2029 T value;
2031 size_t read;
2032};
2033
2034struct int_convert { // NOLINT
2035 inline static const uint8_t NUMBERS[] = {
2036 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2037 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
2038 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
2039 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
2040 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
2041 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2042 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2043 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2044 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2045 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
2046
2047 template<typename K, unsigned Base>
2048 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2049 auto us = static_cast<std::make_unsigned_t<K>>(s);
2050 if constexpr (Base <= 10) {
2051 return us - '0';
2052 } else {
2053 if constexpr (sizeof(K) == 1) {
2054 return NUMBERS[us];
2055 } else {
2056 return us < 256 ? NUMBERS[us] : us;
2057 }
2058 }
2059 }
2060
2061 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
2062 requires(Base != 0)
2063 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
2064 using u_type = std::make_unsigned_t<T>;
2065 #ifndef HAS_BUILTIN_OVERFLOW
2066 u_type maxMult = 0, maxAdd = 0;
2067 if constexpr (CheckOverflow) {
2068 maxMult = std::numeric_limits<u_type>::max() / Base;
2069 maxAdd = std::numeric_limits<u_type>::max() % Base;
2070 }
2071 #endif
2072 u_type number = 0;
2073 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2075 const K* from = current;
2076
2077 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2078
2079 if (no_need_check_o_f) {
2080 for (;;) {
2081 const u_type digit = toDigit<K, Base>(*current);
2082 if (digit >= Base) {
2083 break;
2084 }
2085 number = number * Base + digit;
2086 if (++current == end) {
2088 break;
2089 }
2090 }
2091 } else {
2092 for (;maxDigits; maxDigits--) {
2093 const u_type digit = toDigit<K, Base>(*current);
2094 if (digit >= Base) {
2095 break;
2096 }
2097 number = number * Base + digit;
2098 ++current;
2099 }
2100 if (!maxDigits) {
2101 // Прошли все цифры, дальше надо с проверкой на overflow
2102 // All numbers have passed, then we need to check for overflow
2103 for (;;) {
2104 const u_type digit = toDigit<K, Base>(*current);
2105 if (digit >= Base) {
2106 break;
2107 }
2108 #ifdef HAS_BUILTIN_OVERFLOW
2109 if (__builtin_mul_overflow(number, Base, &number) ||
2110 __builtin_add_overflow(number, digit, &number)) {
2111 #else
2112 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2113 number = number * Base + digit;
2114 } else {
2115 #endif
2117 while(++current < end) {
2118 if (toDigit<K, Base>(*current) >= Base) {
2119 break;
2120 }
2121 }
2122 break;
2123 }
2124 if (++current == end) {
2126 break;
2127 }
2128 }
2129 }
2130 }
2131 T result;
2132 if constexpr (std::is_signed_v<T>) {
2133 result = negate ? 0 - number : number;
2134 if constexpr (CheckOverflow) {
2135 if (error != IntConvertResult::Overflow) {
2136 if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
2138 }
2139 }
2140 }
2141 } else {
2142 result = number;
2143 }
2144 if (error == IntConvertResult::NotNumber && current > from) {
2146 }
2147 return {result, error, size_t(current - start)};
2148 }
2149public:
2150 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
2151 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
2152 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
2153 // If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
2154 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
2155 requires(Base == -1 || (Base < 37 && Base != 1))
2156 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
2157 const K *ptr = start, *end = ptr + len;
2158 bool negate = false;
2159 if constexpr (SkipWs) {
2160 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
2161 ptr++;
2162 }
2163 if (ptr != end) {
2164 if constexpr (std::is_signed_v<T>) {
2165 if constexpr (AllowSign) {
2166 // Может быть число, +число или -число
2167 // Can be a number, +number or -number
2168 if (*ptr == '+') {
2169 ptr++;
2170 } else if (*ptr == '-') {
2171 negate = true;
2172 ptr++;
2173 }
2174 } else {
2175 // Может быть число или -число
2176 // Can be a number or -number
2177 if (*ptr == '-') {
2178 negate = true;
2179 ptr++;
2180 }
2181 }
2182 } else if constexpr (AllowSign) {
2183 // Может быть число или +число
2184 // Can be a number or +number
2185 if (*ptr == '+') {
2186 ptr++;
2187 }
2188 }
2189 }
2190 if (ptr != end) {
2191 if constexpr (Base == 0 || Base == -1) {
2192 if (*ptr == '0') {
2193 ptr++;
2194 if (ptr != end) {
2195 if (*ptr == 'x' || *ptr == 'X') {
2196 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
2197 }
2198 if constexpr (Base == -1) {
2199 if (*ptr == 'b' || *ptr == 'B') {
2200 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
2201 }
2202 if (*ptr == 'o' || *ptr == 'O') {
2203 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
2204 }
2205 }
2206 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
2207 }
2208 return {0, IntConvertResult::Success, size_t(ptr - start)};
2209 }
2210 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
2211 } else
2212 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
2213 }
2214 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
2215 }
2216};
2217
2218template<typename K, typename Impl>
2219class null_terminated {
2220public:
2227 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
2228};
2229
2230template<typename K, typename Impl, bool Mutable> class buffer_pointers;
2231
2240template<typename K, typename Impl>
2241class buffer_pointers<K, Impl, false> {
2242 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
2243public:
2250 constexpr const K* data() const { return d().symbols(); }
2257 constexpr const K* begin() const { return d().symbols(); }
2264 constexpr const K* end() const { return d().symbols() + d().length(); }
2271 constexpr const K* cbegin() const { return d().symbols(); }
2278 constexpr const K* cend() const { return d().symbols() + d().length(); }
2279};
2280
2281template<typename K, typename Impl>
2282class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
2283 constexpr Impl& d() { return *static_cast<Impl*>(this); }
2284 using base = buffer_pointers<K, Impl, false>;
2285public:
2292 constexpr const K* data() const { return base::data(); }
2299 constexpr const K* begin() const { return base::begin(); }
2306 constexpr const K* end() const { return base::end(); }
2313 constexpr const K* cbegin() const { return base::cbegin(); }
2320 constexpr const K* cend() const { return base::cend(); }
2327 constexpr K* data() { return d().str(); }
2334 constexpr K* begin() { return d().str(); }
2341 constexpr K* end() { return d().str() + d().length(); }
2342};
2343
2350template<typename K, typename StrSrc>
2351class SplitterBase {
2352 using str_t = StrSrc;
2353 str_t text_;
2354 str_t delim_;
2355
2356public:
2357 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
2362 constexpr bool is_done() const {
2363 return text_.length() == str::npos;
2364 }
2365
2371 constexpr str_t next() {
2372 if (!text_.length()) {
2373 auto ret = text_;
2374 text_.str++;
2375 text_.len--;
2376 return ret;
2377 } else if (text_.length() == str::npos) {
2378 return {nullptr, 0};
2379 }
2380 size_t pos = text_.find(delim_), next = 0;
2381 if (pos == str::npos) {
2382 pos = text_.length();
2383 next = pos + 1;
2384 } else {
2385 next = pos + delim_.length();
2386 }
2387 str_t result{text_.str, pos};
2388 text_.str += next;
2389 text_.len -= next;
2390 return result;
2391 }
2392};
2393
2418template<typename K, typename StrRef, typename Impl, bool Mutable>
2419class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
2420 constexpr const Impl& d() const noexcept {
2421 return *static_cast<const Impl*>(this);
2422 }
2423 constexpr size_t _len() const noexcept {
2424 return d().length();
2425 }
2426 constexpr const K* _str() const noexcept {
2427 return d().symbols();
2428 }
2429 constexpr bool _is_empty() const noexcept {
2430 return d().is_empty();
2431 }
2432
2433public:
2434 using symb_type = K;
2435 using str_piece = StrRef;
2436 using traits = ch_traits<K>;
2437 using uns_type = std::make_unsigned_t<K>;
2438 using my_type = Impl;
2439 using base = str_src_algs<K, StrRef, Impl, Mutable>;
2440 str_src_algs() = default;
2441
2454 constexpr K* place(K* ptr) const noexcept {
2455 size_t myLen = _len();
2456 traits::copy(ptr, _str(), myLen);
2457 return ptr + myLen;
2458 }
2459
2469 void copy_to(K* buffer, size_t bufSize) {
2470 size_t tlen = std::min(_len(), bufSize - 1);
2471 traits::copy(buffer, _str(), tlen);
2472 buffer[tlen] = 0;
2473 }
2474
2480 constexpr size_t size() const {
2481 return _len();
2482 }
2483
2490 template<typename D = K> requires is_equal_str_type_v<K, D>
2491 constexpr std::basic_string_view<D> to_sv() const noexcept {
2492 return {(const D*)_str(), _len()};
2493 }
2494
2500 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
2501 constexpr operator std::basic_string_view<D, Traits>() const {
2502 return {(const D*)_str(), _len()};
2503 }
2504
2510 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
2511 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
2512 return {(const D*)_str(), _len()};
2513 }
2514
2520 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
2521 constexpr operator std::basic_string<D, Traits, Allocator>() const {
2522 return {(const D*)_str(), _len()};
2523 }
2524
2530 constexpr operator str_piece() const noexcept {
2531 return str_piece{_str(), _len()};
2532 }
2533
2539 constexpr str_piece to_str() const noexcept {
2540 return {_str(), _len()};
2541 }
2542
2565 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
2566 size_t myLen = _len(), idxStart = from >= 0 ? from : myLen > -from ? myLen + from : 0,
2567 idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
2568 if (idxEnd > myLen)
2569 idxEnd = myLen;
2570 if (idxStart > idxEnd)
2571 idxStart = idxEnd;
2572 return str_piece{_str() + idxStart, idxEnd - idxStart};
2573 }
2574
2584 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
2585 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
2586 if (idxEnd > myLen)
2587 idxEnd = myLen;
2588 if (idxStart > idxEnd)
2589 idxStart = idxEnd;
2590 return str_piece{_str() + idxStart, idxEnd - idxStart};
2591 }
2592
2606 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
2607 return str_piece{_str() + from, to - from};
2608 }
2609
2613 constexpr bool operator!() const noexcept {
2614 return _is_empty();
2615 }
2616
2626 constexpr K at(ptrdiff_t idx) const {
2627 return _str()[idx >= 0 ? idx : _len() + idx];
2628 }
2629 // Сравнение строк
2630 // String comparison
2631 constexpr int compare(const K* text, size_t len) const {
2632 size_t myLen = _len();
2633 int cmp = traits::compare(_str(), text, std::min(myLen, len));
2634 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
2635 }
2644 constexpr int compare(str_piece o) const {
2645 return compare(o.symbols(), o.length());
2646 }
2647
2655 constexpr int strcmp(const K* text) const {
2656 size_t myLen = _len(), idx = 0;
2657 const K* ptr = _str();
2658 for (; idx < myLen; idx++) {
2659 uns_type s1 = (uns_type)text[idx];
2660 if (!s1) {
2661 return 1;
2662 }
2663 uns_type s2 = (uns_type)ptr[idx];
2664 if (s1 < s2) {
2665 return 1;
2666 } else if (s1 > s2) {
2667 return -1;
2668 }
2669 }
2670 return text[idx] == 0 ? 0 : -1;
2671 }
2672
2673 constexpr bool equal(const K* text, size_t len) const noexcept {
2674 return len == _len() && traits::compare(_str(), text, len) == 0;
2675 }
2684 constexpr bool equal(str_piece other) const noexcept {
2685 return equal(other.symbols(), other.length());
2686 }
2687
2695 constexpr bool operator==(const base& other) const noexcept {
2696 return equal(other._str(), other._len());
2697 }
2698
2704 constexpr auto operator<=>(const base& other) const noexcept {
2705 return compare(other._str(), other._len()) <=> 0;
2706 }
2707
2713 template<typename T, size_t N = const_lit_for<K, T>::Count>
2714 constexpr bool operator==(T&& other) const noexcept {
2715 return N - 1 == _len() && traits::compare(_str(), other, N - 1) == 0;
2716 }
2717
2723 template<typename T, size_t N = const_lit_for<K, T>::Count>
2724 constexpr auto operator<=>(T&& other) const noexcept {
2725 size_t myLen = _len();
2726 int cmp = traits::compare(_str(), other, std::min(myLen, N - 1));
2727 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
2728 return res <=> 0;
2729 }
2730
2731 // Сравнение ascii строк без учёта регистра
2732 // Compare ascii strings without taking into account case
2733 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
2734 if (!len)
2735 return _is_empty() ? 0 : 1;
2736 size_t myLen = _len(), checkLen = std::min(myLen, len);
2737 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
2738 while (checkLen--) {
2739 uns_type s1 = *ptr1++, s2 = *ptr2++;
2740 if (s1 == s2)
2741 continue;
2742 s1 = makeAsciiLower(s1);
2743 s2 = makeAsciiLower(s2);
2744 if (s1 > s2)
2745 return 1;
2746 else if (s1 < s2)
2747 return -1;
2748 }
2749 return myLen == len ? 0 : myLen > len ? 1 : -1;
2750 }
2759 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
2760 return compare_ia(text.symbols(), text.length());
2761 }
2762
2771 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
2772 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
2773 }
2774
2782 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
2783 return compare_ia(text.symbols(), text.length()) < 0;
2784 }
2785
2786 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
2787 size_t lenText = _len();
2788 // Образец, не вмещающийся в строку и пустой образец не находим
2789 // We don't look for an empty line or a line longer than the text.
2790 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
2791 return str::npos;
2792 lenPattern--;
2793 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
2794 pattern++;
2795 for (const K* fnd = text + offset;; ++fnd) {
2796 fnd = traits::find(fnd, last - fnd, first);
2797 if (!fnd)
2798 return str::npos;
2799 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
2800 return static_cast<size_t>(fnd - text);
2801 }
2802 }
2813 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
2814 return find(pattern.symbols(), pattern.length(), offset);
2815 }
2816
2832 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
2833 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
2834 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
2835 return fnd;
2836 }
2837 throw Exc(std::forward<Args>(args)...);
2838 }
2839
2849 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
2850 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
2851 return fnd == str::npos ? fnd : fnd + pattern.length();
2852 }
2853
2863 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
2864 auto fnd = find(pattern.symbols(), pattern.length(), offset);
2865 return fnd == str::npos ? _len() : fnd;
2866 }
2867
2877 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
2878 auto fnd = find(pattern.symbols(), pattern.length(), offset);
2879 return fnd == str::npos ? _len() : fnd + pattern.length();
2880 }
2881
2882 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
2883 if (lenPattern == 1)
2884 return find_last(pattern[0], offset);
2885 size_t lenText = std::min(_len(), offset);
2886 // Образец, не вмещающийся в строку и пустой образец не находим
2887 // We don't look for an empty line or a line longer than the text.
2888 if (!lenPattern || lenPattern > lenText)
2889 return str::npos;
2890
2891 lenPattern--;
2892 const K *text = _str() + lenPattern, last = pattern[lenPattern];
2893 lenText -= lenPattern;
2894 while(lenText) {
2895 if (text[--lenText] == last) {
2896 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
2897 return lenText;
2898 }
2899 }
2900 }
2901 return str::npos;
2902 }
2913 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
2914 return find_last(pattern.symbols(), pattern.length(), offset);
2915 }
2916
2926 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
2927 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
2928 return fnd == str::npos ? fnd : fnd + pattern.length();
2929 }
2930
2940 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
2941 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
2942 return fnd == str::npos ? _len() : fnd;
2943 }
2944
2954 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
2955 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
2956 return fnd == str::npos ? _len() : fnd + pattern.length();
2957 }
2958
2968 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
2969 return find(pattern, offset) != str::npos;
2970 }
2971
2981 constexpr size_t find(K s, size_t offset = 0) const noexcept {
2982 size_t len = _len();
2983 if (offset < len) {
2984 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
2985 if (fnd)
2986 return static_cast<size_t>(fnd - str);
2987 }
2988 return str::npos;
2989 }
2990
3000 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
3001 size_t len = _len();
3002 if (offset < len) {
3003 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3004 if (fnd)
3005 return static_cast<size_t>(fnd - str);
3006 }
3007 return len;
3008 }
3009
3010 template<typename Op>
3011 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3012 if (!maxCount)
3013 maxCount--;
3014 while (maxCount-- > 0) {
3015 size_t fnd = find(pattern, patternLen, offset);
3016 if (fnd == str::npos)
3017 break;
3018 op(fnd);
3019 offset = fnd + patternLen;
3020 }
3021 }
3034 template<typename Op>
3035 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3036 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
3037 }
3038
3039 template<typename To = std::vector<size_t>>
3040 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3041 To result;
3042 for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
3043 return result;
3044 }
3057 template<typename To = std::vector<size_t>>
3058 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3059 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
3060 }
3061 template<typename To = std::vector<size_t>>
3062 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
3063 return for_all_finded([&](size_t pos) {
3064 to.emplace_back(pos);
3065 }, pattern, len, offset, maxCount);
3066 }
3077 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
3078 size_t len = std::min(_len(), offset);
3079 const K *text = _str();
3080 while (len > 0) {
3081 if (text[--len] == s)
3082 return len;
3083 }
3084 return str::npos;
3085 }
3086
3096 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
3097 return std::string_view{_str(), _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
3098 }
3099
3109 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
3110 const K* text = _str();
3111 size_t fnd = std::string_view{text, _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
3112 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
3113 }
3114
3124 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
3125 return std::string_view{_str(), _len()}.find_first_not_of(std::string_view{pattern.str, pattern.len}, offset);
3126 }
3127
3137 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
3138 return std::string_view{_str(), _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
3139 }
3140
3150 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
3151 const K* text = _str();
3152 size_t fnd = std::string_view{text, _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
3153 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
3154 }
3155
3165 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
3166 return std::string_view{_str(), _len()}.find_last_not_of(std::string_view{pattern.str, pattern.len}, offset);
3167 }
3168
3178 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
3179 return my_type{d()(from, len)};
3180 }
3181
3191 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
3192 return my_type{d().mid(from, len)};
3193 }
3194
3222 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
3223 constexpr T as_int() const noexcept {
3224 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
3225 return err == IntConvertResult::Overflow ? 0 : res;
3226 }
3227
3255 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
3256 constexpr convert_result<T> to_int() const noexcept {
3257 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
3258 }
3259
3265 template<bool SkipWS = true, bool AllowPlus = true> requires (sizeof(K) == 1)
3266 std::optional<double> to_double() const noexcept {
3267 size_t len = _len();
3268 const K* ptr = _str();
3269 if constexpr (SkipWS) {
3270 while (len && uns_type(*ptr) <= ' ') {
3271 len--;
3272 ptr++;
3273 }
3274 }
3275 if constexpr (AllowPlus) {
3276 if (len && *ptr == K('+')) {
3277 ptr++;
3278 len--;
3279 }
3280 }
3281 if (!len) {
3282 return {};
3283 }
3284 double d{};
3285 if (std::from_chars((const u8s*)ptr, (const u8s*)ptr + len, d).ec == std::errc{}) {
3286 return d;
3287 }
3288 return {};
3289 }
3290
3296 template<bool SkipWS = true> requires (sizeof(K) == 1)
3297 std::optional<double> to_double_hex() const noexcept {
3298 size_t len = _len();
3299 const K* ptr = _str();
3300 if constexpr (SkipWS) {
3301 while (len && uns_type(*ptr) <= ' ') {
3302 len--;
3303 ptr++;
3304 }
3305 }
3306 if (len) {
3307 double d{};
3308 if (std::from_chars(ptr, ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
3309 return d;
3310 }
3311 }
3312 return {};
3313 }
3314
3322 template<ToIntNumber T>
3323 constexpr void as_number(T& t) const {
3324 t = as_int<T>();
3325 }
3326
3327 template<typename T, typename Op>
3328 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
3329 size_t mylen = _len();
3330 std::conditional_t<std::is_same_v<T, void>, char, T> results;
3331 str_piece me{_str(), mylen};
3332 for (int i = 0;; i++) {
3333 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
3334 if (beginOfDelim == str::npos) {
3335 str_piece last{me.symbols() + offset, me.length() - offset};
3336 if constexpr (std::is_invocable_v<Op, str_piece&>) {
3337 beforeFunc(last);
3338 }
3339 if constexpr (requires { results.emplace_back(last); }) {
3340 if (last.is_same(me)) {
3341 // Пробуем положить весь объект.
3342 // Try to put the entire object.
3343 results.emplace_back(d());
3344 } else {
3345 results.emplace_back(last);
3346 }
3347 } else if constexpr (requires { results.push_back(last); }) {
3348 if (last.is_same(me)) {
3349 // Пробуем положить весь объект.
3350 // Try to put the entire object.
3351 results.push_back(d());
3352 } else {
3353 results.push_back(last);
3354 }
3355 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
3356 if (i < std::size(results)) {
3357 if (last.is_same(me)) {
3358 // Пробуем положить весь объект.
3359 // Try to put the entire object.
3360 results[i] = d();
3361 } else
3362 results[i] = last;
3363 }
3364 }
3365 break;
3366 }
3367 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
3368 if constexpr (std::is_invocable_v<Op, str_piece&>) {
3369 beforeFunc(piece);
3370 }
3371 if constexpr (requires { results.emplace_back(piece); }) {
3372 results.emplace_back(piece);
3373 } else if constexpr (requires { results.push_back(piece); }) {
3374 results.push_back(piece);
3375 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
3376 if (i < std::size(results)) {
3377 results[i] = piece;
3378 if (i == results.size() - 1) {
3379 break;
3380 }
3381 }
3382 }
3383 offset = beginOfDelim + lendelimiter;
3384 }
3385 if constexpr (!std::is_same_v<T, void>) {
3386 return results;
3387 }
3388 }
3419 template<typename T, typename Op>
3420 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
3421 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
3422 }
3423
3435 template<typename T>
3436 constexpr T split(str_piece delimiter, size_t offset = 0) const {
3437 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
3438 }
3439
3440 // Начинается ли эта строка с указанной подстроки
3441 // Does this string start with the specified substring
3442 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
3443 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
3444 }
3451 constexpr bool starts_with(str_piece prefix) const noexcept {
3452 return starts_with(prefix.symbols(), prefix.length());
3453 }
3454
3455 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
3456 size_t myLen = _len();
3457 if (myLen < len) {
3458 return false;
3459 }
3460 const K* ptr1 = _str();
3461 while (len--) {
3462 K s1 = *ptr1++, s2 = *prefix++;
3463 if (s1 == s2)
3464 continue;
3465 if (makeAsciiLower(s1) != makeAsciiLower(s2))
3466 return false;
3467 }
3468 return true;
3469 }
3476 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
3477 return starts_with_ia(prefix.symbols(), prefix.length());
3478 }
3479
3480 // Является ли эта строка началом указанной строки
3481 // Is this string the beginning of the specified string
3482 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
3483 size_t myLen = _len();
3484 if (myLen > len)
3485 return false;
3486 return !myLen || 0 == traits::compare(text, _str(), myLen);
3487 }
3494 constexpr bool prefix_in(str_piece text) const noexcept {
3495 return prefix_in(text.symbols(), text.length());
3496 }
3497 // Заканчивается ли строка указанной подстрокой
3498 // Does the string end with the specified substring
3499 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
3500 size_t myLen = _len();
3501 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
3502 }
3509 constexpr bool ends_with(str_piece suffix) const noexcept {
3510 return ends_with(suffix.symbols(), suffix.length());
3511 }
3512 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
3513 // Whether the string ends with the specified substring, case insensitive ASCII
3514 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
3515 size_t myLen = _len();
3516 if (myLen < len) {
3517 return false;
3518 }
3519 const K* ptr1 = _str() + myLen - len;
3520 while (len--) {
3521 K s1 = *ptr1++, s2 = *suffix++;
3522 if (s1 == s2)
3523 continue;
3524 if (makeAsciiLower(s1) != makeAsciiLower(s2))
3525 return false;
3526 }
3527 return true;
3528 }
3535 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
3536 return ends_with_ia(suffix.symbols(), suffix.length());
3537 }
3538
3542 constexpr bool is_ascii() const noexcept {
3543 if (_is_empty())
3544 return true;
3545 const int sl = ascii_mask<K>::WIDTH;
3546 const size_t mask = ascii_mask<K>::VALUE;
3547 size_t len = _len();
3548 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
3549 if constexpr (sl > 1) {
3550 const size_t roundMask = sizeof(size_t) - 1;
3551 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
3552 if (*ptr++ > 127)
3553 return false;
3554 len--;
3555 }
3556 while (len >= sl) {
3557 if (*reinterpret_cast<const size_t*>(ptr) & mask)
3558 return false;
3559 ptr += sl;
3560 len -= sl;
3561 }
3562 }
3563 while (len--) {
3564 if (*ptr++ > 127)
3565 return false;
3566 }
3567 return true;
3568 }
3569
3577 template<typename R = my_type>
3579 return R::upperred_only_ascii_from(d());
3580 }
3581
3589 template<typename R = my_type>
3591 return R::lowered_only_ascii_from(d());
3592 }
3593
3609 template<typename R = my_type>
3610 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
3611 return R::replaced_from(d(), pattern, repl, offset, maxCount);
3612 }
3613
3614 template<StrType<K> From>
3615 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
3616 str_piece sfrom = from, newPos = opTrim(sfrom);
3617 if (newPos.is_same(sfrom)) {
3618 my_type res = from;
3619 return res;
3620 }
3621 return my_type{newPos};
3622 }
3623 template<TrimSides S, StrType<K> From>
3624 constexpr static my_type trim_static(const From& from) {
3625 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
3626 }
3627
3628 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
3629 requires is_const_pattern<N>
3630 constexpr static my_type trim_static(const From& from, T&& pattern) {
3631 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
3632 }
3633
3634 template<TrimSides S, bool withSpaces, StrType<K> From>
3635 constexpr static my_type trim_static(const From& from, str_piece pattern) {
3636 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
3637 }
3646 template<typename R = str_piece>
3647 constexpr R trimmed() const {
3648 return R::template trim_static<TrimSides::TrimAll>(d());
3649 }
3650
3658 template<typename R = str_piece>
3659 R trimmed_left() const {
3660 return R::template trim_static<TrimSides::TrimLeft>(d());
3661 }
3662
3670 template<typename R = str_piece>
3671 R trimmed_right() const {
3672 return R::template trim_static<TrimSides::TrimRight>(d());
3673 }
3674
3684 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3685 requires is_const_pattern<N>
3686 R trimmed(T&& pattern) const {
3687 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
3688 }
3689
3699 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3700 requires is_const_pattern<N>
3701 R trimmed_left(T&& pattern) const {
3702 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
3703 }
3704
3714 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3715 requires is_const_pattern<N>
3716 R trimmed_right(T&& pattern) const {
3717 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
3718 }
3719 // Триминг по символам в литерале и пробелам
3720 // Trimming by characters in literal and spaces
3721
3736 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3737 requires is_const_pattern<N>
3738 R trimmed_with_spaces(T&& pattern) const {
3739 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
3740 }
3741
3755 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3756 requires is_const_pattern<N>
3757 R trimmed_left_with_spaces(T&& pattern) const {
3758 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
3759 }
3760
3774 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
3775 requires is_const_pattern<N>
3776 R trimmed_right_with_spaces(T&& pattern) const {
3777 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
3778 }
3779 // Триминг по динамическому источнику
3780 // Trimming by dynamic source
3781
3792 template<typename R = str_piece>
3793 R trimmed(str_piece pattern) const {
3794 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
3795 }
3796
3806 template<typename R = str_piece>
3807 R trimmed_left(str_piece pattern) const {
3808 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
3809 }
3810
3820 template<typename R = str_piece>
3821 R trimmed_right(str_piece pattern) const {
3822 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
3823 }
3824
3838 template<typename R = str_piece>
3839 R trimmed_with_spaces(str_piece pattern) const {
3840 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
3841 }
3842
3856 template<typename R = str_piece>
3857 R trimmed_left_with_spaces(str_piece pattern) const {
3858 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
3859 }
3860
3874 template<typename R = str_piece>
3875 R trimmed_right_with_spaces(str_piece pattern) const {
3876 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
3877 }
3878
3889 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
3890 return SplitterBase<K, str_piece>{*this, delimiter};
3891 }
3892};
3893
3894template<size_t N> requires (N > 1)
3895struct find_all_container {
3896 static constexpr size_t max_capacity = N;
3897 size_t positions_[N];
3898 size_t added_{};
3899
3900 void emplace_back(size_t pos) {
3901 positions_[added_++] = pos;
3902 }
3903};
3904
3923template<typename K>
3924struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
3925 using symb_type = K;
3926 using my_type = str_src<K>;
3927
3928 const symb_type* str;
3929 size_t len;
3930
3931 str_src() = default;
3936 template<typename T, size_t N = const_lit_for<K, T>::Count>
3937 constexpr str_src(T&& v) noexcept : str(v), len(N - 1) {}
3938
3943 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
3944
3945 template<StrType<K> T>
3946 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
3947
3952 template<typename A>
3953 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
3958 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
3959
3964 constexpr size_t length() const noexcept {
3965 return len;
3966 }
3967
3971 constexpr const symb_type* symbols() const noexcept {
3972 return str;
3973 }
3974
3978 constexpr bool is_empty() const noexcept {
3979 return len == 0;
3980 }
3981
3987 constexpr bool is_same(str_src<K> other) const noexcept {
3988 return str == other.str && len == other.len;
3989 }
3990
3996 constexpr bool is_part_of(str_src<K> other) const noexcept {
3997 return str >= other.str && str + len <= other.str + other.len;
3998 }
3999
4007 constexpr K operator[](size_t idx) const {
4008 return str[idx];
4009 }
4010
4018 constexpr my_type& remove_prefix(size_t delta) {
4019 str += delta;
4020 len -= delta;
4021 return *this;
4022 }
4023
4031 constexpr my_type& remove_suffix(size_t delta) {
4032 len -= delta;
4033 return *this;
4034 }
4035};
4036
4059template<typename K>
4060struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
4061 using symb_type = K;
4062 using my_type = str_src_nt<K>;
4063 using base = str_src<K>;
4064
4065 constexpr static const K empty_string[1] = {0};
4066
4067 str_src_nt() = default;
4084 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
4085 constexpr explicit str_src_nt(T&& p) noexcept {
4086 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
4087 base::str = base::len ? p : empty_string;
4088 }
4089
4093 template<typename T, size_t N = const_lit_for<K, T>::Count>
4094 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
4095
4100 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
4101
4102 template<StrType<K> T>
4103 constexpr str_src_nt(T&& t) {
4104 base::str = t.symbols();
4105 base::len = t.length();
4106 }
4111 template<typename A>
4112 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
4113
4114 static const my_type empty_str;
4123 constexpr my_type to_nts(size_t from) {
4124 if (from > base::len) {
4125 from = base::len;
4126 }
4127 return {base::str + from, base::len - from};
4128 }
4129};
4130
4131template<typename K>
4132inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
4133template<typename K> struct simple_str_selector;
4134
4135#ifndef IN_FULL_SIMSTR
4136
4137template<typename K>
4138using simple_str = str_src<K>;
4139
4140template<typename K>
4141struct simple_str_selector {
4142 using type = simple_str<K>;
4143};
4144
4145template<typename K>
4146using simple_str_nt = str_src_nt<K>;
4147
4148template<typename K>
4149using Splitter = SplitterBase<K, str_src<K>>;
4150
4151using ssa = str_src<u8s>;
4152using ssb = str_src<ubs>;
4153using ssw = str_src<wchar_t>;
4154using ssu = str_src<u16s>;
4155using ssuu = str_src<u32s>;
4156using stra = str_src_nt<u8s>;
4157using strb = str_src_nt<ubs>;
4158using strw = str_src_nt<wchar_t>;
4159using stru = str_src_nt<u16s>;
4160using struu = str_src_nt<u32s>;
4161
4162inline namespace literals {
4163
4174SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4175 return str_src_nt<u8s>{ptr, l};
4176}
4177
4187SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4188 return str_src_nt<ubs>{ptr, l};
4189}
4190
4200SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4201 return str_src_nt<uws>{ptr, l};
4202}
4203
4213SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4214 return str_src_nt<u16s>{ptr, l};
4215}
4216
4227SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4228 return str_src_nt<u32s>{ptr, l};
4229}
4230
4231} // namespace literals
4232
4233#endif
4234
4235template<typename K, bool withSpaces>
4236struct CheckSpaceTrim {
4237 constexpr bool is_trim_spaces(K s) const {
4238 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
4239 }
4240};
4241template<typename K>
4242struct CheckSpaceTrim<K, false> {
4243 constexpr bool is_trim_spaces(K) const {
4244 return false;
4245 }
4246};
4247
4248template<typename K>
4249struct CheckSymbolsTrim {
4250 str_src<K> symbols;
4251 constexpr bool is_trim_symbols(K s) const {
4252 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
4253 }
4254};
4255
4256template<typename K, size_t N>
4257struct CheckConstSymbolsTrim {
4258 const const_lit_to_array<K, N> symbols;
4259
4260 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
4261 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
4262
4263 constexpr bool is_trim_symbols(K s) const noexcept {
4264 return symbols.contain(s);
4265 }
4266};
4267
4268template<typename K>
4269struct CheckConstSymbolsTrim<K, 0> {
4270 constexpr bool is_trim_symbols(K) const {
4271 return false;
4272 }
4273};
4274
4275template<typename K, size_t N>
4276struct SymbSelector {
4277 using type = CheckConstSymbolsTrim<K, N>;
4278};
4279
4280template<typename K>
4281struct SymbSelector<K, 0> {
4282 using type = CheckSymbolsTrim<K>;
4283};
4284
4285template<typename K>
4286struct SymbSelector<K, static_cast<size_t>(-1)> {
4287 using type = CheckConstSymbolsTrim<K, 0>;
4288};
4289
4290template<TrimSides S, typename K, size_t N, bool withSpaces>
4291struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
4292 constexpr bool isTrim(K s) const {
4293 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
4294 }
4295 constexpr str_src<K> operator()(str_src<K> from) const {
4296 if constexpr ((S & TrimSides::TrimLeft) != 0) {
4297 while (from.len) {
4298 if (isTrim(*from.str)) {
4299 from.str++;
4300 from.len--;
4301 } else
4302 break;
4303 }
4304 }
4305 if constexpr ((S & TrimSides::TrimRight) != 0) {
4306 const K* back = from.str + from.len - 1;
4307 while (from.len) {
4308 if (isTrim(*back)) {
4309 back--;
4310 from.len--;
4311 } else
4312 break;
4313 }
4314 }
4315 return from;
4316 }
4317};
4318
4319template<TrimSides S, typename K>
4320using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
4321
4322template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
4323 requires is_const_pattern<N>
4324constexpr inline auto trimOp(T&& pattern) {
4325 return trim_operator<S, K, N - 1, withSpaces>{pattern};
4326}
4327
4328template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
4329constexpr inline auto trimOp(str_src<K> pattern) {
4330 return trim_operator<S, K, 0, withSpaces>{pattern};
4331}
4332
4333template<typename T>
4334concept StrSource = StdStrSource<T> || requires {
4335 typename std::remove_cvref_t<T>::symb_type;
4336};
4337
4338template<typename T>
4339struct to_src_str_base {
4340 using symb_type = typename T::symb_type;
4341 static str_src<symb_type> get(const T& s) {
4342 return {s.symbols(), s.length()};
4343 }
4344};
4345
4346struct to_src_str_base_none {};
4347
4348template<typename T>
4349struct to_src_str : std::conditional_t<StrSource<T>, to_src_str_base<T>, to_src_str_base_none> {
4350};
4351
4352template<typename K, typename T, typename A>
4353struct to_src_str<std::basic_string<K, T, A>> {
4354 using symb_type = K;
4355 static str_src<K> get(const std::basic_string<K, T, A>& s) {
4356 return {s.data(), s.length()};
4357 }
4358};
4359
4360template<typename K, typename T>
4361struct to_src_str<std::basic_string_view<K, T>> {
4362 using symb_type = K;
4363 static str_src<K> get(const std::basic_string_view<K, T>& s) {
4364 return {s.data(), s.length()};
4365 }
4366};
4367
4368template<typename T>
4369using src_str_t = to_src_str<std::remove_cvref_t<T>>::symb_type;
4370
4371template<typename T>
4372auto get_str_src_from(T&& t) {
4373 return to_src_str<std::remove_cvref_t<T>>::get(std::forward<T>(t));
4374}
4375
4376static constexpr size_t FIND_CACHE_SIZE = 16;
4377
4378template<typename K, size_t N, size_t L>
4379struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
4380 using symb_type = K;
4381 using my_type = expr_replaces<K, N, L>;
4382 str_src<K> what;
4383 const K(&pattern)[N + 1];
4384 const K(&repl)[L + 1];
4385 mutable find_all_container<FIND_CACHE_SIZE> matches_;
4386 mutable size_t last_;
4387
4388 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
4389
4390 constexpr size_t length() const {
4391 size_t l = what.length();
4392 if constexpr (N == L) {
4393 return l;
4394 }
4395 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
4396 if (matches_.added_) {
4397 last_ = matches_.positions_[matches_.added_ - 1] + N;
4398 l += int(L - N) * matches_.added_;
4399
4400 if (matches_.added_ == FIND_CACHE_SIZE) {
4401 for (;;) {
4402 size_t next = what.find(pattern, N, last_);
4403 if (next == str::npos) {
4404 break;
4405 }
4406 last_ = next + N;
4407 l += L - N;
4408 }
4409 }
4410 }
4411 if (!l) {
4412 matches_.added_ = -1;
4413 }
4414 return l;
4415 }
4416 constexpr K* place(K* ptr) const noexcept {
4417 if constexpr (N == L) {
4418 const K* from = what.symbols();
4419 for (size_t start = 0; start < what.length();) {
4420 size_t next = what.find(pattern, N, start);
4421 if (next == str::npos) {
4422 next = what.length();
4423 }
4424 size_t delta = next - start;
4425 ch_traits<K>::copy(ptr, from + start, delta);
4426 ptr += delta;
4427 ch_traits<K>::copy(ptr, repl, L);
4428 ptr += L;
4429 start = next + N;
4430 }
4431 return ptr;
4432 }
4433 if (matches_.added_ == 0) {
4434 return what.place(ptr);
4435 } else if (matches_.added_ == -1) {
4436 // after replaces text become empty
4437 return ptr;
4438 }
4439 const K* from = what.symbols();
4440 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
4441 ch_traits<K>::copy(ptr, from + start, offset - start);
4442 ptr += offset - start;
4443 ch_traits<K>::copy(ptr, repl, L);
4444 ptr += L;
4445 start = offset + N;
4446 if (start >= last_) {
4447 size_t tail = what.length() - last_;
4448 ch_traits<K>::copy(ptr, from + last_, tail);
4449 ptr += tail;
4450 break;
4451 } else {
4452 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
4453 }
4454 }
4455 return ptr;
4456 }
4457};
4458
4472template<StrSource A, typename K = src_str_t<A>, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
4473 requires(N > 1)
4474constexpr auto e_repl(A&& w, T&& p, X&& r) {
4475 return expr_replaces<K, N - 1, L - 1>{get_str_src_from(std::forward<A>(w)), p, r};
4476}
4477
4485template<typename K>
4486struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
4487 using symb_type = K;
4488 using my_type = expr_replaced<K>;
4489 str_src<K> what;
4490 const str_src<K> pattern;
4491 const str_src<K> repl;
4492 mutable find_all_container<FIND_CACHE_SIZE> matches_;
4493 mutable size_t last_;
4504 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
4505
4506 constexpr size_t length() const {
4507 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
4508 if (!plen || plen == rlen) {
4509 return l;
4510 }
4511 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
4512 if (matches_.added_) {
4513 last_ = matches_.positions_[matches_.added_ - 1] + plen;
4514 l += int(rlen - plen) * matches_.added_;
4515
4516 if (matches_.added_ == FIND_CACHE_SIZE) {
4517 for (;;) {
4518 size_t next = what.find(pattern.symbols(), plen, last_);
4519 if (next == str::npos) {
4520 break;
4521 }
4522 last_ = next + plen;
4523 l += rlen - plen;
4524 }
4525 }
4526 }
4527 if (!l) {
4528 matches_.added_ = -1;
4529 }
4530 return l;
4531 }
4532 constexpr K* place(K* ptr) const noexcept {
4533 size_t plen = pattern.length(), rlen = repl.length();
4534 if (plen == rlen) {
4535 const K* from = what.symbols();
4536 for (size_t start = 0; start < what.length();) {
4537 size_t next = what.find(pattern, start);
4538 if (next == str::npos) {
4539 next = what.length();
4540 }
4541 size_t delta = next - start;
4542 ch_traits<K>::copy(ptr, from + start, delta);
4543 ptr += delta;
4544 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
4545 ptr += rlen;
4546 start = next + plen;
4547 }
4548 return ptr;
4549 }
4550 if (matches_.added_ == 0) {
4551 return what.place(ptr);
4552 } else if (matches_.added_ == -1) {
4553 // after replaces text become empty
4554 return ptr;
4555 }
4556 const K* from = what.symbols();
4557 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
4558 ch_traits<K>::copy(ptr, from + start, offset - start);
4559 ptr += offset - start;
4560 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
4561 ptr += rlen;
4562 start = offset + plen;
4563 if (start >= last_) {
4564 size_t tail = what.length() - start;
4565 ch_traits<K>::copy(ptr, from + start, tail);
4566 ptr += tail;
4567 break;
4568 } else {
4569 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
4570 }
4571 }
4572 return ptr;
4573 }
4574};
4575
4591template<typename K, StrExprForType<K> E>
4592struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
4593 using symb_type = K;
4594 using my_type = expr_replaced<K>;
4595 str_src<K> what;
4596 const str_src<K> pattern;
4597 mutable size_t replLen;
4598 mutable find_all_container<FIND_CACHE_SIZE> matches_;
4599 mutable size_t last_;
4600 const E& expr;
4611 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
4612
4613 constexpr size_t length() const {
4614 size_t l = what.length(), plen = pattern.length();
4615 if (!plen) {
4616 return l;
4617 }
4618 matches_.positions_[0] = what.find(pattern);
4619 if (matches_.positions_[0] == -1) {
4620 // Не нашли вхождений, нечего менять
4621 return l;
4622 }
4623 matches_.added_ = 1;
4624 // Вхождение есть, надо теперь получить длину замены
4625 replLen = expr.length();
4626 if (replLen == plen) {
4627 // Замена той же длины, общая длина не изменится
4628 return l;
4629 }
4630 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
4631
4632 last_ = matches_.positions_[matches_.added_ - 1] + plen;
4633 l += int(replLen - plen) * matches_.added_;
4634
4635 if (matches_.added_ == FIND_CACHE_SIZE) {
4636 for (;;) {
4637 size_t next = what.find(pattern.symbols(), plen, last_);
4638 if (next == str::npos) {
4639 break;
4640 }
4641 last_ = next + plen;
4642 l += replLen - plen;
4643 }
4644 }
4645 if (!l) {
4646 matches_.added_ = -1;
4647 }
4648 return l;
4649 }
4650 constexpr K* place(K* ptr) const noexcept {
4651 if (matches_.added_ == 0) {
4652 // не было найдено вхождений
4653 return what.place(ptr);
4654 } else if (matches_.added_ == -1) {
4655 // Строка стала пустой
4656 return ptr;
4657 }
4658 size_t plen = pattern.length();
4659 const K* from = what.symbols();
4660 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
4661 ptr += matches_.positions_[0];
4662 const K* repl = ptr;
4663 expr.place((typename E::symb_type*)ptr);
4664 ptr += replLen;
4665 size_t start = matches_.positions_[0] + plen;
4666
4667 if (plen == replLen) {
4668 for (;;) {
4669 size_t next = what.find(pattern, start);
4670 if (next == str::npos) {
4671 break;
4672 }
4673 size_t delta = next - start;
4674 ch_traits<K>::copy(ptr, from + start, delta);
4675 ptr += delta;
4676 ch_traits<K>::copy(ptr, repl, replLen);
4677 ptr += replLen;
4678 start = next + plen;
4679 }
4680 } else {
4681 for (size_t idx = 1;;) {
4682 if (start >= last_) {
4683 break;
4684 }
4685 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
4686 size_t delta = next - start;
4687 ch_traits<K>::copy(ptr, from + start, delta);
4688 ptr += delta;
4689 ch_traits<K>::copy(ptr, repl, replLen);
4690 ptr += replLen;
4691 start = next + plen;
4692 }
4693 }
4694 size_t tail = what.length() - start;
4695 ch_traits<K>::copy(ptr, from + start, tail);
4696 return ptr + tail;
4697 }
4698};
4699
4713template<StrSource A, typename K = src_str_t<A>, typename T, typename X>
4714 requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, X> && (!is_const_lit_v<T> || !is_const_lit_v<X>))
4715constexpr auto e_repl(A&& w, T&& p, X&& r) {
4716 str_src<K> pattern{std::forward<T>(p)};
4717 str_src<K> repl{std::forward<X>(r)};
4718 return expr_replaced<K>{get_str_src_from(std::forward<A>(w)), pattern, repl};
4719}
4720
4734template<StrSource A, typename K = src_str_t<A>, typename T, StrExprForType<K> E>
4735 requires std::is_constructible_v<str_src<K>, T>
4736constexpr auto e_repl(A&& w, T&& p, const E& expr) {
4737 str_src<K> pattern{std::forward<T>(p)};
4738 return expr_replaced_e<K, E>{get_str_src_from(std::forward<A>(w)), pattern, expr};
4739}
4740
4741template<bool UseVectorForReplace>
4742struct replace_search_result_store {
4743 size_t count_{};
4744 std::pair<size_t, size_t> replaces_[16];
4745};
4746
4747template<>
4748struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
4749
4750// Строковое выражение для замены символов
4751// String expression to replace characters
4752template<typename K, size_t N, bool UseVectorForReplace>
4753struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
4754 using symb_type = K;
4755 inline static const int BIT_SEARCH_TRESHHOLD = 4;
4756 const K pattern_[N];
4757 const str_src<K> source_;
4758 const str_src<K> replaces_[N];
4759
4760 mutable replace_search_result_store<UseVectorForReplace> search_results_;
4761
4762 [[_no_unique_address]]
4763 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
4764
4765 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
4766 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
4767
4768 size_t length() const {
4769 size_t l = source_.length();
4770 auto [fnd, num] = find_first_of(source_.str, source_.len);
4771 if (fnd == str::npos) {
4772 return l;
4773 }
4774 l += replaces_[num].len - 1;
4775 if constexpr (UseVectorForReplace) {
4776 search_results_.reserve((l >> 4) + 8);
4777 search_results_.emplace_back(fnd, num);
4778 for (size_t start = fnd + 1;;) {
4779 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
4780 if (fnd == str::npos) {
4781 break;
4782 }
4783 search_results_.emplace_back(fnd, idx);
4784 start = fnd + 1;
4785 l += replaces_[idx].len - 1;
4786 }
4787 } else {
4788 const size_t max_store = std::size(search_results_.replaces_);
4789 search_results_.replaces_[0] = {fnd, num};
4790 search_results_.count_++;
4791 for (size_t start = fnd + 1;;) {
4792 auto [found, idx] = find_first_of(source_.str, source_.len, start);
4793 if (found == str::npos) {
4794 break;
4795 }
4796 if (search_results_.count_ < max_store) {
4797 search_results_.replaces_[search_results_.count_] = {found, idx};
4798 }
4799 l += replaces_[idx].len - 1;
4800 search_results_.count_++;
4801 start = found + 1;
4802 }
4803 }
4804 return l;
4805 }
4806 K* place(K* ptr) const noexcept {
4807 size_t start = 0;
4808 const K* text = source_.str;
4809 if constexpr (UseVectorForReplace) {
4810 for (const auto& [pos, num] : search_results_) {
4811 size_t delta = pos - start;
4812 ch_traits<K>::copy(ptr, text + start, delta);
4813 ptr += delta;
4814 ptr = replaces_[num].place(ptr);
4815 start = pos + 1;
4816 }
4817 } else {
4818 const size_t max_store = std::size(search_results_.replaces_);
4819 size_t founded = search_results_.count_;
4820 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
4821 const auto [pos, num] = search_results_.replaces_[idx];
4822 size_t delta = pos - start;
4823 ch_traits<K>::copy(ptr, text + start, delta);
4824 ptr += delta;
4825 ptr = replaces_[num].place(ptr);
4826 start = pos + 1;
4827 }
4828 if (founded > max_store) {
4829 founded -= max_store;
4830 while (founded--) {
4831 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
4832 size_t delta = fnd - start;
4833 ch_traits<K>::copy(ptr, text + start, delta);
4834 ptr += delta;
4835 ptr = replaces_[idx].place(ptr);
4836 start = fnd + 1;
4837 }
4838 }
4839 }
4840 size_t tail = source_.len - start;
4841 ch_traits<K>::copy(ptr, text + start, tail);
4842 return ptr + tail;
4843 }
4844
4845protected:
4846 template<typename ... Repl>
4847 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
4848 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
4849
4850 template<typename ... Repl> requires (sizeof...(Repl) == N)
4851 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
4852 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
4853 {
4854 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
4855 for (size_t idx = 0; idx < N; idx++) {
4856 uu8s s = static_cast<uu8s>(pattern_[idx]);
4857 if constexpr (sizeof(K) == 1) {
4858 bit_mask_[s >> 3] |= 1 << (s & 7);
4859 } else {
4860 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
4861 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
4862 } else {
4863 bit_mask_[s >> 3] |= 1 << (s & 7);
4864 }
4865 }
4866 }
4867 }
4868 }
4869
4870 template<size_t Idx>
4871 size_t index_of(K s) const {
4872 if constexpr (Idx < N) {
4873 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
4874 }
4875 return -1;
4876 }
4877 bool is_in_mask(uu8s s) const {
4878 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
4879 }
4880 bool is_in_mask2(uu8s s) const {
4881 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
4882 }
4883
4884 bool is_in_pattern(K s, size_t& idx) const {
4885 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
4886 if constexpr (sizeof(K) == 1) {
4887 if (is_in_mask(s)) {
4888 idx = index_of<0>(s);
4889 return true;
4890 }
4891 } else {
4892 if (std::make_unsigned_t<const K>(s) > 255) {
4893 if (is_in_mask2(s)) {
4894 return (idx = index_of<0>(s)) != -1;
4895 }
4896 } else {
4897 if (is_in_mask(s)) {
4898 idx = index_of<0>(s);
4899 return true;
4900 }
4901 }
4902 }
4903 }
4904 return false;
4905 }
4906 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
4907 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
4908 size_t idx;
4909 while (offset < len) {
4910 if (is_in_pattern(text[offset], idx)) {
4911 return {offset, idx};
4912 }
4913 offset++;
4914 }
4915 } else {
4916 while (offset < len) {
4917 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
4918 return {offset, idx};
4919 }
4920 offset++;
4921 }
4922 }
4923 return {-1, -1};
4924 }
4925};
4926
4966template<bool UseVector = false, StrSource A, typename K = src_str_t<A>, typename ... Repl>
4967 requires (sizeof...(Repl) % 2 == 0)
4968auto e_repl_const_symbols(A&& src, Repl&& ... other) {
4969 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(get_str_src_from(std::forward<A>(src)), std::forward<Repl>(other)...);
4970}
4971
5011template<typename K, bool UseVectorForReplace = false>
5012struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
5013 using symb_type = K;
5014 using str_t = typename simple_str_selector<K>::type;
5015 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5016
5017 const str_src<K> source_;
5018 const std::vector<std::pair<K, str_t>>& replaces_;
5019
5020 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
5021
5022 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5023
5024 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
5052 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
5053 : source_(source), replaces_(repl)
5054 {
5055 size_t pattern_len = replaces_.size();
5056 pattern_.resize(pattern_len);
5057 K* pattern = pattern_.data();
5058
5059 for (size_t idx = 0; idx < replaces_.size(); idx++) {
5060 *pattern++ = replaces_[idx].first;
5061 }
5062
5063 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
5064 for (size_t idx = 0; idx < pattern_len; idx++) {
5065 uu8s s = static_cast<uu8s>(pattern_[idx]);
5066 if constexpr (sizeof(K) == 1) {
5067 bit_mask_[s >> 3] |= (1 << (s & 7));
5068 } else {
5069 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
5070 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
5071 } else {
5072 bit_mask_[s >> 3] |= (1 << (s & 7));
5073 }
5074 }
5075 }
5076 }
5077 }
5078
5079 size_t length() const {
5080 size_t l = source_.length();
5081 auto [fnd, num] = find_first_of(source_.str, source_.len);
5082 if (fnd == str::npos) {
5083 return l;
5084 }
5085 l += replaces_[num].second.len - 1;
5086 if constexpr (UseVectorForReplace) {
5087 search_results_.reserve((l >> 4) + 8);
5088 search_results_.emplace_back(fnd, num);
5089 for (size_t start = fnd + 1;;) {
5090 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5091 if (fnd == str::npos) {
5092 break;
5093 }
5094 search_results_.emplace_back(fnd, idx);
5095 start = fnd + 1;
5096 l += replaces_[idx].second.len - 1;
5097 }
5098 } else {
5099 const size_t max_store = std::size(search_results_.replaces_);
5100 search_results_.replaces_[0] = {fnd, num};
5101 search_results_.count_++;
5102 for (size_t start = fnd + 1;;) {
5103 auto [found, idx] = find_first_of(source_.str, source_.len, start);
5104 if (found == str::npos) {
5105 break;
5106 }
5107 if (search_results_.count_ < max_store) {
5108 search_results_.replaces_[search_results_.count_] = {found, idx};
5109 }
5110 l += replaces_[idx].second.len - 1;
5111 search_results_.count_++;
5112 start = found + 1;
5113 }
5114 }
5115 return l;
5116 }
5117 K* place(K* ptr) const noexcept {
5118 size_t start = 0;
5119 const K* text = source_.str;
5120 if constexpr (UseVectorForReplace) {
5121 for (const auto& [pos, num] : search_results_) {
5122 size_t delta = pos - start;
5123 ch_traits<K>::copy(ptr, text + start, delta);
5124 ptr += delta;
5125 ptr = replaces_[num].second.place(ptr);
5126 start = pos + 1;
5127 }
5128 } else {
5129 const size_t max_store = std::size(search_results_.replaces_);
5130 size_t founded = search_results_.count_;
5131 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
5132 const auto [pos, num] = search_results_.replaces_[idx];
5133 size_t delta = pos - start;
5134 ch_traits<K>::copy(ptr, text + start, delta);
5135 ptr += delta;
5136 ptr = replaces_[num].second.place(ptr);
5137 start = pos + 1;
5138 }
5139 if (founded > max_store) {
5140 founded -= max_store;
5141 while (founded--) {
5142 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5143 size_t delta = fnd - start;
5144 ch_traits<K>::copy(ptr, text + start, delta);
5145 ptr += delta;
5146 ptr = replaces_[idx].second.place(ptr);
5147 start = fnd + 1;
5148 }
5149 }
5150 }
5151 size_t tail = source_.len - start;
5152 ch_traits<K>::copy(ptr, text + start, tail);
5153 return ptr + tail;
5154 }
5155
5156protected:
5157 size_t index_of(K s) const {
5158 return pattern_.find(s);
5159 }
5160
5161 bool is_in_mask(uu8s s) const {
5162 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
5163 }
5164 bool is_in_mask2(uu8s s) const {
5165 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
5166 }
5167
5168 bool is_in_pattern(K s, size_t& idx) const {
5169 if constexpr (sizeof(K) == 1) {
5170 if (is_in_mask(s)) {
5171 idx = index_of(s);
5172 return true;
5173 }
5174 } else {
5175 if (std::make_unsigned_t<const K>(s) > 255) {
5176 if (is_in_mask2(s)) {
5177 return (idx = index_of(s)) != -1;
5178 }
5179 } else {
5180 if (is_in_mask(s)) {
5181 idx = index_of(s);
5182 return true;
5183 }
5184 }
5185 }
5186 return false;
5187 }
5188
5189 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
5190 size_t pl = pattern_.length();
5191 if (pl >= BIT_SEARCH_TRESHHOLD) {
5192 size_t idx;
5193 while (offset < len) {
5194 if (is_in_pattern(text[offset], idx)) {
5195 return {offset, idx};
5196 }
5197 offset++;
5198 }
5199 } else {
5200 while (offset < len) {
5201 if (size_t idx = index_of(text[offset]); idx != -1) {
5202 return {offset, idx};
5203 }
5204 offset++;
5205 }
5206 }
5207 return {-1, -1};
5208 }
5209};
5210
5215namespace str {
5216
5249template<typename K, typename A, StrExprForType<K> E>
5250std::basic_string<K, std::char_traits<K>, A>& change(std::basic_string<K, std::char_traits<K>, A>& str, size_t from, size_t count, const E& expr) {
5251 size_t expr_length = expr.length();
5252 if (!expr_length) {
5253 str.erase(from, count);
5254 return str;
5255 }
5256 size_t str_length = str.length();
5257 if (from > str_length) {
5258 from = str_length;
5259 }
5260 if (from + count > str_length) {
5261 count = str_length - from;
5262 }
5263 size_t new_length = str_length - count + expr_length;
5264 size_t tail_length = str_length - count - from;
5265
5266 if (new_length <= str_length) {
5267 K* data = str.data();
5268 expr.place((typename E::symb_type*)data + from);
5269 if (expr_length < count) {
5270 if (tail_length) {
5271 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
5272 }
5273 str.resize(new_length);
5274 }
5275 } else {
5276 auto fill = [&](K* data, size_t) -> size_t {
5277 if (tail_length) {
5278 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
5279 }
5280 expr.place((typename E::symb_type*)data + from);
5281 return new_length;
5282 };
5283 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
5284 str.resize_and_overwrite(new_length, fill);
5285 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
5286 str._Resize_and_overwrite(new_length, fill);
5287 } else {
5288 str.resize(new_length);
5289 fill(str.data(), 0);
5290 }
5291 }
5292 return str;
5293}
5294
5322template<typename K, typename A, StrExprForType<K> E>
5323std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
5324 return change(str, str.length(), 0, expr);
5325}
5326
5355template<typename K, typename A, StrExprForType<K> E>
5356std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
5357 return change(str, 0, 0, expr);
5358}
5359
5390template<typename K, typename A, StrExprForType<K> E>
5391std::basic_string<K, std::char_traits<K>, A>& insert(std::basic_string<K, std::char_traits<K>, A>& str, size_t from, const E& expr) {
5392 return change(str, from, 0, expr);
5393}
5394
5395namespace details {
5396
5397template<typename K, typename A, typename E>
5398struct replace_grow_helper {
5399 using my_type = std::basic_string<K, std::char_traits<K>, A>;
5400
5401 replace_grow_helper(my_type& src, str_src<K> p, const K* r, size_t rl, size_t mc, size_t d, const E& e)
5402 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
5403 my_type& str;
5404
5405 const str_src<K> source;
5406 const str_src<K> pattern;
5407 K* repl;
5408 const size_t replLen;
5409
5410 size_t maxCount;
5411 const size_t delta;
5412 size_t all_delta{};
5413 const E& expr;
5414
5415 K* reserve_for_copy{};
5416 size_t end_of_piece{};
5417 size_t total_length{};
5418
5419 std::optional<my_type> dst;
5420
5421 void replace(size_t offset) {
5422 size_t found[16] = {offset};
5423 maxCount--;
5424
5425 offset += pattern.len;
5426 all_delta += delta;
5427 size_t idx = 1;
5428 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
5429 found[idx] = source.find(pattern, offset);
5430 if (found[idx] == npos) {
5431 break;
5432 }
5433 offset = found[idx] + pattern.len;
5434 all_delta += delta;
5435 }
5436 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
5437 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
5438 }
5439 // Теперь делаем свои замены
5440 // Now we make our replacements
5441 if (!reserve_for_copy) {
5442 // Только начинаем
5443 // Just getting started
5444 end_of_piece = source.length();
5445 total_length = end_of_piece + all_delta;
5446 my_type* dst_str{};
5447 if (total_length <= str.capacity()) {
5448 // Строка поместится в старое место | The line will be placed in the old location.
5449 dst_str = &str;
5450 } else {
5451 // Будем создавать в другом буфере | We will create in another buffer.
5452 dst_str = &dst.emplace();
5453 }
5454 auto fill = [this](K* p, size_t) -> size_t {
5455 reserve_for_copy = p;
5456 return total_length;
5457 };
5458 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
5459 dst_str->_Resize_and_overwrite(total_length, fill);
5460 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
5461 dst_str->resize_and_overwrite(total_length, fill);
5462 } else {
5463 dst_str->resize(total_length);
5464 reserve_for_copy = dst_str->data();
5465 }
5466 }
5467 K* dst_start = reserve_for_copy;
5468 const K* src_start = str.c_str();
5469 while(idx-- > 0) {
5470 size_t pos = found[idx] + pattern.len;
5471 size_t lenOfPiece = end_of_piece - pos;
5472 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
5473 if constexpr (std::is_same_v<E, int>) {
5474 ch_traits<K>::copy(dst_start + pos + all_delta - replLen, repl, replLen);
5475 } else {
5476 if (!repl) {
5477 repl = dst_start + pos + all_delta - replLen;
5478 expr.place(repl);
5479 } else {
5480 ch_traits<K>::copy(dst_start + pos + all_delta - replLen, repl, replLen);
5481 }
5482 }
5483 all_delta -= delta;
5484 end_of_piece = found[idx];
5485 }
5486 if (!all_delta && reserve_for_copy != src_start) {
5487 ch_traits<K>::copy(dst_start, src_start, found[0]);
5488 str = std::move(*dst);
5489 }
5490 }
5491};
5492
5493} // namespace details
5494
5529template<typename K, typename A, StrExprForType<K> E, typename T>
5530requires (std::is_constructible_v<str_src<K>, T>)
5531std::basic_string<K, std::char_traits<K>, A>& replace(std::basic_string<K, std::char_traits<K>, A>& str, T&& pattern, const E& repl, size_t offset = 0, size_t max_count = -1) {
5532 if (!max_count) {
5533 return str;
5534 }
5535 str_src<K> src = str;
5536 str_src<K> spattern{std::forward<T>(pattern)};
5537 offset = src.find(pattern, offset);
5538 if (offset == npos) {
5539 return str;
5540 }
5541 size_t replLen = repl.length();
5542 K* replStart{};
5543 if (spattern.len == replLen) {
5544 // Заменяем inplace на подстроку такой же длины
5545 // Replace inplace with a substring of the same length
5546 K* ptr = str.data();
5547 replStart = ptr + offset;
5548 repl.place(replStart);
5549
5550 while (--max_count) {
5551 offset = src.find(spattern, offset + replLen);
5552 if (offset == npos)
5553 break;
5554 ch_traits<K>::copy(ptr + offset, replStart, replLen);
5555 }
5556 } else if (spattern.len > replLen) {
5557 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
5558 // Replace with a shorter piece, the length of the text will decrease, go from left to right
5559 K* ptr = str.data();
5560 replStart = ptr + offset;
5561 repl.place(replStart);
5562 size_t posWrite = offset + replLen;
5563 offset += spattern.len;
5564
5565 while (--max_count) {
5566 size_t idx = src.find(spattern, offset);
5567 if (idx == npos)
5568 break;
5569 size_t lenOfPiece = idx - offset;
5570 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
5571 posWrite += lenOfPiece;
5572 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
5573 posWrite += replLen;
5574 offset = idx + spattern.len;
5575 }
5576 size_t tailLen = src.len - offset;
5577 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
5578 str.resize(posWrite + tailLen);
5579 } else {
5580 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
5581 }
5582 return str;
5583}
5584
5605template<typename K, typename A>
5606std::basic_string<K, std::char_traits<K>, A>& replace(std::basic_string<K, std::char_traits<K>, A>& str, str_src<K> pattern, str_src<K> repl, size_t offset = 0, size_t max_count = -1) {
5607 if (!max_count) {
5608 return str;
5609 }
5610 str_src<K> src = str;
5611 offset = src.find(pattern, offset);
5612 if (offset == npos) {
5613 return str;
5614 }
5615 if (pattern.len == repl.len) {
5616 // Заменяем inplace на подстроку такой же длины
5617 // Replace inplace with a substring of the same length
5618 K* ptr = str.data();
5619 while (max_count--) {
5620 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
5621 offset = src.find(pattern, offset + repl.len);
5622 if (offset == npos)
5623 break;
5624 }
5625 } else if (pattern.len > repl.len) {
5626 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
5627 // Replace with a shorter piece, the length of the text will decrease, go from left to right
5628 K* ptr = str.data();
5629 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
5630 size_t posWrite = offset + repl.len;
5631 offset += pattern.len;
5632
5633 while (--max_count) {
5634 size_t idx = src.find(pattern, offset);
5635 if (idx == npos)
5636 break;
5637 size_t lenOfPiece = idx - offset;
5638 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
5639 posWrite += lenOfPiece;
5640 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
5641 posWrite += repl.len;
5642 offset = idx + pattern.len;
5643 }
5644 size_t tailLen = src.len - offset;
5645 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
5646 str.resize(posWrite + tailLen);
5647 } else {
5648 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
5649 }
5650 return str;
5651}
5652
5653template<typename K, typename A, typename T, typename M>
5654requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
5655std::basic_string<K, std::char_traits<K>, A>& replace(std::basic_string<K, std::char_traits<K>, A>& str, T&& pattern, M&& repl, size_t offset = 0, size_t max_count = -1) {
5656 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
5657}
5658
5659} // namespace str
5660
5661} // namespace simstr
5662
5663namespace std {
5683template<simstr::StdStrSource T>
5685 return {str};
5686}
5687
5716template<typename K, typename A, simstr::StrExprForType<K> E>
5717std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
5718 return simstr::str::change(str, str.length(), 0, expr);
5719}
5720
5749template<typename K, typename A, simstr::StrExprForType<K> E>
5750std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
5751 return simstr::str::change(str, 0, 0, expr);
5752}
5753
5754} // namespace std
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:2351
constexpr bool is_done() const
Find out if substrings are running out.
Definition strexpr.h:2362
constexpr str_t next()
Get the next substring.
Definition strexpr.h:2371
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition strexpr.h:3297
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Find the last occurrence of a character in this string.
Definition strexpr.h:3077
R trimmed_right() const
Get a string with whitespace removed on the right.
Definition strexpr.h:3671
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition strexpr.h:2759
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition strexpr.h:2644
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition strexpr.h:2981
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition strexpr.h:3509
R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition strexpr.h:3793
R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition strexpr.h:3807
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition strexpr.h:2695
constexpr std::basic_string_view< D > to_sv() const noexcept
Convert to std::basic_string_view.
Definition strexpr.h:2491
R trimmed_left() const
Get a string with whitespace removed on the left.
Definition strexpr.h:3659
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the end of the first occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:2877
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition strexpr.h:3451
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition strexpr.h:2684
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition strexpr.h:2469
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition strexpr.h:2833
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:3223
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition strexpr.h:2782
constexpr T split(str_piece delimiter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition strexpr.h:3436
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition strexpr.h:2584
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Get a substring. Works similarly to operator(), only the result is the same type as the method applie...
Definition strexpr.h:3178
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition strexpr.h:3578
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:2539
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Get the substring str_src from position from to position to (not including it).
Definition strexpr.h:2606
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Get a copy of the string with occurrences of substrings replaced.
Definition strexpr.h:3610
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition strexpr.h:2655
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Whether the string ends with the specified substring in a case-insensitive ASCII character.
Definition strexpr.h:3535
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character not from the given character set.
Definition strexpr.h:3124
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:3096
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string or the end of the string.
Definition strexpr.h:2940
R trimmed_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:3839
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Whether the string begins with the given substring in a case-insensitive ASCII character.
Definition strexpr.h:3476
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string.
Definition strexpr.h:2913
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:3137
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Find a character in this string or the end of a string.
Definition strexpr.h:3000
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character not from the given character set.
Definition strexpr.h:3165
constexpr void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Call a functor on all found occurrences of a substring in this string.
Definition strexpr.h:3035
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Find the end of the occurrence of a substring in this string.
Definition strexpr.h:2849
constexpr bool operator!() const noexcept
Check for emptiness.
Definition strexpr.h:2613
R trimmed(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left and right.
Definition strexpr.h:3686
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition strexpr.h:3647
constexpr size_t size() const
The size of the string in characters.
Definition strexpr.h:2480
R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition strexpr.h:3821
constexpr To find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Find all occurrences of a substring in this string.
Definition strexpr.h:3058
constexpr my_type str_mid(size_t from, size_t len=-1) const
Get part of a string with an object of the same type to which the method is applied,...
Definition strexpr.h:3191
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string.
Definition strexpr.h:2926
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:3109
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition strexpr.h:3542
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string.
Definition strexpr.h:2813
constexpr SplitterBase< K, str_piece > splitter(str_piece delimiter) const
Retrieve a Splitter object by the given splitter, which allows sequential get substrings using the ne...
Definition strexpr.h:3889
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:3150
R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition strexpr.h:3701
R trimmed_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:3738
std::optional< double > to_double() const noexcept
Convert string to double.
Definition strexpr.h:3266
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string or the end of the string.
Definition strexpr.h:2863
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition strexpr.h:2714
R trimmed_right_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:3875
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition strexpr.h:2704
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition strexpr.h:2968
R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition strexpr.h:3716
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:2565
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition strexpr.h:2626
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition strexpr.h:3590
R trimmed_right_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:3776
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition strexpr.h:2724
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Convert to std::basic_string.
Definition strexpr.h:2511
constexpr T splitf(str_piece delimiter, const Op &beforeFunc, size_t offset=0) const
Split a string into parts at a given delimiter, possibly applying a functor to each substring.
Definition strexpr.h:3420
R trimmed_left_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:3757
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:2954
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition strexpr.h:3494
R trimmed_left_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:3857
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition strexpr.h:2454
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:3256
constexpr bool equal_ia(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
Definition strexpr.h:2771
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:3323
The concept of a string expression compatible with a given character type.
Definition strexpr.h:418
Concept of "String Expressions".
Definition strexpr.h:400
Base concept of string object.
Definition strexpr.h:264
constexpr expr_if< A > e_if(bool c, const A &a)
Creating a conditional string expression expr_if.
Definition strexpr.h:1318
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Unary operator + for converting standard strings to string expressions.
Definition strexpr.h:5684
constexpr expr_spaces< uws, N > e_spcw()
Generates a string of N wchar_t spaces.
Definition strexpr.h:878
expr_fill< K, A, true > e_fill_left(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:1816
constexpr empty_expr< uws > eew
Empty string expression of type wchar_t.
Definition strexpr.h:636
constexpr expr_spaces< u8s, N > e_spca()
Generates a string of N char spaces.
Definition strexpr.h:860
constexpr empty_expr< u32s > eeuu
Empty string expression of type char32_t.
Definition strexpr.h:648
constexpr expr_num< K, T > e_num(T t)
Convert an integer to a string expression.
Definition strexpr.h:1527
constexpr bool is_equal_str_type_v
Checks whether two types are compatible string types.
Definition strexpr.h:136
HexFlags
Flags for the e_hex function.
Definition strexpr.h:1680
constexpr auto e_hex(T v)
Allows you to concatenate text and a unsigned number in hexadecimal.
Definition strexpr.h:1704
constexpr strexprjoin< A, B > operator+(const A &a, const B &b)
An addition operator for two arbitrary string expressions of the same character type.
Definition strexpr.h:529
constexpr expr_repeat_lit< K, M - 1 > e_repeat(T &&s, size_t l)
Generate a string from l string constants s of type K.
Definition strexpr.h:992
constexpr expr_literal< typename const_lit< T >::symb_type, static_cast< size_t >(N - 1)> e_t(T &&s)
Converts a string literal to a string expression.
Definition strexpr.h:765
expr_fill< K, A, false > e_fill_right(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:1842
constexpr expr_choice< A, B > e_choice(bool c, const A &a, const B &b)
Create a conditional string expression expr_choice.
Definition strexpr.h:1240
constexpr expr_char< K > e_char(K s)
Generates a string of 1 given character.
Definition strexpr.h:692
constexpr empty_expr< u8s > eea
Empty string expression of type char.
Definition strexpr.h:624
constexpr expr_pad< K > e_c(size_t l, K s)
Generates a string of l characters s of type K.
Definition strexpr.h:923
constexpr empty_expr< u16s > eeu
Empty string expression of type char16_t.
Definition strexpr.h:642
auto e_repl_const_symbols(A &&src, Repl &&... other)
Returns a string expression that generates a string containing the given characters replaced with giv...
Definition strexpr.h:4968
constexpr empty_expr< u8s > eeb
Empty string expression of type char8_t.
Definition strexpr.h:630
constexpr auto e_repl(A &&w, T &&p, X &&r)
Get a string expression that generates a string with all occurrences of a given substring replaced.
Definition strexpr.h:4474
constexpr auto e_join(const T &s, L &&d)
Get a string expression concatenating the strings in the container into a single string with the give...
Definition strexpr.h:1920
@ Short
without leading zeroes
Definition strexpr.h:1681
Small namespace for standard string methods.
Definition strexpr.h:1393
std::basic_string< K, std::char_traits< K >, A > & append(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Append a string expression to a standard string.
Definition strexpr.h:5323
std::basic_string< K, std::char_traits< K >, A > & prepend(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Insert a string expression at the beginning of a standard string.
Definition strexpr.h:5356
std::basic_string< K, std::char_traits< K >, A > & change(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, size_t count, const E &expr)
Replace a portion of a standard string with the given string expression.
Definition strexpr.h:5250
std::basic_string< K, std::char_traits< K >, A > & replace(std::basic_string< K, std::char_traits< K >, A > &str, T &&pattern, const E &repl, size_t offset=0, size_t max_count=-1)
A function for searching for substrings in a standard string and replacing the found occurrences with...
Definition strexpr.h:5531
std::basic_string< K, std::char_traits< K >, A > & insert(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, const E &expr)
Insert a string expression at the specified position in a standard string.
Definition strexpr.h:5391
Library namespace.
Definition sstring.cpp:12
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition strexpr.h:1987
@ Overflow
Переполнение, число не помещается в заданный тип
Definition strexpr.h:1990
@ Success
Успешно
Definition strexpr.h:1988
@ NotNumber
Вообще не число
Definition strexpr.h:1991
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition strexpr.h:1989
An "empty" string expression.
Definition strexpr.h:609
Conditional selection string expression.
Definition strexpr.h:1119
Conditional selection string expression.
Definition strexpr.h:1185
Conditional selection string expression.
Definition strexpr.h:1028
Conditional selection string expression.
Definition strexpr.h:1057
A type of string expression that returns N specified characters.
Definition strexpr.h:894
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Expression constructor.
Definition strexpr.h:5052
A string expression that generates a string replacing all occurrences of the given substring to strin...
Definition strexpr.h:4592
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Constructor.
Definition strexpr.h:4611
A string expression that generates a string replacing all occurrences of the given substring to anoth...
Definition strexpr.h:4486
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Constructor.
Definition strexpr.h:4504
A type of string expression that returns N specified characters.
Definition strexpr.h:834
A type for using std::string and std::string_view as sources in string expressions.
Definition strexpr.h:1357
Base class for converting string expressions to standard strings.
Definition strexpr.h:468
A class that claims to refer to a null-terminated string.
Definition strexpr.h:4060
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition strexpr.h:4123
constexpr str_src_nt(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:4094
constexpr str_src_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition strexpr.h:4085
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:4112
constexpr str_src_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:4100
The simplest class of an immutable non-owning string.
Definition strexpr.h:3924
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:3953
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition strexpr.h:3978
constexpr size_t length() const noexcept
Get the length of the string.
Definition strexpr.h:3964
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition strexpr.h:4007
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a line by the specified number of characters.
Definition strexpr.h:4018
constexpr str_src(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:3943
constexpr bool is_same(str_src< K > other) const noexcept
Check if two objects point to the same string.
Definition strexpr.h:3987
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition strexpr.h:3958
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:3971
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition strexpr.h:4031
constexpr bool is_part_of(str_src< K > other) const noexcept
Check if a string is part of another string.
Definition strexpr.h:3996
constexpr str_src(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:3937
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:554
Template class for concatenating two string expressions into one using operator +
Definition strexpr.h:492