simstr 1.7.3
Yet another strings library
 
Loading...
Searching...
No Matches
strexpr.h
1/*
2 * ver. 1.7.3
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) == sizeof(char16_t);
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>
92concept is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
93
94template<typename K>
95concept is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
96
97template<is_one_of_std_char_v From>
98auto to_one_of_std_char(From* from) {
99 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
100 return from;
101 } else if constexpr (std::is_same_v<From, ubs>) {
102 return reinterpret_cast<u8s*>(from);
103 } else {
104 return from_w(from);
105 }
106}
107
108template<is_one_of_std_char_v From>
109auto to_one_of_std_char(const From* from) {
110 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
111 return from;
112 } else if constexpr (std::is_same_v<From, ubs>) {
113 return reinterpret_cast<const u8s*>(from);
114 } else {
115 return from_w(from);
116 }
117}
118
119template<typename K>
120struct to_std_char_type : std::type_identity<K>{};
121
122template<>
123struct to_std_char_type<char8_t>{
124 using type = char;
125};
126
127template<>
128struct to_std_char_type<char16_t>{
129 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
130};
131
132template<>
133struct to_std_char_type<char32_t>{
134 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
135};
136
137template<typename K>
138using to_std_char_t = typename to_std_char_type<K>::type;
139
140template<typename K>
141struct to_base_char_type : std::type_identity<K>{};
142
143template<>
144struct to_base_char_type<char8_t>{
145 using type = char;
146};
147
148template<>
149struct to_base_char_type<wchar_t>{
150 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), char16_t, char32_t>;
151};
152
153template<typename K>
154using to_base_char_t = typename to_base_char_type<K>::type;
155
171template<typename K1, typename K2>
172concept is_equal_str_type_v = sizeof(K1) == sizeof(K2) && is_one_of_char_v<K1> && is_one_of_char_v<K2>;
173
174/*
175Вспомогательные шаблоны для определения строковых литералов.
176Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
177Если пишем
178template<size_t N>
179void func(const char(&lit)[N]);
180то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
181
182// Выделили место под символы
183char buf[100];
184// Как то наполнили буфер, допустим, до половины.
185
186stringa text = buf;
187Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
188для строкового литерала, в text запишется просто указатель на buf и длина 100.
189
190Поэтому такие параметры объявляем как
191template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
192void func(T&& lit);
193
194Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
195и выражение с параметром char[100] - не скомпилируется.
196
197Helper templates for defining string literals.
198They are used to limit types in function parameters strictly as `const K(&)[N]`
199If we write
200template<size_t N>
201void func(const char(&lit)[N]);
202then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
203
204// Allocate space for symbols
205char buf[100];
206// Somehow the buffer was filled, say, to half.
207
208stringa text = buf;
209Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
210for a string literal, text will simply contain a pointer to buf and length 100.
211
212Therefore, we declare such parameters as
213template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
214void func(T&& lit);
215
216Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
217and an expression with the char[100] parameter will not compile.
218*/
219
220template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
221// Для правильных типов параметров есть определение, в виде специализации шаблона
222// There is a definition for the correct parameter types, in the form of a template specialization
223template<is_one_of_char_v T, size_t N>
224struct const_lit<const T(&)[N]> {
225 using symb_type = T;
226 constexpr static size_t Count = N;
227};
228
229template<typename T>
230concept is_const_lit_v = requires {
231 typename const_lit<T>::symb_type;
232};
233
234// Тут ещё дополнительно ограничиваем тип литерала
235// Here we further restrict the type of the literal
236template<typename K, typename T> struct const_lit_for;
237
238template<typename K, is_equal_str_type_v<K> P, size_t N>
239struct const_lit_for<K, const P(&)[N]> {
240 constexpr static size_t Count = N;
241};
242
243template<typename K, size_t N>
244class const_lit_to_array {
245
246 template<size_t Idx>
247 constexpr size_t find(K s) const {
248 if constexpr (Idx < N) {
249 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
250 }
251 return -1;
252 }
253
254 template<size_t Idx>
255 constexpr bool exist(K s) const {
256 if constexpr (Idx < N) {
257 return s == symbols_[Idx] || exist<Idx + 1>(s);
258 }
259 return false;
260 }
261public:
262 const K (&symbols_)[N + 1];
263
264 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
265 constexpr const_lit_to_array(T&& s)
266 : symbols_((const K(&)[M])s) {}
267
268 constexpr bool contain(K s) const {
269 return exist<0>(s);
270 }
271 constexpr size_t index_of(K s) const {
272 return find<0>(s);
273 }
274};
275
276template<typename A>
277concept StrTypeCommon = requires(const A& a) {
278 typename std::remove_cvref_t<A>::symb_type;
279 { a.is_empty() } -> std::same_as<bool>;
280 { a.length() } -> std::convertible_to<size_t>;
281 { a.symbols() };
282};
283
284template<typename A>
285concept HasSymbType = requires {
286 typename std::remove_cvref_t<A>::symb_type;
287};
288
310template<typename A, typename K>
311concept StrType = StrTypeCommon<A> && requires(const A& a) {
312 { a.symbols() } -> std::same_as<const K*>;
313} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
314
315template<typename T> struct is_std_string_source : std::false_type{};
316
317template<typename K, typename A>
318struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
319
320template<typename K>
321struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
322
323template<typename T>
324concept is_std_string_source_v = is_std_string_source<T>::value;
325
326template<typename T>
327concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
328
329template<typename T, typename K>
330concept StdStrSourceForType = StdStrSource<T> && is_equal_str_type_v<K, typename T::value_type>;
331
332template<typename T>
333concept StrSource = StdStrSource<T> || is_const_lit_v<T> || StrTypeCommon<T>;
334
335template<typename T>
336concept StrSourceNoLiteral = StdStrSource<T> || StrTypeCommon<T>;
337
338template<typename T> struct is_std_string : std::false_type{};
339
340template<typename K, typename A>
341struct is_std_string<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
342
343template<typename T>
344concept is_std_string_v = is_std_string<T>::value;
345template<typename T, typename K>
346concept StdStringForType = is_std_string_v<T> && is_equal_str_type_v<K, typename T::value_type>;
347
348template<typename T>
349struct symb_type_from_src {
350 using type = void;
351
352};
353
354template<typename K, size_t N>
355struct symb_type_from_src<const K(&)[N]> {
356 using type = K;
357};
358
359template<StdStrSource T>
360struct symb_type_from_src<T> {
361 using type = typename std::remove_cvref_t<T>::value_type;
362};
363
364template<HasSymbType T>
365struct symb_type_from_src<T> {
366 using type = typename std::remove_cvref_t<T>::symb_type;
367};
368
369template<typename T>
370using symb_type_from_src_t = symb_type_from_src<T>::type;
371
372
498
506template<typename A>
507concept StrExpr = requires(const A& a) {
508 typename std::remove_cvref_t<A>::symb_type;
509 { a.length() } -> std::convertible_to<size_t>;
510 { a.place(std::declval<typename std::remove_cvref_t<A>::symb_type*>()) } -> std::same_as<typename std::remove_cvref_t<A>::symb_type*>;
511};
512
524template<typename A, typename K>
526
527template<typename K, typename T>
528struct convert_to_strexpr;
529
530template<typename A, typename K>
531concept strexpr_from = requires(const A& a) {
532 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StrExprForType<K>;
533};
534
535template<typename A, typename K>
536concept strexpr_std = requires(const A& a) {
537 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StdStringForType<K>;
538};
539
540template<typename A, typename K>
542
543template<typename A, typename K>
545
546template<typename A, typename K>
547concept to_strexpr_meth = requires(const A& a) {
548 {a.template to_strexpr<K>()} -> StrExprForType<K>;
549};
550
551template<typename A, typename K>
552concept to_strexpr_std = requires(const A& a) {
553 {a.template to_strexpr<K>()} -> StdStringForType<K>;
554};
555
556template<typename A, typename K>
557concept convertible_to_strexpr = strexpr_for<A, K> || strexpr_from<A, K> || strexpr_std<A, K> || to_strexpr_type<A, K> || to_strexpr_meth<A, K> || to_strexpr_std<A, K>;
558
559template<typename K, strexpr_from<K> T>
560constexpr auto to_strexpr(T&& t) {
561 return convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t));
562}
563
564template<typename K, strexpr_for<K> T>
565constexpr typename convert_to_strexpr<K, std::remove_cvref_t<T>>::type to_strexpr(T&& t) {
566 return {std::forward<T>(t)};
567}
568
569template<typename K, to_strexpr_type<K> T>
570constexpr typename std::remove_cvref_t<T>::strexpr to_strexpr(T&& t) {
571 return {std::forward<T>(t)};
572}
573
574template<typename K, to_strexpr_meth<K> T>
575constexpr auto to_strexpr(T&& t) {
576 return t.template to_strexpr<K>();
577}
578
579template<typename K, StdStrSource T> requires is_equal_str_type_v<K, typename T::value_type>
580struct expr_stdstr_c {
581 using symb_type = K;
582 T t_;
583
584 expr_stdstr_c(T t) : t_(std::move(t)){}
585
586 constexpr size_t length() const noexcept {
587 return t_.length();
588 }
589 constexpr symb_type* place(symb_type* p) const noexcept {
590 size_t s = t_.size();
591 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
592 return p + s;
593 }
594};
595
596template<typename K, strexpr_std<K> T>
597constexpr auto to_strexpr(T&& t) {
598 using type = decltype(convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t)));
599 return expr_stdstr_c<K, type>{convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t))};
600}
601
602template<typename K, to_strexpr_std<K> T>
603constexpr auto to_strexpr(T&& t) {
604 using type = decltype(t.template to_strexpr<K>());
605 return expr_stdstr_c<K, type>{t.template to_strexpr<K>()};
606}
607
608template<typename K, typename T>
609using convert_to_strexpr_t = decltype(to_strexpr<K>(std::declval<T>()));
610
611/*
612* Шаблонные классы для создания строковых выражений из нескольких источников.
613* Благодаря компиляторно-шаблонной "магии" позволяют максимально эффективно
614* получать результирующую строку - сначала вычисляется длина результирующей строки,
615* потом один раз выделяется память для результата, после символы помещаются в
616* выделенную память.
617* Для конкатенация двух объектов строковых выражений в один
618
619* Template classes for creating string expressions from multiple sources.
620* Thanks to compiler-template "magic" they allow you to maximize efficiency
621* get the resulting string - first the length of the resulting string is calculated,
622* then memory is allocated once for the result, after which the characters are placed in
623* allocated memory.
624* For concatenating two string expression objects into one.
625*/
626
627template<typename K, typename Allocator, StrExpr A>
628constexpr std::basic_string<K, std::char_traits<K>, Allocator> to_std_string(const A& expr) {
629 std::basic_string<K, std::char_traits<K>, Allocator> res;
630 if (size_t l = expr.length()) {
631 auto fill = [&](K* ptr, size_t size) -> size_t {
632 expr.place((typename A::symb_type*)ptr);
633 return l;
634 };
635 if constexpr (requires { res.resize_and_overwrite(l, fill); }) {
636 res.resize_and_overwrite(l, fill);
637 } else if constexpr (requires{ res._Resize_and_overwrite(l, fill); }) {
638 // Work in MSVC std lib before C++23
639 res._Resize_and_overwrite(l, fill);
640 } else {
641 res.resize(l); // bad, fill by 0 first.
642 expr.place((typename A::symb_type*)res.data());
643 }
644 }
645 return res;
646}
647
658template<typename Impl>
660 template<is_equal_str_type_v<typename Impl::symb_type> P, typename Allocator>
661 constexpr operator std::basic_string<P, std::char_traits<P>, Allocator>() const {
662 return to_std_string<P, Allocator>(*static_cast<const Impl*>(this));
663 }
664};
665
681template<StrExpr A, StrExprForType<typename A::symb_type> B>
682struct strexprjoin : expr_to_std_string<strexprjoin<A, B>>{
683 using symb_type = typename A::symb_type;
684 const A& a;
685 const B& b;
686 constexpr strexprjoin(const A& a_, const B& b_) : a(a_), b(b_){}
687 constexpr size_t length() const noexcept {
688 return a.length() + b.length();
689 }
690 constexpr symb_type* place(symb_type* p) const noexcept {
691 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
692 }
693};
694
718template<StrExpr A, StrExprForType<typename A::symb_type> B>
719constexpr strexprjoin<A, B> operator+(const A& a, const B& b) {
720 return {a, b};
721}
722
743template<StrExpr A, StrExprForType<typename A::symb_type> B, bool last = true>
744struct strexprjoin_c : expr_to_std_string<strexprjoin_c<A, B, last>>{
745 using symb_type = typename A::symb_type;
746 const A& a;
747 B b;
748 template<typename... Args>
749 constexpr strexprjoin_c(const A& a_, Args&&... args_) : a(a_), b(std::forward<Args>(args_)...) {}
750 constexpr size_t length() const noexcept {
751 return a.length() + b.length();
752 }
753 constexpr symb_type* place(symb_type* p) const noexcept {
754 if constexpr (last) {
755 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
756 } else {
757 return a.place((symb_type*)b.place((typename B::symb_type*)p));
758 }
759 }
760};
761
776template<StrExpr A, convertible_to_strexpr<typename A::symb_type> B>
778 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
779}
780
795template<StrExpr A, convertible_to_strexpr<typename A::symb_type> B>
797 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
798}
799
834template<typename K>
835struct empty_expr : expr_to_std_string<empty_expr<K>>{
836 using symb_type = K;
837 constexpr size_t length() const noexcept {
838 return 0;
839 }
840 constexpr symb_type* place(symb_type* p) const noexcept {
841 return p;
842 }
843};
844
850inline constexpr empty_expr<u8s> eea{};
856inline constexpr empty_expr<u8s> eeb{};
862inline constexpr empty_expr<uws> eew{};
868inline constexpr empty_expr<u16s> eeu{};
874inline constexpr empty_expr<u32s> eeuu{};
875
876template<typename K>
877struct expr_char : expr_to_std_string<expr_char<K>>{
878 using symb_type = K;
879 K value;
880 constexpr expr_char(K v) : value(v){}
881 constexpr size_t length() const noexcept {
882 return 1;
883 }
884 constexpr symb_type* place(symb_type* p) const noexcept {
885 *p++ = value;
886 return p;
887 }
888};
889
902template<typename K, StrExprForType<K> A>
903constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
904 return {a, s};
905}
906
917template<typename K>
918constexpr expr_char<K> e_char(K s) {
919 return {s};
920}
921
922template<typename K, size_t N>
923struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
924 using symb_type = K;
925 const K (&str)[N + 1];
926 constexpr expr_literal(const K (&str_)[N + 1]) : str(str_){}
927
928 constexpr size_t length() const noexcept {
929 return N;
930 }
931 constexpr symb_type* place(symb_type* p) const noexcept {
932 if constexpr (N != 0)
933 std::char_traits<K>::copy(p, str, N);
934 return p + N;
935 }
936};
937
990template<typename T, size_t N = const_lit<T>::Count>
991constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
992 return {s};
993}
994
995template<bool first, typename K, size_t N, typename A>
996struct expr_literal_join : expr_to_std_string<expr_literal_join<first, K, N, A>> {
997 using symb_type = K;
998 using atype = typename A::symb_type;
999 const K (&str)[N + 1];
1000 const A& a;
1001 constexpr expr_literal_join(const K (&str_)[N + 1], const A& a_) : str(str_), a(a_){}
1002
1003 constexpr size_t length() const noexcept {
1004 return N + a.length();
1005 }
1006 constexpr symb_type* place(symb_type* p) const noexcept {
1007 if constexpr (N != 0) {
1008 if constexpr (first) {
1009 std::char_traits<K>::copy(p, str, N);
1010 return (symb_type*)a.place((atype*)(p + N));
1011 } else {
1012 p = (symb_type*)a.place((atype*)p);
1013 std::char_traits<K>::copy(p, str, N);
1014 return p + N;
1015 }
1016 } else {
1017 return a.place(p);
1018 }
1019 }
1020};
1021
1029template<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>
1030constexpr expr_literal_join<false, P, (N - 1), A> operator+(const A& a, T&& s) {
1031 return {s, a};
1032}
1033
1041template<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>
1042constexpr expr_literal_join<true, P, (N - 1), A> operator+(T&& s, const A& a) {
1043 return {s, a};
1044}
1045
1059template<typename K, size_t N, size_t S = ' '>
1060struct expr_spaces : expr_to_std_string<expr_spaces<K, N>> {
1061 using symb_type = K;
1062 constexpr size_t length() const noexcept {
1063 return N;
1064 }
1065 constexpr symb_type* place(symb_type* p) const noexcept {
1066 if constexpr (N != 0)
1067 std::char_traits<K>::assign(p, N, static_cast<K>(S));
1068 return p + N;
1069 }
1070};
1071
1085template<size_t N>
1087 return {};
1088}
1089
1103template<size_t N>
1105 return {};
1106}
1107
1119template<typename K>
1120struct expr_pad : expr_to_std_string<expr_pad<K>> {
1121 using symb_type = K;
1122 size_t len;
1123 K s;
1124 constexpr expr_pad(size_t len_, K s_) : len(len_), s(s_){}
1125 constexpr size_t length() const noexcept {
1126 return len;
1127 }
1128 constexpr symb_type* place(symb_type* p) const noexcept {
1129 if (len)
1130 std::char_traits<K>::assign(p, len, s);
1131 return p + len;
1132 }
1133};
1134
1148template<typename K>
1149constexpr expr_pad<K> e_c(size_t l, K s) {
1150 return { l, s };
1151}
1152
1153template<typename K, size_t N>
1154struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
1155 using symb_type = K;
1156 size_t repeat_;
1157 const K (&s)[N + 1];
1158 constexpr expr_repeat_lit(size_t repeat, const K (&s_)[N + 1]) : repeat_(repeat), s(s_){}
1159 constexpr size_t length() const noexcept {
1160 return N * repeat_;
1161 }
1162 constexpr symb_type* place(symb_type* p) const noexcept {
1163 if constexpr (N) {
1164 for (size_t i = 0; i < repeat_; i++) {
1165 std::char_traits<K>::copy(p, s, N);
1166 p += N;
1167 }
1168 }
1169 return p;
1170 }
1171};
1172
1173template<StrExpr A>
1174struct expr_repeat_expr : expr_to_std_string<expr_repeat_expr<A>> {
1175 using symb_type = typename A::symb_type;
1176 size_t repeat_;
1177 const A& expr_;
1178 constexpr expr_repeat_expr(size_t repeat, const A& expr) : repeat_(repeat), expr_(expr){}
1179 constexpr size_t length() const noexcept {
1180 if (repeat_) {
1181 return repeat_ * expr_.length();
1182 }
1183 return 0;
1184 }
1185 constexpr symb_type* place(symb_type* p) const noexcept {
1186 if (repeat_) {
1187 if (repeat_ == 1) {
1188 return expr_.place(p);
1189 }
1190 symb_type* start = p;
1191 p = expr_.place(p);
1192 size_t len = size_t(p - start);
1193 if (len) {
1194 for (size_t i = 1; i < repeat_; i++) {
1195 std::char_traits<symb_type>::copy(p, start, len);
1196 p += len;
1197 }
1198 }
1199 }
1200 return p;
1201 }
1202};
1203
1217template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
1218constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
1219 return { l, s };
1220}
1221
1235template<StrExpr A>
1236constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
1237 return { l, s };
1238}
1239
1253template<StrExpr A, StrExprForType<typename A::symb_type> B>
1254struct expr_choice : expr_to_std_string<expr_choice<A, B>> {
1255 using symb_type = typename A::symb_type;
1256 using my_type = expr_choice<A, B>;
1257 const A& a;
1258 const B& b;
1259 bool choice;
1260
1261 constexpr expr_choice(const A& _a, const B& _b, bool _choice) : a(_a), b(_b), choice(_choice){}
1262
1263 constexpr size_t length() const noexcept {
1264 return choice ? a.length() : b.length();
1265 }
1266 constexpr symb_type* place(symb_type* ptr) const noexcept {
1267 return choice ? a.place(ptr) : (symb_type*)b.place((typename B::symb_type*)ptr);
1268 }
1269};
1270
1282template<StrExpr A>
1283struct expr_if : expr_to_std_string<expr_if<A>> {
1284 using symb_type = typename A::symb_type;
1285 using my_type = expr_if<A>;
1286 const A& a;
1287 bool choice;
1288 constexpr expr_if(const A& _a, bool _choice) : a(_a), choice(_choice){}
1289
1290 constexpr size_t length() const noexcept {
1291 return choice ? a.length() : 0;
1292 }
1293 constexpr symb_type* place(symb_type* ptr) const noexcept {
1294 return choice ? a.place(ptr) : ptr;
1295 }
1296};
1297
1344template<typename L, StrExprForType<L> A, size_t N, bool Compare>
1345struct expr_choice_one_lit : expr_to_std_string<expr_choice_one_lit<L, A, N, Compare>> {
1346 using symb_type = L;
1347 const symb_type (&str)[N + 1];
1348 const A& a;
1349 bool choice;
1350 constexpr expr_choice_one_lit(const symb_type (&_str)[N + 1], const A& _a, bool _choice) : str(_str), a(_a), choice(_choice){}
1351
1352 constexpr size_t length() const noexcept {
1353 return choice == Compare ? a.length() : N;
1354 }
1355 constexpr symb_type* place(symb_type* ptr) const noexcept {
1356 if (choice == Compare) {
1357 return (L*)a.place((typename A::symb_type*)ptr);
1358 }
1359 if constexpr (N != 0) {
1360 std::char_traits<symb_type>::copy(ptr, str, N);
1361 }
1362 return ptr + N;
1363 }
1364};
1365
1410template<typename K, size_t N, typename P, size_t M>
1411struct expr_choice_two_lit : expr_to_std_string<expr_choice_two_lit<K, N, P, M>> {
1412 using symb_type = K;
1413 const K (&str_a)[N + 1];
1414 const P (&str_b)[M + 1];
1415 bool choice;
1416 constexpr expr_choice_two_lit(const K(&_str_a)[N + 1], const P(&_str_b)[M + 1], bool _choice)
1417 : str_a(_str_a), str_b(_str_b), choice(_choice){}
1418
1419 constexpr size_t length() const noexcept {
1420 return choice ? N : M;
1421 }
1422 constexpr symb_type* place(symb_type* ptr) const noexcept {
1423 if (choice) {
1424 if constexpr (N != 0) {
1425 std::char_traits<symb_type>::copy(ptr, str_a, N);
1426 }
1427 return ptr + N;
1428 }
1429 if constexpr (M != 0) {
1430 std::char_traits<symb_type>::copy(ptr, (const K(&)[M + 1])str_b, M);
1431 }
1432 return ptr + M;
1433 }
1434};
1435
1465template<StrExpr A, StrExprForType<typename A::symb_type> B>
1466constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
1467 return {a, b, c};
1468}
1469
1475template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1476constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
1477 return {str, a, c};
1478}
1479
1485template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1486constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
1487 return {str, a, c};
1488}
1489
1494template<typename T, typename L, typename K = typename const_lit<T>::symb_type, typename P = typename const_lit<L>::symb_type,
1495 size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
1496 requires is_equal_str_type_v<K, P>
1497constexpr expr_choice_two_lit<K, N -1, P, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
1498 return {str_a, str_b, c};
1499}
1500
1543template<StrExpr A>
1544constexpr expr_if<A> e_if(bool c, const A& a) {
1545 return {a, c};
1546}
1547
1552template<typename T, size_t N = const_lit<T>::Count>
1553constexpr auto e_if(bool c, T&& str) {
1554 using K = typename const_lit<T>::symb_type;
1555 const K empty[1] = {0};
1556 return expr_choice_two_lit<K, N - 1, K, 0>{str, empty, c};
1557}
1558
1568template<typename K, StdStrSourceForType<K> T>
1569struct expr_stdstr {
1570 using symb_type = K;
1571 const T& t_;
1572
1573 expr_stdstr(const T& t) : t_(t){}
1574
1575 constexpr size_t length() const noexcept {
1576 return t_.length();
1577 }
1578 constexpr symb_type* place(symb_type* p) const noexcept {
1579 size_t s = t_.size();
1580 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
1581 return p + s;
1582 }
1583};
1584
1595template<typename K, StdStrSource T>
1596struct convert_to_strexpr<K, T> {
1597 using type = expr_stdstr<K, T>;
1598};
1599
1600namespace str {
1601constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
1602} // namespace str
1603
1604template<typename K>
1605std::char_traits<K> char_traits_selector(...);
1606
1607template<typename K>
1608using ch_traits = decltype(char_traits_selector<K>(int(0)));
1609
1610template<typename T>
1611concept FromIntNumber =
1612 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;
1613
1614template<typename T>
1615concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1616
1617template<typename K, bool I, typename T>
1618struct need_sign { // NOLINT
1619 bool negate;
1620 std::make_unsigned_t<T> val;
1621 constexpr need_sign(T t) : negate(t < 0), val(t < 0 ? std::make_unsigned_t<T>{} - t : t) {}
1622 constexpr void after(K*& ptr) {
1623 if (negate)
1624 *--ptr = '-';
1625 }
1626};
1627
1628template<typename K, typename T>
1629struct need_sign<K, false, T> {
1630 T val;
1631 constexpr need_sign(T t) : val(t){}
1632 constexpr void after(K*&) {}
1633};
1634
1635template<typename K, typename T>
1636constexpr size_t fromInt(K* bufEnd, T val) {
1637 const char* twoDigit =
1638 "0001020304050607080910111213141516171819"
1639 "2021222324252627282930313233343536373839"
1640 "4041424344454647484950515253545556575859"
1641 "6061626364656667686970717273747576777879"
1642 "8081828384858687888990919293949596979899";
1643 if (val) {
1644 need_sign<K, std::is_signed_v<T>, T> store(val);
1645 K* itr = bufEnd;
1646 while (store.val >= 100) {
1647 const char* ptr = twoDigit + (store.val % 100) * 2;
1648 *--itr = static_cast<K>(ptr[1]);
1649 *--itr = static_cast<K>(ptr[0]);
1650 store.val /= 100;
1651 }
1652 if (store.val < 10) {
1653 *--itr = static_cast<K>('0' + store.val);
1654 } else {
1655 const char* ptr = twoDigit + store.val * 2;
1656 *--itr = static_cast<K>(ptr[1]);
1657 *--itr = static_cast<K>(ptr[0]);
1658 }
1659 store.after(itr);
1660 return size_t(bufEnd - itr);
1661 }
1662 bufEnd[-1] = '0';
1663 return 1;
1664}
1665
1666template<typename K, typename T>
1667struct expr_num : expr_to_std_string<expr_num<K, T>> {
1668 using symb_type = K;
1669 using my_type = expr_num<K, T>;
1670
1671 enum { bufSize = 24 };
1672 mutable K buf[bufSize];
1673 mutable T value;
1674
1675 constexpr expr_num(T t) : value(t) {}
1676 constexpr expr_num(expr_num&& t) noexcept : value(t.value) {}
1677
1678 constexpr size_t length() const noexcept {
1679 value = static_cast<T>(fromInt(buf + bufSize, value));
1680 return static_cast<size_t>(value);
1681 }
1682 constexpr K* place(K* ptr) const noexcept {
1683 size_t len = static_cast<size_t>(value);
1684 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1685 return ptr + len;
1686 }
1687};
1688
1699template<typename K, FromIntNumber T>
1700struct convert_to_strexpr<K, T> {
1701 using type = expr_num<K, T>;
1702};
1703
1719template<typename K, FromIntNumber T>
1720constexpr expr_num<K, T> e_num(T t) {
1721 return {t};
1722}
1723
1724namespace f{
1725
1726enum class int_align { none, left, right, center };
1727enum class int_plus_sign {none, plus, space};
1728enum class int_prefix {none, lcase, ucase};
1729
1730enum fmt_kinds : unsigned {
1731 fmt_none = 0,
1732 fmt_align = 1,
1733 fmt_width = 2,
1734 fmt_fill = 4,
1735 fmt_sign = 8,
1736 fmt_upper = 16,
1737 fmt_prefix = 32,
1738 fmt_zero = 64,
1739};
1740
1741template<typename A, typename B, unsigned Kind>
1742struct fmt_info {
1743 using a_t = A;
1744 using b_t = B;
1745 inline static constexpr unsigned kind = Kind;
1746};
1747
1748struct fmt_default {
1749 inline static constexpr unsigned kind = fmt_none;
1750};
1751
1752template<int_align A>
1753struct align_info{
1754 inline static constexpr unsigned kind = fmt_align;
1755};
1756
1757template<size_t Width = 0>
1758struct width_info {
1759 inline static constexpr unsigned kind = fmt_width;
1760};
1761
1762template<unsigned Fill = ' '>
1763struct fill_info {
1764 inline static constexpr unsigned kind = fmt_fill;
1765};
1766
1767template<int_plus_sign S>
1768struct sign_info {
1769 inline static constexpr unsigned kind = fmt_sign;
1770};
1771
1772struct upper_info {
1773 inline static constexpr unsigned kind = fmt_upper;
1774};
1775
1776template<int_prefix>
1777struct prefix_info {
1778 inline static constexpr unsigned kind = fmt_prefix;
1779};
1780
1781struct zero_info {
1782 inline static constexpr unsigned kind = fmt_zero;
1783};
1784
1785template<typename T>
1786concept FmtParam = requires {
1787 {std::remove_cvref_t<T>::kind} -> std::same_as<const unsigned&>;
1788};
1789
1790template<FmtParam A, FmtParam B>
1791requires((A::kind & B::kind) == 0)
1792constexpr auto operator | (const A&, const B&) {
1793 return fmt_info<A, B, A::kind | B::kind>{};
1794}
1795
1796inline constexpr align_info<int_align::left> l;
1797inline constexpr align_info<int_align::right> r;
1798inline constexpr align_info<int_align::center> c;
1799
1800template<size_t W>
1801inline constexpr width_info<W> w;
1802
1803template<unsigned F>
1804inline constexpr fill_info<F> f;
1805
1806inline constexpr sign_info<int_plus_sign::plus> sp;
1807inline constexpr sign_info<int_plus_sign::space> ss;
1808inline constexpr upper_info u;
1809inline constexpr prefix_info<int_prefix::lcase> p;
1810inline constexpr prefix_info<int_prefix::ucase> P;
1811inline constexpr zero_info z;
1812
1813inline constexpr width_info<unsigned(-1)> wp;
1814inline constexpr fmt_default df;
1815
1816template<typename T>
1817struct extract_align : std::false_type {
1818 inline static constexpr int_align align = int_align::none;
1819};
1820
1821template<int_align A>
1822struct extract_align<align_info<A>> : std::true_type {
1823 inline static constexpr int_align align = A;
1824};
1825
1826template<typename A, typename B, unsigned U>
1827struct extract_align<fmt_info<A, B, U>> {
1828 inline static constexpr bool in_a = extract_align<A>::value, in_b = extract_align<B>::value, value = in_a | in_b;
1829 inline static constexpr int_align align = extract_align<std::conditional_t<in_a, A, std::conditional_t<in_b, B, align_info<int_align::none>>>>::align;
1830};
1831
1832template<typename T>
1833struct extract_width : std::false_type {
1834 inline static constexpr size_t width = 0;
1835};
1836
1837template<size_t W>
1838struct extract_width<width_info<W>> : std::true_type {
1839 inline static constexpr size_t width = W;
1840};
1841
1842template<typename A, typename B, unsigned U>
1843struct extract_width<fmt_info<A, B, U>> {
1844 inline static constexpr bool in_a = extract_width<A>::value, in_b = extract_width<B>::value, value = in_a | in_b;
1845 inline static constexpr size_t width = extract_width<std::conditional_t<in_a, A, std::conditional_t<in_b, B, width_info<0>>>>::width;
1846};
1847
1848template<typename T>
1849struct extract_fill : std::false_type {
1850 inline static constexpr unsigned fill = ' ';
1851};
1852
1853template<unsigned F>
1854struct extract_fill<fill_info<F>> : std::true_type {
1855 inline static constexpr unsigned fill = F;
1856};
1857
1858template<typename A, typename B, unsigned U>
1859struct extract_fill<fmt_info<A, B, U>> {
1860 inline static constexpr bool in_a = extract_fill<A>::value, in_b = extract_fill<B>::value, value = in_a | in_b;
1861 inline static constexpr unsigned fill = extract_fill<std::conditional_t<in_a, A, std::conditional_t<in_b, B, fill_info<' '>>>>::fill;
1862};
1863
1864template<typename T>
1865struct extract_sign : std::false_type {
1866 inline static constexpr int_plus_sign sign = int_plus_sign::none;
1867};
1868
1869template<int_plus_sign S>
1870struct extract_sign<sign_info<S>> : std::true_type {
1871 inline static constexpr int_plus_sign sign = S;
1872};
1873
1874template<typename A, typename B, unsigned U>
1875struct extract_sign<fmt_info<A, B, U>> {
1876 inline static constexpr bool in_a = extract_sign<A>::value, in_b = extract_sign<B>::value, value = in_a | in_b;
1877 inline static constexpr int_plus_sign sign = extract_sign<std::conditional_t<in_a, A, std::conditional_t<in_b, B, sign_info<int_plus_sign::none>>>>::sign;
1878};
1879
1880template<typename T>
1881struct extract_upper : std::false_type {};
1882
1883template<>
1884struct extract_upper<upper_info> : std::true_type {};
1885
1886template<typename A, typename B, unsigned U>
1887struct extract_upper<fmt_info<A, B, U>> {
1888 inline static constexpr bool in_a = extract_upper<A>::value, in_b = extract_upper<B>::value,
1889 value = extract_upper<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1890};
1891
1892template<typename T>
1893struct extract_prefix : std::false_type{
1894 inline static constexpr int_prefix prefix = int_prefix::none;
1895};
1896
1897template<int_prefix P>
1898struct extract_prefix<prefix_info<P>> : std::true_type {
1899 inline static constexpr int_prefix prefix = P;
1900};
1901
1902template<typename A, typename B, unsigned U>
1903struct extract_prefix<fmt_info<A, B, U>> {
1904 inline static constexpr bool in_a = extract_prefix<A>::value, in_b = extract_prefix<B>::value, value = in_a | in_b;
1905 inline static constexpr int_prefix prefix = extract_prefix<std::conditional_t<in_a, A, std::conditional_t<in_b, B, prefix_info<int_prefix::none>>>>::prefix;
1906};
1907
1908template<typename T>
1909struct extract_zero : std::false_type{};
1910
1911template<>
1912struct extract_zero<zero_info> : std::true_type {};
1913
1914template<typename A, typename B, unsigned U>
1915struct extract_zero<fmt_info<A, B, U>> {
1916 inline static constexpr bool in_a = extract_zero<A>::value, in_b = extract_zero<B>::value,
1917 value = extract_zero<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1918};
1919
1920template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1921struct fmt_params {
1922 inline static constexpr int_align align = Align;
1923 inline static constexpr unsigned width = Width;
1924 inline static constexpr unsigned fill = Fill;
1925 inline static constexpr int_plus_sign sign = Sign;
1926 inline static constexpr bool upper = Upper;
1927 inline static constexpr int_prefix prefix = Prefix;
1928 inline static constexpr bool zero = Zero;
1929};
1930
1931template<FmtParam T>
1932using p_to_fmt_t = fmt_params<
1933 extract_align<T>::align,
1934 extract_width<T>::width,
1935 extract_fill<T>::fill,
1936 extract_sign<T>::sign,
1937 extract_upper<T>::value,
1938 extract_prefix<T>::prefix,
1939 extract_zero<T>::value>;
1940
1941template<FmtParam T>
1942using to_fmt_t = p_to_fmt_t<std::remove_cvref_t<T>>;
1943
1944template<typename T>
1945struct is_fmt_params : std::false_type{};
1946
1947template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1948struct is_fmt_params<fmt_params<Align, Width, Fill, Sign, Upper, Prefix, Zero>> : std::true_type{};
1949
1950template<typename T>
1951concept FmtParamSet = is_fmt_params<T>::value;
1952
1953struct fmt_end{};
1954
1955template<f::FmtParam F>
1956constexpr auto operator|(const F& f, fmt_end) {
1957 return f;
1958}
1959
1960template<char C = 0, char...Chars>
1961constexpr auto parse_fmt_symbol();
1962
1963template<unsigned N, char C = 'Z', char...Chars>
1964constexpr auto parse_width() {
1965 if constexpr (C >= '0' && C <= '9') {
1966 return parse_width<N * 10 + C - '0', Chars...>();
1967 } else {
1968 return w<N> | parse_fmt_symbol<C, Chars...>();
1969 }
1970}
1971
1972template<unsigned N, char C = 'Z', char...Chars>
1973constexpr auto parse_fill() {
1974 if constexpr (C >= '0' && C <= '9') {
1975 return parse_fill<N * 16 + C - '0', Chars...>();
1976 } else if constexpr (C >= 'a' && C <= 'f') {
1977 return parse_fill<N * 16 + C - 'a' + 10, Chars...>();
1978 } else if constexpr (C >= 'A' && C <= 'F') {
1979 return parse_fill<N * 16 + C - 'A' + 10, Chars...>();
1980 } else {
1981 return f<N> | parse_fmt_symbol<C, Chars...>();
1982 }
1983}
1984
1985template<char C, char...Chars>
1986constexpr auto parse_fmt_symbol() {
1987 if constexpr (C == '0') {
1988 return z | parse_fmt_symbol<Chars...>();
1989 } else if constexpr (C == 'a') {
1990 return p | parse_fmt_symbol<Chars...>();
1991 } else if constexpr (C == 'A') {
1992 return P | parse_fmt_symbol<Chars...>();
1993 } else if constexpr (C == 'b') {
1994 return l | parse_fmt_symbol<Chars...>();
1995 } else if constexpr (C == 'c') {
1996 return c | parse_fmt_symbol<Chars...>();
1997 } else if constexpr (C == 'd') {
1998 return r | parse_fmt_symbol<Chars...>();
1999 } else if constexpr (C == 'e') {
2000 return sp | parse_fmt_symbol<Chars...>();
2001 } else if constexpr (C == 'f') {
2002 return ss | parse_fmt_symbol<Chars...>();
2003 } else if constexpr (C == 'E') {
2004 return u | parse_fmt_symbol<Chars...>();
2005 } else if constexpr (C == '\'') {
2006 return parse_fmt_symbol<Chars...>();
2007 } else if constexpr (C == 'F') {
2008 return parse_fill<0, Chars...>();
2009 } else if constexpr (C >= '1' && C <= '9') {
2010 return parse_width<C - '0', Chars...>();
2011 } else {
2012 return fmt_end{};
2013 }
2014}
2015
2016template<unsigned R, typename T>
2017struct fmt_radix_info {};
2018
2019template<unsigned N, char C = 0, char...Chars>
2020constexpr auto parse_radix() {
2021 if constexpr (C >='0' && C <= '9') {
2022 return parse_radix<N * 10 + C - '0', Chars...>();
2023 } else if constexpr (C == 0) {
2024 return fmt_radix_info<N, to_fmt_t<fmt_default>>{};
2025 } else {
2026 return fmt_radix_info<N, decltype(parse_fmt_symbol<C, Chars...>())>{};
2027 }
2028};
2029
2030template<char C1, char C2, char C3, char...Chars>
2031constexpr auto skip_0x() {
2032 static_assert(C1 == '0' && C2 == 'x' && "Fmt symbols must begin with 0x");
2033 static_assert(C3 >= '1' && C3 <= '9' && "Radix must begin with 1-9");
2034 return parse_radix<C3 - '0', Chars...>();
2035}
2036
2037} // namespace f
2038
2039template<typename K, bool Ucase>
2040inline constexpr K digits_symbols[36] = {K('0'), K('1'), K('2'), K('3'), K('4'), K('5'), K('6'), K('7'), K('8'), K('9'),
2041 K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'), K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'),
2042 K(Ucase ? 'F' : 'f'), K(Ucase ? 'G' : 'g'), K(Ucase ? 'H' : 'h'), K(Ucase ? 'I' : 'i'), K(Ucase ? 'J' : 'j'),
2043 K(Ucase ? 'K' : 'k'), K(Ucase ? 'L' : 'l'), K(Ucase ? 'M' : 'm'), K(Ucase ? 'N' : 'n'), K(Ucase ? 'O' : 'o'),
2044 K(Ucase ? 'P' : 'p'), K(Ucase ? 'Q' : 'q'), K(Ucase ? 'R' : 'r'), K(Ucase ? 'S' : 's'), K(Ucase ? 'T' : 't'),
2045 K(Ucase ? 'U' : 'u'), K(Ucase ? 'V' : 'v'), K(Ucase ? 'W' : 'w'), K(Ucase ? 'X' : 'x'), K(Ucase ? 'Y' : 'y'),
2046 K(Ucase ? 'Z' : 'z'),
2047};
2048
2049template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2050struct expr_integer_src {
2051 T value_;
2052 unsigned width_{};
2053 constexpr expr_integer_src(T v) : value_(v){}
2054 constexpr expr_integer_src(T v, unsigned w) : value_(v), width_(w){}
2055
2056 template<is_std_string_v S>
2057 operator S() const;
2058
2059 template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2060 operator S() const;
2061};
2062
2063template<is_one_of_char_v K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2064requires (Radix > 1 && Radix <= 36)
2065struct expr_integer : expr_to_std_string<expr_integer<K, T, Radix, FP>> {
2066 using symb_type = K;
2067
2068 enum { bufSize = 64 };
2069 mutable K buf[bufSize];
2070 mutable T value_;
2071 unsigned width_{};
2072
2073 mutable bool negate_{};
2074
2075 constexpr expr_integer(T t) : value_(t){}
2076 constexpr expr_integer(T t, unsigned w) : value_(t), width_(w){}
2077 constexpr expr_integer(const expr_integer_src<T, Radix, FP>& v) : value_(v.value_), width_(v.width_){}
2078
2079 constexpr expr_integer(expr_integer&& t) noexcept : value_(t.value_), width_(t.width_){}
2080
2081 constexpr size_t length() const noexcept {
2082 K* bufEnd = std::end(buf), *itr = bufEnd;
2083
2084 if (value_) {
2085 need_sign<K, std::is_signed_v<T>, T> store(value_);
2086 while (store.val) {
2087 *--itr = digits_symbols<K, FP::upper>[store.val % Radix];
2088 store.val /= Radix;
2089 }
2090 if constexpr (std::is_signed_v<T>) {
2091 negate_ = store.negate;
2092 }
2093 } else {
2094 *--itr = digits_symbols<K, FP::upper>[0];
2095 }
2096 size_t len = bufEnd - itr;
2097 value_ = T(len);
2098 if constexpr (std::is_signed_v<T>) {
2099 if constexpr (FP::sign != f::int_plus_sign::none) {
2100 len++;
2101 } else {
2102 if (negate_) {
2103 len++;
2104 }
2105 }
2106 }
2107 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2108 len++; // add 0
2109 if constexpr (Radix != 8) { // for octo just add 0,
2110 len++; // for other b or x
2111 }
2112 }
2113 if constexpr (FP::width == unsigned(-1)) {
2114 return std::max<size_t>(width_, len);
2115 } else {
2116 return std::max<size_t>(FP::width, len);
2117 }
2118 }
2119 constexpr K* place(K* ptr) const noexcept {
2120 size_t len = static_cast<size_t>(value_), all_len = len;
2121 if constexpr (std::is_signed_v<T>) {
2122 if constexpr (FP::sign != f::int_plus_sign::none) {
2123 all_len++;
2124 } else {
2125 if (negate_) {
2126 all_len++;
2127 }
2128 }
2129 }
2130 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2131 all_len++; // add 0
2132 if constexpr (Radix != 8) { // for octo just add 0,
2133 all_len++; // for other b or x
2134 }
2135 }
2136 if constexpr (FP::zero) {
2137 if constexpr (std::is_signed_v<T>) {
2138 if (negate_) {
2139 *ptr++ = K('-');
2140 } else {
2141 if constexpr (FP::sign == f::int_plus_sign::plus) {
2142 *ptr++ = K('+');
2143 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2144 *ptr++ = K(' ');
2145 }
2146 }
2147 }
2148 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2149 *ptr++ = K('0');
2150 if constexpr (Radix == 2) {
2151 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2152 } else if constexpr (Radix == 16) {
2153 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2154 }
2155 }
2156 size_t before = 0;
2157 if constexpr (FP::width == unsigned(-1)) {
2158 if (width_ > all_len) {
2159 before = width_ - all_len;
2160 }
2161 } else {
2162 if (FP::width > all_len) {
2163 before = FP::width - all_len;
2164 }
2165 }
2166 if (before) {
2167 ch_traits<K>::assign(ptr, before, K('0'));
2168 ptr += before;
2169 }
2170 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2171 ptr += len;
2172 } else {
2173 size_t before = 0, after = 0;
2174 if constexpr (FP::width == unsigned(-1)) {
2175 if (width_ > all_len) {
2176 if constexpr (FP::align == f::int_align::left) {
2177 after = width_ - all_len;
2178 } else if constexpr (FP::align == f::int_align::center) {
2179 before = (width_ - all_len) / 2;
2180 after = width_ - all_len - before;
2181 } else {
2182 before = width_ - all_len;
2183 }
2184 }
2185 } else {
2186 if (FP::width > all_len) {
2187 if constexpr (FP::align == f::int_align::left) {
2188 after = FP::width - all_len;
2189 } else if constexpr (FP::align == f::int_align::center) {
2190 before = (FP::width - all_len) / 2;
2191 after = FP::width - all_len - before;
2192 } else {
2193 before = FP::width - all_len;
2194 }
2195 }
2196 }
2197 if (before) {
2198 ch_traits<K>::assign(ptr, before, K(FP::fill));
2199 ptr += before;
2200 }
2201 if constexpr (std::is_signed_v<T>) {
2202 if (negate_) {
2203 *ptr++ = K('-');
2204 } else {
2205 if constexpr (FP::sign == f::int_plus_sign::plus) {
2206 *ptr++ = K('+');
2207 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2208 *ptr++ = K(' ');
2209 }
2210 }
2211 }
2212 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2213 *ptr++ = K('0');
2214 if constexpr (Radix == 2) {
2215 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2216 } else if constexpr (Radix == 16) {
2217 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2218 }
2219 }
2220 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2221 ptr += len;
2222
2223 if (after) {
2224 ch_traits<K>::assign(ptr, after, K(FP::fill));
2225 ptr += after;
2226 }
2227 }
2228 return ptr;
2229 }
2230};
2231
2232template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2233template<is_std_string_v S>
2234expr_integer_src<T, Radix, FP>::operator S() const {
2235 using st = typename S::value_type;
2236 using Al = typename S::allocator_type;
2237 return to_std_string<st, Al>(expr_integer<st, T, Radix, FP>{value_, width_});
2238}
2239
2240template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2241template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2242expr_integer_src<T, Radix, FP>::operator S() const {
2243 using st = typename S::symb_type;
2244 return S{expr_integer<st, T, Radix, FP>{value_, width_}};
2245}
2246
2247template<typename K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2248struct convert_to_strexpr<K, expr_integer_src<T, Radix, FP>> {
2249 using type = expr_integer<K, T, Radix, FP>;
2250};
2251
2252template<unsigned R, typename T, typename F>
2253struct flags_checker {
2254 using val_t = T;
2255 using flags_t = f::to_fmt_t<F>;
2256 inline static constexpr unsigned Radix = R;
2257};
2258
2259template<typename T, bool ArgWidth>
2260concept good_int_flags =
2261 T::Radix > 1 && T::Radix <= 36
2262 // Должно быть задано только или выравнивание, или заполнение нулём.
2263 // Only either alignment or zero-padding must be specified.
2264 && (T::flags_t::align == f::int_align::none || !T::flags_t::zero)
2265 // Флаг вывода знака должен задаваться только для знаковых чисел
2266 // The sign output flag should only be set for signed numbers.
2267 && (std::is_signed_v<typename T::val_t> || T::flags_t::sign == f::int_plus_sign::none)
2268 // Неверное поле ширины
2269 // Invalid width field
2270 && (T::flags_t::width == unsigned(-1)) == ArgWidth
2271 // Префикс может быть только при основании 2, 8, 16
2272 // Prefix may by only for radix 2, 8, 16
2273 && (T::flags_t::prefix == f::int_prefix::none || T::Radix == 2 || T::Radix == 8 || T::Radix == 16);
2274
2382template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, false>
2383constexpr auto e_int(T v) {
2384 using fmt_param = f::to_fmt_t<decltype(fp)>;
2385 return expr_integer_src<T, R, fmt_param>{v};
2386}
2387
2391template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, true>
2392constexpr auto e_int(T v, unsigned w) {
2393 using fmt_param = f::to_fmt_t<decltype(fp)>;
2394 return expr_integer_src<T, R, fmt_param>{v, w};
2395}
2396
2397template<typename T, unsigned R, typename F> requires good_int_flags<flags_checker<R, T, F>, false>
2398auto operator / (T v, const f::fmt_radix_info<R, F>&) {
2399 return expr_integer_src<T, R, f::to_fmt_t<F>>{v};
2400}
2401
2402template<typename K>
2403struct expr_real : expr_to_std_string<expr_real<K>> {
2404 using symb_type = K;
2405 mutable u8s buf[40];
2406 mutable size_t l;
2407 double v;
2408 constexpr expr_real(double d) : v(d) {}
2409 constexpr expr_real(float d) : v(d) {}
2410
2411 size_t length() const noexcept {
2412 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
2413 l = ec != std::errc{} ? 0 : ptr - buf;
2414 return l;
2415 }
2416 K* place(K* ptr) const noexcept {
2417 if constexpr (sizeof(K) == sizeof(buf[0])) {
2418 ch_traits<K>::copy(ptr, (K*)buf, l);
2419 } else {
2420 for (size_t i = 0; i < l; i++) {
2421 ptr[i] = buf[i];
2422 }
2423 }
2424 return ptr + l;
2425 }
2426};
2427
2438template<typename K, std::floating_point T>
2439struct convert_to_strexpr<K, T> {
2440 using type = expr_real<K>;
2441};
2442
2454template<typename K> requires is_one_of_char_v<K>
2455inline constexpr expr_real<K> e_num(double t) {
2456 return {t};
2457}
2458
2459template<FromIntNumber Val, bool All, bool Ucase, bool Ox>
2460struct expr_hex_src {
2461 explicit constexpr expr_hex_src(Val v) : v_(v){}
2462 Val v_;
2463};
2464
2465template<typename K, FromIntNumber Val, bool All, bool Ucase, bool Ox>
2466struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
2467 using symb_type = K;
2468 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
2469 mutable K buf_[sizeof(Val) * 2]{};
2470
2471 explicit constexpr expr_hex(Val v) : v_(v){}
2472 constexpr expr_hex(const expr_hex_src<Val, All, Ucase, Ox>& v) : v_(v.v_){}
2473
2474 constexpr size_t length() const noexcept {
2475 K *ptr = buf_ + std::size(buf_);
2476 size_t l = 0;
2477 for (;;) {
2478 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2479 v_.val >>= 4;
2480 l++;
2481 if (v_.val) {
2482 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2483 v_.val >>= 4;
2484 l++;
2485 }
2486 if (!v_.val) {
2487 if constexpr (All) {
2488 if (size_t need = sizeof(Val) * 2 - l) {
2489 ch_traits<K>::assign(buf_, need, K('0'));
2490 }
2491 l = sizeof(Val) * 2;
2492 }
2493 break;
2494 }
2495 }
2496 v_.val = l;
2497 if constexpr (std::is_signed_v<Val>) {
2498 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
2499 }
2500 return l + (Ox ? 2 : 0);
2501 }
2502 constexpr K* place(K* ptr) const noexcept {
2503 if constexpr (std::is_signed_v<Val>) {
2504 if (v_.negate) {
2505 *ptr++ = K('-');
2506 }
2507 }
2508 if constexpr (Ox) {
2509 *ptr++ = K('0');
2510 *ptr++ = K('x');
2511 }
2512 if constexpr (All) {
2513 ch_traits<K>::copy(ptr, buf_, sizeof(Val) * 2);
2514 return ptr + sizeof(Val) * 2;
2515 } else {
2516 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
2517 return ptr + v_.val;
2518 }
2519 }
2520};
2521
2527enum HexFlags : unsigned {
2528 Short = 1,
2529 No0x = 2, //< without 0x prefix
2530 Lcase = 4, //< Use lower case
2531};
2532
2583template<unsigned Flags = 0, FromIntNumber T>
2584constexpr auto e_hex(T v) {
2585 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2586}
2587
2588template<char c = 0, char...Chars>
2589constexpr unsigned parse_f16_flags() {
2590 if constexpr (c == '1') {
2591 return HexFlags::Short | parse_f16_flags<Chars...>();
2592 } else if constexpr (c == '2') {
2593 return HexFlags::Lcase | parse_f16_flags<Chars...>();
2594 } else if constexpr (c == '3') {
2595 return HexFlags::No0x | parse_f16_flags<Chars...>();
2596 } else {
2597 return 0;
2598 }
2599}
2600
2601template<unsigned N>
2602struct f16flags{};
2603
2604template<FromIntNumber T, unsigned Flags>
2605constexpr auto operator/(T v, const f16flags<Flags>&) {
2606 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2607}
2608
2609inline namespace literals {
2610template<char...Chars>
2611constexpr auto operator""_f16() {
2612 return f16flags<parse_f16_flags<Chars...>()>{};
2613}
2614} // namespace literals
2615
2624template<typename K, typename Val, bool All, bool Ucase, bool Ox>
2625struct convert_to_strexpr<K, expr_hex_src<Val, All, Ucase, Ox>> {
2626 using type = expr_hex<K, Val, All, Ucase, Ox>;
2627};
2628
2629template<typename K>
2630struct expr_pointer : expr_hex<K, uintptr_t, true, true, true> {
2631 constexpr expr_pointer(const void* ptr) : expr_hex<K, uintptr_t, true, true, true>((uintptr_t)ptr){}
2632};
2633
2642template<typename K, typename T>
2643struct convert_to_strexpr<K, const T*> {
2644 using type = expr_pointer<K>;
2645};
2646
2647template<typename K, StrExprForType<K> A, bool Left>
2648struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
2649 using symb_type = K;
2650 K symbol_;
2651 size_t width_;
2652 const A& a_;
2653 mutable size_t alen_{};
2654 constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
2655
2656 constexpr size_t length() const noexcept {
2657 alen_ = a_.length();
2658 return std::max(alen_, width_);
2659 }
2660 constexpr K* place(K* ptr) const noexcept {
2661 if (alen_ >= width_) {
2662 return (K*)a_.place((typename A::symb_type*)ptr);
2663 }
2664 size_t w = width_ - alen_;
2665 if constexpr (Left) {
2666 ch_traits<K>::assign(ptr, w, symbol_);
2667 ptr += w;
2668 return (K*)a_.place((typename A::symb_type*)ptr);
2669 } else {
2670 ptr = (K*)a_.place((typename A::symb_type*)ptr);
2671 ch_traits<K>::assign(ptr, w, symbol_);
2672 return ptr + w;
2673 }
2674 }
2675};
2676
2698template<StrExpr A, typename K = typename A::symb_type>
2699expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
2700 return {symbol, width, a};
2701}
2702
2724template<StrExpr A, typename K = typename A::symb_type>
2725expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
2726 return {symbol, width, a};
2727}
2728
2729/*
2730* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
2731* K - тип символов строки
2732* T - тип контейнера строк (vector, list)
2733* I - длина разделителя в символах
2734* tail - добавлять разделитель после последнего элемента контейнера.
2735* Если контейнер пустой, разделитель в любом случае не добавляется
2736* skip_empty - пропускать пустые строки без добавления разделителя
2737* To create string concatenations with vectors and lists joined by a constant delimiter
2738* K is the symbols
2739* T - type of string container (vector, list)
2740* I - length of separator in characters
2741* tail - add a separator after the last element of the container.
2742* If the container is empty, the separator is not added anyway
2743* skip_empty - skip empty lines without adding a separator
2744*/
2745template<typename K, typename T, size_t I, bool tail, bool skip_empty>
2746struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
2747 using symb_type = K;
2748 using my_type = expr_join<K, T, I, tail, skip_empty>;
2749
2750 const T& s;
2751 const K* delim;
2752 constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
2753
2754 constexpr size_t length() const noexcept {
2755 size_t l = 0;
2756 for (const auto& t: s) {
2757 size_t len = t.length();
2758 if (len > 0 || !skip_empty) {
2759 if (I > 0 && l > 0) {
2760 l += I;
2761 }
2762 l += len;
2763 }
2764 }
2765 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
2766 }
2767 constexpr K* place(K* ptr) const noexcept {
2768 if (s.empty()) {
2769 return ptr;
2770 }
2771 K* write = ptr;
2772 for (const auto& t: s) {
2773 size_t copyLen = t.length();
2774 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
2775 ch_traits<K>::copy(write, delim, I);
2776 write += I;
2777 }
2778 ch_traits<K>::copy(write, t.data(), copyLen);
2779 write += copyLen;
2780 }
2781 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
2782 ch_traits<K>::copy(write, delim, I);
2783 write += I;
2784 }
2785 return write;
2786 }
2787};
2788
2802template<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>
2803inline constexpr auto e_join(const T& s, L&& d) {
2804 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
2805}
2806
2807template<size_t N>
2808concept is_const_pattern = N > 1 && N <= 17;
2809
2810template<typename K, size_t I>
2811struct _ascii_mask { // NOLINT
2812 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
2813};
2814
2815template<typename K>
2816struct _ascii_mask<K, 0> {
2817 constexpr static const size_t value = 0;
2818};
2819
2820template<typename K>
2821struct ascii_mask { // NOLINT
2822 using uns = std::make_unsigned_t<K>;
2823 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
2824 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
2825};
2826
2827template<typename K>
2828constexpr inline bool isAsciiUpper(K k) {
2829 return k >= 'A' && k <= 'Z';
2830}
2831
2832template<typename K>
2833constexpr inline bool isAsciiLower(K k) {
2834 return k >= 'a' && k <= 'z';
2835}
2836
2837template<typename K>
2838constexpr inline K makeAsciiLower(K k) {
2839 return isAsciiUpper(k) ? k | 0x20 : k;
2840}
2841
2842template<typename K>
2843constexpr inline K makeAsciiUpper(K k) {
2844 return isAsciiLower(k) ? k & ~0x20 : k;
2845}
2846
2847enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
2848template<TrimSides S, typename K, size_t N, bool withSpaces = false>
2849struct trim_operator;
2850
2851template<size_t I>
2852struct digits_selector {
2853 using wider_type = uint16_t;
2854};
2855
2856template<>
2857struct digits_selector<2> {
2858 using wider_type = uint32_t;
2859};
2860
2861template<>
2862struct digits_selector<4> {
2863 using wider_type = uint64_t;
2864};
2865
2876
2877template<bool CanNegate, bool CheckOverflow, typename T>
2878struct result_type_selector { // NOLINT
2879 using type = T;
2880};
2881
2882template<typename T>
2883struct result_type_selector<true, false, T> {
2884 using type = std::make_unsigned_t<T>;
2885};
2886
2887template<unsigned Base>
2888constexpr unsigned digit_width() {
2889 if (Base <=2) {
2890 return 1;
2891 }
2892 if (Base <= 4) {
2893 return 2;
2894 }
2895 if (Base <= 8) {
2896 return 3;
2897 }
2898 if (Base <= 16) {
2899 return 4;
2900 }
2901 if (Base <= 32) {
2902 return 5;
2903 }
2904 return 6;
2905}
2906
2907template<typename T, unsigned Base>
2908constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
2909
2910template<typename T>
2911struct convert_result {
2912 T value;
2914 size_t read;
2915};
2916
2917struct int_convert { // NOLINT
2918 inline static constexpr uint8_t NUMBERS[] = {
2919 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,
2920 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,
2921 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,
2922 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,
2923 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,
2924 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,
2925 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,
2926 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,
2927 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,
2928 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
2929
2930 template<typename K, unsigned Base>
2931 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2932 auto us = static_cast<std::make_unsigned_t<K>>(s);
2933 if constexpr (Base <= 10) {
2934 return us - '0';
2935 } else {
2936 if constexpr (sizeof(K) == 1) {
2937 return NUMBERS[us];
2938 } else {
2939 return us < 256 ? NUMBERS[us] : us;
2940 }
2941 }
2942 }
2943
2944 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
2945 requires(Base != 0)
2946 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
2947 using u_type = std::make_unsigned_t<T>;
2948 #ifndef HAS_BUILTIN_OVERFLOW
2949 u_type maxMult = 0, maxAdd = 0;
2950 if constexpr (CheckOverflow) {
2951 maxMult = std::numeric_limits<u_type>::max() / Base;
2952 maxAdd = std::numeric_limits<u_type>::max() % Base;
2953 }
2954 #endif
2955 u_type number = 0;
2956 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2958 const K* from = current;
2959
2960 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2961
2962 if (no_need_check_o_f) {
2963 for (;;) {
2964 const u_type digit = toDigit<K, Base>(*current);
2965 if (digit >= Base) {
2966 break;
2967 }
2968 number = number * Base + digit;
2969 if (++current == end) {
2971 break;
2972 }
2973 }
2974 } else {
2975 for (;maxDigits; maxDigits--) {
2976 const u_type digit = toDigit<K, Base>(*current);
2977 if (digit >= Base) {
2978 break;
2979 }
2980 number = number * Base + digit;
2981 ++current;
2982 }
2983 if (!maxDigits) {
2984 // Прошли все цифры, дальше надо с проверкой на overflow
2985 // All numbers have passed, then we need to check for overflow
2986 for (;;) {
2987 const u_type digit = toDigit<K, Base>(*current);
2988 if (digit >= Base) {
2989 break;
2990 }
2991 #ifdef HAS_BUILTIN_OVERFLOW
2992 if (__builtin_mul_overflow(number, Base, &number) ||
2993 __builtin_add_overflow(number, digit, &number)) {
2994 #else
2995 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2996 number = number * Base + digit;
2997 } else {
2998 #endif
3000 while(++current < end) {
3001 if (toDigit<K, Base>(*current) >= Base) {
3002 break;
3003 }
3004 }
3005 break;
3006 }
3007 if (++current == end) {
3009 break;
3010 }
3011 }
3012 }
3013 }
3014 T result;
3015 if constexpr (std::is_signed_v<T>) {
3016 result = negate ? 0 - number : number;
3017 if constexpr (CheckOverflow) {
3018 if (error != IntConvertResult::Overflow) {
3019 if (number > (u_type)std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
3021 }
3022 }
3023 }
3024 } else {
3025 result = number;
3026 }
3027 if (error == IntConvertResult::NotNumber && current > from) {
3029 }
3030 return {result, error, size_t(current - start)};
3031 }
3032public:
3033 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
3034 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
3035 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
3036 // 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
3037 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
3038 requires(Base == -1 || (Base < 37 && Base != 1))
3039 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
3040 const K *ptr = start, *end = ptr + len;
3041 bool negate = false;
3042 if constexpr (SkipWs) {
3043 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
3044 ptr++;
3045 }
3046 if (ptr != end) {
3047 if constexpr (std::is_signed_v<T>) {
3048 if constexpr (AllowSign) {
3049 // Может быть число, +число или -число
3050 // Can be a number, +number or -number
3051 if (*ptr == '+') {
3052 ptr++;
3053 } else if (*ptr == '-') {
3054 negate = true;
3055 ptr++;
3056 }
3057 } else {
3058 // Может быть число или -число
3059 // Can be a number or -number
3060 if (*ptr == '-') {
3061 negate = true;
3062 ptr++;
3063 }
3064 }
3065 } else if constexpr (AllowSign) {
3066 // Может быть число или +число
3067 // Can be a number or +number
3068 if (*ptr == '+') {
3069 ptr++;
3070 }
3071 }
3072 }
3073 if (ptr != end) {
3074 if constexpr (Base == 0 || Base == -1) {
3075 if (*ptr == '0') {
3076 ptr++;
3077 if (ptr != end) {
3078 if (*ptr == 'x' || *ptr == 'X') {
3079 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
3080 }
3081 if constexpr (Base == -1) {
3082 if (*ptr == 'b' || *ptr == 'B') {
3083 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
3084 }
3085 if (*ptr == 'o' || *ptr == 'O') {
3086 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
3087 }
3088 }
3089 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
3090 }
3091 return {0, IntConvertResult::Success, size_t(ptr - start)};
3092 }
3093 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
3094 } else
3095 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
3096 }
3097 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
3098 }
3099};
3100
3101template<typename K, typename Impl>
3102class null_terminated {
3103public:
3110 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
3111};
3112
3113template<typename K, typename Impl, bool Mutable> class buffer_pointers;
3114
3123template<typename K, typename Impl>
3124class buffer_pointers<K, Impl, false> {
3125 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
3126public:
3133 constexpr const K* data() const { return d().symbols(); }
3140 constexpr const K* begin() const { return d().symbols(); }
3147 constexpr const K* end() const { return d().symbols() + d().length(); }
3154 constexpr const K* cbegin() const { return d().symbols(); }
3161 constexpr const K* cend() const { return d().symbols() + d().length(); }
3162};
3163
3164template<typename K, typename Impl>
3165class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
3166 constexpr Impl& d() { return *static_cast<Impl*>(this); }
3167 using base = buffer_pointers<K, Impl, false>;
3168public:
3175 constexpr const K* data() const { return base::data(); }
3182 constexpr const K* begin() const { return base::begin(); }
3189 constexpr const K* end() const { return base::end(); }
3196 constexpr const K* cbegin() const { return base::cbegin(); }
3203 constexpr const K* cend() const { return base::cend(); }
3210 constexpr K* data() { return d().str(); }
3217 constexpr K* begin() { return d().str(); }
3224 constexpr K* end() { return d().str() + d().length(); }
3225};
3226
3233template<typename K, typename StrSrc>
3234class SplitterBase {
3235 using str_t = StrSrc;
3236 str_t text_;
3237 str_t delim_;
3238
3239public:
3240 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
3245 constexpr bool is_done() const {
3246 return text_.length() == str::npos;
3247 }
3248
3254 constexpr str_t next() {
3255 if (!text_.length()) {
3256 auto ret = text_;
3257 text_.str++;
3258 text_.len--;
3259 return ret;
3260 } else if (text_.length() == str::npos) {
3261 return {nullptr, 0};
3262 }
3263 size_t pos = text_.find(delim_), next = 0;
3264 if (pos == str::npos) {
3265 pos = text_.length();
3266 next = pos + 1;
3267 } else {
3268 next = pos + delim_.length();
3269 }
3270 str_t result{text_.str, pos};
3271 text_.str += next;
3272 text_.len -= next;
3273 return result;
3274 }
3275};
3276
3301template<typename K, typename StrRef, typename Impl, bool Mutable>
3302class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
3303 constexpr const Impl& d() const noexcept {
3304 return *static_cast<const Impl*>(this);
3305 }
3306 constexpr size_t _len() const noexcept {
3307 return d().length();
3308 }
3309 constexpr const K* _str() const noexcept {
3310 return d().symbols();
3311 }
3312 constexpr bool _is_empty() const noexcept {
3313 return d().is_empty();
3314 }
3315
3316public:
3317 using symb_type = K;
3318 using str_piece = StrRef;
3319 using traits = ch_traits<K>;
3320 using uns_type = std::make_unsigned_t<K>;
3321 using my_type = Impl;
3322 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3323 str_src_algs() = default;
3324
3337 constexpr K* place(K* ptr) const noexcept {
3338 size_t myLen = _len();
3339 traits::copy(ptr, _str(), myLen);
3340 return ptr + myLen;
3341 }
3342
3352 void copy_to(K* buffer, size_t bufSize) {
3353 size_t tlen = std::min(_len(), bufSize - 1);
3354 traits::copy(buffer, _str(), tlen);
3355 buffer[tlen] = 0;
3356 }
3357
3363 constexpr size_t size() const {
3364 return _len();
3365 }
3366
3373 template<typename D = K> requires is_equal_str_type_v<K, D>
3374 constexpr std::basic_string_view<D> to_sv() const noexcept {
3375 return {(const D*)_str(), _len()};
3376 }
3377
3383 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
3384 constexpr operator std::basic_string_view<D, Traits>() const {
3385 return {(const D*)_str(), _len()};
3386 }
3387
3393 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
3394 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
3395 return {(const D*)_str(), _len()};
3396 }
3397
3403 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
3404 constexpr operator std::basic_string<D, Traits, Allocator>() const {
3405 return {(const D*)_str(), _len()};
3406 }
3407
3413 constexpr operator str_piece() const noexcept {
3414 return str_piece{_str(), _len()};
3415 }
3416
3422 constexpr str_piece to_str() const noexcept {
3423 return {_str(), _len()};
3424 }
3425
3448 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3449 size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
3450 idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
3451 if (idxEnd > myLen)
3452 idxEnd = myLen;
3453 if (idxStart > idxEnd)
3454 idxStart = idxEnd;
3455 return str_piece{_str() + idxStart, idxEnd - idxStart};
3456 }
3457
3467 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
3468 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3469 if (idxEnd > myLen)
3470 idxEnd = myLen;
3471 if (idxStart > idxEnd)
3472 idxStart = idxEnd;
3473 return str_piece{_str() + idxStart, idxEnd - idxStart};
3474 }
3475
3489 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
3490 return str_piece{_str() + from, to - from};
3491 }
3492
3502 constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
3503 return (*this)(0, find_or_all(pattern, offset));
3504 }
3505
3509 constexpr bool operator!() const noexcept {
3510 return _is_empty();
3511 }
3512
3522 constexpr K at(ptrdiff_t idx) const {
3523 return _str()[idx >= 0 ? idx : _len() + idx];
3524 }
3525 // Сравнение строк
3526 // String comparison
3527 constexpr int compare(const K* text, size_t len) const {
3528 size_t myLen = _len();
3529 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3530 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3531 }
3540 constexpr int compare(str_piece o) const {
3541 return compare(o.symbols(), o.length());
3542 }
3543
3551 constexpr int strcmp(const K* text) const {
3552 size_t myLen = _len(), idx = 0;
3553 const K* ptr = _str();
3554 for (; idx < myLen; idx++) {
3555 uns_type s1 = (uns_type)text[idx];
3556 if (!s1) {
3557 return 1;
3558 }
3559 uns_type s2 = (uns_type)ptr[idx];
3560 if (s1 < s2) {
3561 return 1;
3562 } else if (s1 > s2) {
3563 return -1;
3564 }
3565 }
3566 return text[idx] == 0 ? 0 : -1;
3567 }
3568
3569 constexpr bool equal(const K* text, size_t len) const noexcept {
3570 return len == _len() && traits::compare(_str(), text, len) == 0;
3571 }
3580 constexpr bool equal(str_piece other) const noexcept {
3581 return equal(other.symbols(), other.length());
3582 }
3583
3591 constexpr bool operator==(const base& other) const noexcept {
3592 return equal(other._str(), other._len());
3593 }
3594
3600 constexpr auto operator<=>(const base& other) const noexcept {
3601 return compare(other._str(), other._len()) <=> 0;
3602 }
3603
3609 template<typename T, size_t N = const_lit_for<K, T>::Count>
3610 constexpr bool operator==(T&& other) const noexcept {
3611 return N - 1 == _len() && traits::compare(_str(), (const K*)other, N - 1) == 0;
3612 }
3613
3619 template<typename T, size_t N = const_lit_for<K, T>::Count>
3620 constexpr auto operator<=>(T&& other) const noexcept {
3621 size_t myLen = _len();
3622 int cmp = traits::compare(_str(), (const K*)other, std::min(myLen, N - 1));
3623 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3624 return res <=> 0;
3625 }
3626
3627 // Сравнение ascii строк без учёта регистра
3628 // Compare ascii strings without taking into account case
3629 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
3630 if (!len)
3631 return _is_empty() ? 0 : 1;
3632 size_t myLen = _len(), checkLen = std::min(myLen, len);
3633 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
3634 while (checkLen--) {
3635 uns_type s1 = *ptr1++, s2 = *ptr2++;
3636 if (s1 == s2)
3637 continue;
3638 s1 = makeAsciiLower(s1);
3639 s2 = makeAsciiLower(s2);
3640 if (s1 > s2)
3641 return 1;
3642 else if (s1 < s2)
3643 return -1;
3644 }
3645 return myLen == len ? 0 : myLen > len ? 1 : -1;
3646 }
3655 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
3656 return compare_ia(text.symbols(), text.length());
3657 }
3658
3667 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
3668 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
3669 }
3670
3678 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
3679 return compare_ia(text.symbols(), text.length()) < 0;
3680 }
3681
3682 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3683 size_t lenText = _len();
3684 // Образец, не вмещающийся в строку и пустой образец не находим
3685 // We don't look for an empty string or a string longer than the text.
3686 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3687 return str::npos;
3688 lenPattern--;
3689 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3690 pattern++;
3691 for (const K* fnd = text + offset;; ++fnd) {
3692 fnd = traits::find(fnd, last - fnd, first);
3693 if (!fnd)
3694 return str::npos;
3695 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3696 return static_cast<size_t>(fnd - text);
3697 }
3698 }
3709 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
3710 return find(pattern.symbols(), pattern.length(), offset);
3711 }
3712
3728 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
3729 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
3730 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3731 return fnd;
3732 }
3733 throw Exc(std::forward<Args>(args)...);
3734 }
3735
3745 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
3746 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3747 return fnd == str::npos ? fnd : fnd + pattern.length();
3748 }
3749
3759 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3760 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3761 return fnd == str::npos ? _len() : fnd;
3762 }
3763
3773 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3774 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3775 return fnd == str::npos ? _len() : fnd + pattern.length();
3776 }
3777
3778 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3779 if (lenPattern == 1)
3780 return find_last(pattern[0], offset);
3781 size_t lenText = std::min(_len(), offset);
3782 // Образец, не вмещающийся в строку и пустой образец не находим
3783 // We don't look for an empty string or a string longer than the text.
3784 if (!lenPattern || lenPattern > lenText)
3785 return str::npos;
3786
3787 lenPattern--;
3788 const K *text = _str() + lenPattern, last = pattern[lenPattern];
3789 lenText -= lenPattern;
3790 while(lenText) {
3791 if (text[--lenText] == last) {
3792 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
3793 return lenText;
3794 }
3795 }
3796 }
3797 return str::npos;
3798 }
3809 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
3810 return find_last(pattern.symbols(), pattern.length(), offset);
3811 }
3812
3822 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
3823 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3824 return fnd == str::npos ? fnd : fnd + pattern.length();
3825 }
3826
3836 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3837 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
3838 return fnd == str::npos ? _len() : fnd;
3839 }
3840
3850 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3851 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3852 return fnd == str::npos ? _len() : fnd + pattern.length();
3853 }
3854
3864 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
3865 return find(pattern, offset) != str::npos;
3866 }
3867
3877 constexpr size_t find(K s, size_t offset = 0) const noexcept {
3878 size_t len = _len();
3879 if (offset < len) {
3880 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3881 if (fnd)
3882 return static_cast<size_t>(fnd - str);
3883 }
3884 return str::npos;
3885 }
3886
3896 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
3897 size_t len = _len();
3898 if (offset < len) {
3899 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3900 if (fnd)
3901 return static_cast<size_t>(fnd - str);
3902 }
3903 return len;
3904 }
3905
3906 template<typename Op>
3907 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3908 if (!maxCount)
3909 maxCount--;
3910 while (maxCount-- > 0) {
3911 size_t fnd = find(pattern, patternLen, offset);
3912 if (fnd == str::npos)
3913 break;
3914 op(fnd);
3915 offset = fnd + patternLen;
3916 }
3917 }
3930 template<typename Op>
3931 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3932 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
3933 }
3934
3935 template<typename To = std::vector<size_t>>
3936 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3937 To result;
3938 for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
3939 return result;
3940 }
3953 template<typename To = std::vector<size_t>>
3954 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3955 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
3956 }
3957 template<typename To = std::vector<size_t>>
3958 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
3959 return for_all_finded([&](size_t pos) {
3960 to.emplace_back(pos);
3961 }, pattern, len, offset, maxCount);
3962 }
3973 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
3974 size_t len = std::min(_len(), offset);
3975 const K *text = _str();
3976 while (len > 0) {
3977 if (text[--len] == s)
3978 return len;
3979 }
3980 return str::npos;
3981 }
3982
3992 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
3993 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
3994 }
3995
4005 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
4006 const K* text = _str();
4007 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4008 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4009 }
4010
4020 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
4021 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4022 }
4023
4033 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4034 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4035 }
4036
4046 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
4047 const K* text = _str();
4048 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4049 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4050 }
4051
4061 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4062 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4063 }
4064
4074 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
4075 return my_type{d()(from, len)};
4076 }
4077
4087 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
4088 return my_type{d().mid(from, len)};
4089 }
4090
4118 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4119 constexpr T as_int() const noexcept {
4120 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4121 return err == IntConvertResult::Overflow ? 0 : res;
4122 }
4123
4151 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4152 constexpr convert_result<T> to_int() const noexcept {
4153 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4154 }
4155
4161 template<bool SkipWS = true, bool AllowPlus = true>
4162 requires(sizeof(K) == 1 &&
4163 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4164 std::optional<double> to_double() const noexcept {
4165 size_t len = _len();
4166 const K* ptr = _str();
4167 if constexpr (SkipWS) {
4168 while (len && uns_type(*ptr) <= ' ') {
4169 len--;
4170 ptr++;
4171 }
4172 }
4173 if constexpr (AllowPlus) {
4174 if (len && *ptr == K('+')) {
4175 ptr++;
4176 len--;
4177 }
4178 }
4179 if (!len) {
4180 return {};
4181 }
4182 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4183 double d{};
4184 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
4185 return d;
4186 }
4187 }
4188 return {};
4189 }
4190
4196 template<bool SkipWS = true>
4197 requires(sizeof(K) == 1 &&
4198 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4199 std::optional<double> to_double_hex() const noexcept {
4200 size_t len = _len();
4201 const K* ptr = _str();
4202 if constexpr (SkipWS) {
4203 while (len && uns_type(*ptr) <= ' ') {
4204 len--;
4205 ptr++;
4206 }
4207 }
4208 if (len) {
4209 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4210 double d{};
4211 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4212 return d;
4213 }
4214 }
4215 }
4216 return {};
4217 }
4218
4226 template<ToIntNumber T>
4227 constexpr void as_number(T& t) const {
4228 t = as_int<T>();
4229 }
4230
4231 template<typename T, typename Op>
4232 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
4233 size_t mylen = _len();
4234 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4235 str_piece me{_str(), mylen};
4236 for (size_t i = 0;; i++) {
4237 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4238 if (beginOfDelim == str::npos) {
4239 str_piece last{me.symbols() + offset, me.length() - offset};
4240 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4241 beforeFunc(last);
4242 }
4243 if constexpr (requires { results.emplace_back(last); }) {
4244 if (last.is_same(me)) {
4245 // Пробуем положить весь объект.
4246 // Try to put the entire object.
4247 results.emplace_back(d());
4248 } else {
4249 results.emplace_back(last);
4250 }
4251 } else if constexpr (requires { results.push_back(last); }) {
4252 if (last.is_same(me)) {
4253 // Пробуем положить весь объект.
4254 // Try to put the entire object.
4255 results.push_back(d());
4256 } else {
4257 results.push_back(last);
4258 }
4259 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
4260 if (i < std::size(results)) {
4261 if (last.is_same(me)) {
4262 // Пробуем положить весь объект.
4263 // Try to put the entire object.
4264 results[i] = d();
4265 } else
4266 results[i] = last;
4267 }
4268 }
4269 break;
4270 }
4271 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4272 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4273 beforeFunc(piece);
4274 }
4275 if constexpr (requires { results.emplace_back(piece); }) {
4276 results.emplace_back(piece);
4277 } else if constexpr (requires { results.push_back(piece); }) {
4278 results.push_back(piece);
4279 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
4280 if (i < std::size(results)) {
4281 results[i] = piece;
4282 if (i == results.size() - 1) {
4283 break;
4284 }
4285 }
4286 }
4287 offset = beginOfDelim + lendelimiter;
4288 }
4289 if constexpr (!std::is_same_v<T, void>) {
4290 return results;
4291 }
4292 }
4323 template<typename T, typename Op>
4324 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
4325 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4326 }
4327
4339 template<typename T>
4340 constexpr T split(str_piece delimiter, size_t offset = 0) const {
4341 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4342 }
4343
4344 // Начинается ли эта строка с указанной подстроки
4345 // Does this string start with the specified substring
4346 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
4347 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4348 }
4355 constexpr bool starts_with(str_piece prefix) const noexcept {
4356 return starts_with(prefix.symbols(), prefix.length());
4357 }
4358
4364 constexpr bool starts_with_and_ws(str_piece prefix) const noexcept {
4365 return _len() > prefix.length() &&
4366 starts_with(prefix) &&
4367 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4368 }
4369
4377 constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4378 return _len() > prefix.length() &&
4379 starts_with(prefix) &&
4380 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4381 }
4382 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4383 constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4384 return _len() >= N &&
4385 starts_with(prefix) &&
4386 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4387 }
4388
4389 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
4390 size_t myLen = _len();
4391 if (myLen < len) {
4392 return false;
4393 }
4394 const K* ptr1 = _str();
4395 while (len--) {
4396 K s1 = *ptr1++, s2 = *prefix++;
4397 if (s1 == s2)
4398 continue;
4399 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4400 return false;
4401 }
4402 return true;
4403 }
4410 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
4411 return starts_with_ia(prefix.symbols(), prefix.length());
4412 }
4413
4419 constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept {
4420 return _len() > prefix.length() &&
4421 starts_with_ia(prefix) &&
4422 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4423 }
4424
4432 constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4433 return _len() > prefix.length() &&
4434 starts_with_ia(prefix) &&
4435 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4436 }
4437 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4438 constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4439 return _len() >= N &&
4440 starts_with_ia(prefix) &&
4441 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4442 }
4443
4444 // Является ли эта строка началом указанной строки
4445 // Is this string the beginning of the specified string
4446 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
4447 size_t myLen = _len();
4448 if (myLen > len)
4449 return false;
4450 return !myLen || 0 == traits::compare(text, _str(), myLen);
4451 }
4458 constexpr bool prefix_in(str_piece text) const noexcept {
4459 return prefix_in(text.symbols(), text.length());
4460 }
4461 // Заканчивается ли строка указанной подстрокой
4462 // Does the string end with the specified substring
4463 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
4464 size_t myLen = _len();
4465 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4466 }
4473 constexpr bool ends_with(str_piece suffix) const noexcept {
4474 return ends_with(suffix.symbols(), suffix.length());
4475 }
4476 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
4477 // Whether the string ends with the specified substring, case insensitive ASCII
4478 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
4479 size_t myLen = _len();
4480 if (myLen < len) {
4481 return false;
4482 }
4483 const K* ptr1 = _str() + myLen - len;
4484 while (len--) {
4485 K s1 = *ptr1++, s2 = *suffix++;
4486 if (s1 == s2)
4487 continue;
4488 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4489 return false;
4490 }
4491 return true;
4492 }
4499 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
4500 return ends_with_ia(suffix.symbols(), suffix.length());
4501 }
4502
4506 constexpr bool is_ascii() const noexcept {
4507 if (_is_empty())
4508 return true;
4509 if (std::is_constant_evaluated()) {
4510 for (size_t idx = 0; idx < _len(); idx++) {
4511 if (uns_type(_str()[idx]) > 127) {
4512 return false;
4513 }
4514 }
4515 return true;
4516 }
4517 const int sl = ascii_mask<K>::WIDTH;
4518 const size_t mask = ascii_mask<K>::VALUE;
4519 size_t len = _len();
4520 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
4521 if constexpr (sl > 1) {
4522 const size_t roundMask = sizeof(size_t) - 1;
4523 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4524 if (*ptr++ > 127)
4525 return false;
4526 len--;
4527 }
4528 while (len >= sl) {
4529 if (*reinterpret_cast<const size_t*>(ptr) & mask)
4530 return false;
4531 ptr += sl;
4532 len -= sl;
4533 }
4534 }
4535 while (len--) {
4536 if (*ptr++ > 127)
4537 return false;
4538 }
4539 return true;
4540 }
4541
4549 template<typename R = my_type>
4551 return R::upperred_only_ascii_from(d());
4552 }
4553
4561 template<typename R = my_type>
4563 return R::lowered_only_ascii_from(d());
4564 }
4565
4581 template<typename R = my_type>
4582 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
4583 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4584 }
4585
4597 template<typename R = str_piece>
4598 constexpr std::optional<R> strip_prefix(str_piece prefix) const {
4599 if (starts_with(prefix)) {
4600 return R{operator()(prefix.length())};
4601 }
4602 return {};
4603 }
4604
4616 template<typename R = str_piece>
4617 constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
4618 if (starts_with_ia(prefix)) {
4619 return R{operator()(prefix.length())};
4620 }
4621 return {};
4622 }
4623
4635 template<typename R = str_piece>
4636 constexpr std::optional<R> strip_suffix(str_piece suffix) const {
4637 if (ends_with(suffix)) {
4638 return R{operator()(0, -suffix.length())};
4639 }
4640 return {};
4641 }
4642
4654 template<typename R = str_piece>
4655 constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
4656 if (ends_with_ia(suffix)) {
4657 return R{operator()(0, -suffix.length())};
4658 }
4659 return {};
4660 }
4661
4662 template<StrType<K> From>
4663 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
4664 str_piece sfrom = from, newPos = opTrim(sfrom);
4665 if (newPos.is_same(sfrom)) {
4666 my_type res = from;
4667 return res;
4668 }
4669 return my_type{newPos};
4670 }
4671 template<TrimSides S, StrType<K> From>
4672 constexpr static my_type trim_static(const From& from) {
4673 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
4674 }
4675
4676 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
4677 requires is_const_pattern<N>
4678 constexpr static my_type trim_static(const From& from, T&& pattern) {
4679 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
4680 }
4681
4682 template<TrimSides S, bool withSpaces, StrType<K> From>
4683 constexpr static my_type trim_static(const From& from, str_piece pattern) {
4684 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
4685 }
4694 template<typename R = str_piece>
4695 constexpr R trimmed() const {
4696 return R::template trim_static<TrimSides::TrimAll>(d());
4697 }
4698
4706 template<typename R = str_piece>
4707 constexpr R trimmed_left() const {
4708 return R::template trim_static<TrimSides::TrimLeft>(d());
4709 }
4710
4718 template<typename R = str_piece>
4719 constexpr R trimmed_right() const {
4720 return R::template trim_static<TrimSides::TrimRight>(d());
4721 }
4722
4732 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4733 requires is_const_pattern<N>
4734 constexpr R trimmed(T&& pattern) const {
4735 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4736 }
4737
4747 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4748 requires is_const_pattern<N>
4749 constexpr R trimmed_left(T&& pattern) const {
4750 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4751 }
4752
4762 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4763 requires is_const_pattern<N>
4764 constexpr R trimmed_right(T&& pattern) const {
4765 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4766 }
4767 // Триминг по символам в литерале и пробелам
4768 // Trimming by characters in literal and spaces
4769
4784 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4785 requires is_const_pattern<N>
4786 constexpr R trimmed_with_spaces(T&& pattern) const {
4787 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4788 }
4789
4803 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4804 requires is_const_pattern<N>
4805 constexpr R trimmed_left_with_spaces(T&& pattern) const {
4806 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4807 }
4808
4822 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4823 requires is_const_pattern<N>
4824 constexpr R trimmed_right_with_spaces(T&& pattern) const {
4825 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4826 }
4827 // Триминг по динамическому источнику
4828 // Trimming by dynamic source
4829
4840 template<typename R = str_piece>
4841 constexpr R trimmed(str_piece pattern) const {
4842 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4843 }
4844
4854 template<typename R = str_piece>
4855 constexpr R trimmed_left(str_piece pattern) const {
4856 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4857 }
4858
4868 template<typename R = str_piece>
4869 constexpr R trimmed_right(str_piece pattern) const {
4870 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4871 }
4872
4886 template<typename R = str_piece>
4887 constexpr R trimmed_with_spaces(str_piece pattern) const {
4888 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4889 }
4890
4904 template<typename R = str_piece>
4905 constexpr R trimmed_left_with_spaces(str_piece pattern) const {
4906 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4907 }
4908
4922 template<typename R = str_piece>
4923 constexpr R trimmed_right_with_spaces(str_piece pattern) const {
4924 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4925 }
4926
4938 template<typename R = str_piece>
4939 constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
4940 str_piece res = *this;
4941 while(res.starts_with(prefix)) {
4942 res = res(prefix.length());
4943 if (--max_count == 0) {
4944 break;
4945 }
4946 }
4947 return res;
4948 }
4949
4961 template<typename R = str_piece>
4962 constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
4963 str_piece res = *this;
4964 while(res.starts_with_ia(prefix)) {
4965 res = res(prefix.length());
4966 if (--max_count == 0) {
4967 break;
4968 }
4969 }
4970 return res;
4971 }
4972
4984 template<typename R = str_piece>
4985 constexpr R trimmed_suffix(str_piece suffix) const {
4986 str_piece res = *this;
4987 while(res.ends_with(suffix)) {
4988 res = res(0, -suffix.length());
4989 }
4990 return res;
4991 }
4992
5004 template<typename R = str_piece>
5005 constexpr R trimmed_suffix_ia(str_piece suffix) const {
5006 str_piece res = *this;
5007 while(res.ends_with_ia(suffix)) {
5008 res = res(0, -suffix.length());
5009 }
5010 return res;
5011 }
5012
5022 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
5023 return SplitterBase<K, str_piece>{*this, delimiter};
5024 }
5025};
5026
5027template<size_t N> requires (N > 1)
5028struct find_all_container {
5029 static constexpr size_t max_capacity = N;
5030 size_t positions_[N];
5031 size_t added_{};
5032
5033 constexpr void emplace_back(size_t pos) {
5034 positions_[added_++] = pos;
5035 }
5036};
5037
5056template<typename K>
5057struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
5058 using symb_type = K;
5059 using my_type = str_src<K>;
5060
5061 const symb_type* str;
5062 size_t len;
5063
5064 str_src() = default;
5069 template<typename T, size_t N = const_lit_for<K, T>::Count>
5070 constexpr str_src(T&& v) noexcept : str((const K*)v), len(N - 1) {}
5071
5076 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
5077
5078 template<StrType<K> T>
5079 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
5080
5085 template<typename A>
5086 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
5091 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
5092
5097 constexpr size_t length() const noexcept {
5098 return len;
5099 }
5100
5104 constexpr const symb_type* symbols() const noexcept {
5105 return str;
5106 }
5107
5111 constexpr bool is_empty() const noexcept {
5112 return len == 0;
5113 }
5114
5120 constexpr bool is_same(str_src<K> other) const noexcept {
5121 return str == other.str && len == other.len;
5122 }
5123
5129 constexpr bool is_part_of(str_src<K> other) const noexcept {
5130 return str >= other.str && str + len <= other.str + other.len;
5131 }
5132
5140 constexpr K operator[](size_t idx) const {
5141 return str[idx];
5142 }
5143
5151 constexpr my_type& remove_prefix(size_t delta) {
5152 str += delta;
5153 len -= delta;
5154 return *this;
5155 }
5156
5164 constexpr my_type& remove_suffix(size_t delta) {
5165 len -= delta;
5166 return *this;
5167 }
5168};
5169
5192template<typename K>
5193struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
5194 using symb_type = K;
5195 using my_type = str_src_nt<K>;
5196 using base = str_src<K>;
5197
5198 constexpr static const K empty_string[1] = {0};
5199
5200 str_src_nt() = default;
5217 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
5218 constexpr explicit str_src_nt(T&& p) noexcept {
5219 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
5220 base::str = base::len ? p : empty_string;
5221 }
5222
5226 template<typename T, size_t N = const_lit_for<K, T>::Count>
5227 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
5228
5233 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
5234
5235 template<StrType<K> T>
5236 constexpr str_src_nt(T&& t) {
5237 base::str = t.symbols();
5238 base::len = t.length();
5239 }
5244 template<typename A>
5245 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5246
5247 static const my_type empty_str;
5256 constexpr my_type to_nts(size_t from) {
5257 if (from > base::len) {
5258 from = base::len;
5259 }
5260 return {base::str + from, base::len - from};
5261 }
5262};
5263
5264template<typename K>
5265inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5266template<typename K> struct simple_str_selector;
5267
5268inline namespace literals {
5328template<char...Chars>
5329SS_CONSTEVAL auto operator""_fmt() {
5330 return f::skip_0x<Chars...>();
5331}
5332
5333} // namespace literals
5334
5335#ifndef IN_FULL_SIMSTR
5336
5337template<typename K>
5338using simple_str = str_src<K>;
5339
5340template<typename K>
5341struct simple_str_selector {
5342 using type = simple_str<K>;
5343};
5344
5345template<typename K>
5346using simple_str_nt = str_src_nt<K>;
5347
5348template<typename K>
5349using Splitter = SplitterBase<K, str_src<K>>;
5350
5351using ssa = str_src<u8s>;
5352using ssb = str_src<ubs>;
5353using ssw = str_src<wchar_t>;
5354using ssu = str_src<u16s>;
5355using ssuu = str_src<u32s>;
5356using stra = str_src_nt<u8s>;
5357using strb = str_src_nt<ubs>;
5358using strw = str_src_nt<wchar_t>;
5359using stru = str_src_nt<u16s>;
5360using struu = str_src_nt<u32s>;
5361
5362template<typename K>
5363consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
5364 if constexpr (std::is_same_v<K, u8s>)
5365 return s8;
5366 if constexpr (std::is_same_v<K, ubs>)
5367 return sb;
5368 if constexpr (std::is_same_v<K, uws>)
5369 return sw;
5370 if constexpr (std::is_same_v<K, u16s>)
5371 return s16;
5372 if constexpr (std::is_same_v<K, u32s>)
5373 return s32;
5374}
5375
5376#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5377
5378inline namespace literals {
5379
5390SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
5391 return str_src_nt<u8s>{ptr, l};
5392}
5393
5403SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
5404 return str_src_nt<ubs>{ptr, l};
5405}
5406
5416SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
5417 return str_src_nt<uws>{ptr, l};
5418}
5419
5429SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
5430 return str_src_nt<u16s>{ptr, l};
5431}
5432
5443SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
5444 return str_src_nt<u32s>{ptr, l};
5445}
5446
5447} // namespace literals
5448
5449#endif
5450
5451template<typename K, bool withSpaces>
5452struct CheckSpaceTrim {
5453 constexpr bool is_trim_spaces(K s) const {
5454 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
5455 }
5456};
5457template<typename K>
5458struct CheckSpaceTrim<K, false> {
5459 constexpr bool is_trim_spaces(K) const {
5460 return false;
5461 }
5462};
5463
5464template<typename K>
5465struct CheckSymbolsTrim {
5466 str_src<K> symbols;
5467 constexpr bool is_trim_symbols(K s) const {
5468 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
5469 }
5470};
5471
5472template<typename K, size_t N>
5473struct CheckConstSymbolsTrim {
5474 const const_lit_to_array<K, N> symbols;
5475
5476 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
5477 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5478
5479 constexpr bool is_trim_symbols(K s) const noexcept {
5480 return symbols.contain(s);
5481 }
5482};
5483
5484template<typename K>
5485struct CheckConstSymbolsTrim<K, 0> {
5486 constexpr bool is_trim_symbols(K) const {
5487 return false;
5488 }
5489};
5490
5491template<typename K, size_t N>
5492struct SymbSelector {
5493 using type = CheckConstSymbolsTrim<K, N>;
5494};
5495
5496template<typename K>
5497struct SymbSelector<K, 0> {
5498 using type = CheckSymbolsTrim<K>;
5499};
5500
5501template<typename K>
5502struct SymbSelector<K, static_cast<size_t>(-1)> {
5503 using type = CheckConstSymbolsTrim<K, 0>;
5504};
5505
5506template<TrimSides S, typename K, size_t N, bool withSpaces>
5507struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5508 constexpr bool isTrim(K s) const {
5509 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5510 }
5511 constexpr str_src<K> operator()(str_src<K> from) const {
5512 if constexpr ((S & TrimSides::TrimLeft) != 0) {
5513 while (from.len) {
5514 if (isTrim(*from.str)) {
5515 from.str++;
5516 from.len--;
5517 } else
5518 break;
5519 }
5520 }
5521 if constexpr ((S & TrimSides::TrimRight) != 0) {
5522 const K* back = from.str + from.len - 1;
5523 while (from.len) {
5524 if (isTrim(*back)) {
5525 back--;
5526 from.len--;
5527 } else
5528 break;
5529 }
5530 }
5531 return from;
5532 }
5533};
5534
5535template<TrimSides S, typename K>
5536using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
5537
5538template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
5539 requires is_const_pattern<N>
5540constexpr inline auto trimOp(T&& pattern) {
5541 return trim_operator<S, K, N - 1, withSpaces>{pattern};
5542}
5543
5544template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
5545constexpr inline auto trimOp(str_src<K> pattern) {
5546 return trim_operator<S, K, 0, withSpaces>{pattern};
5547}
5548
5549static constexpr size_t FIND_CACHE_SIZE = 16;
5550
5551template<typename K, size_t N, size_t L>
5552struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
5553 using symb_type = K;
5554 using my_type = expr_replaces<K, N, L>;
5555 str_src<K> what;
5556 const K(&pattern)[N + 1];
5557 const K(&repl)[L + 1];
5558 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5559 mutable size_t last_;
5560
5561 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
5562
5563 constexpr size_t length() const {
5564 size_t l = what.length();
5565 if constexpr (N == L) {
5566 return l;
5567 }
5568 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
5569 if (matches_.added_) {
5570 last_ = matches_.positions_[matches_.added_ - 1] + N;
5571 l += int(L - N) * matches_.added_;
5572
5573 if (matches_.added_ == FIND_CACHE_SIZE) {
5574 for (;;) {
5575 size_t next = what.find(pattern, N, last_);
5576 if (next == str::npos) {
5577 break;
5578 }
5579 last_ = next + N;
5580 l += L - N;
5581 }
5582 }
5583 }
5584 if (!l) {
5585 matches_.added_ = -1;
5586 }
5587 return l;
5588 }
5589 constexpr K* place(K* ptr) const noexcept {
5590 if constexpr (N == L) {
5591 const K* from = what.symbols();
5592 for (size_t start = 0; start < what.length();) {
5593 size_t next = what.find(pattern, N, start);
5594 if (next == str::npos) {
5595 next = what.length();
5596 }
5597 size_t delta = next - start;
5598 ch_traits<K>::copy(ptr, from + start, delta);
5599 ptr += delta;
5600 ch_traits<K>::copy(ptr, repl, L);
5601 ptr += L;
5602 start = next + N;
5603 }
5604 return ptr;
5605 }
5606 if (matches_.added_ == 0) {
5607 return what.place(ptr);
5608 } else if (matches_.added_ == size_t(-1)) {
5609 // after replaces text become empty
5610 return ptr;
5611 }
5612 const K* from = what.symbols();
5613 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5614 ch_traits<K>::copy(ptr, from + start, offset - start);
5615 ptr += offset - start;
5616 ch_traits<K>::copy(ptr, repl, L);
5617 ptr += L;
5618 start = offset + N;
5619 if (start >= last_) {
5620 size_t tail = what.length() - last_;
5621 ch_traits<K>::copy(ptr, from + last_, tail);
5622 ptr += tail;
5623 break;
5624 } else {
5625 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
5626 }
5627 }
5628 return ptr;
5629 }
5630};
5631
5645template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
5646 requires(N > 1)
5647constexpr auto e_repl(A&& w, T&& p, X&& r) {
5648 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
5649}
5650
5658template<typename K>
5659struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
5660 using symb_type = K;
5661 using my_type = expr_replaced<K>;
5662 str_src<K> what;
5663 const str_src<K> pattern;
5664 const str_src<K> repl;
5665 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5666 mutable size_t last_;
5677 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
5678
5679 constexpr size_t length() const {
5680 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
5681 if (!plen || plen == rlen) {
5682 return l;
5683 }
5684 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
5685 if (matches_.added_) {
5686 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5687 l += int(rlen - plen) * matches_.added_;
5688
5689 if (matches_.added_ == FIND_CACHE_SIZE) {
5690 for (;;) {
5691 size_t next = what.find(pattern.symbols(), plen, last_);
5692 if (next == str::npos) {
5693 break;
5694 }
5695 last_ = next + plen;
5696 l += rlen - plen;
5697 }
5698 }
5699 }
5700 if (!l) {
5701 matches_.added_ = -1;
5702 }
5703 return l;
5704 }
5705 constexpr K* place(K* ptr) const noexcept {
5706 size_t plen = pattern.length(), rlen = repl.length();
5707 if (plen == rlen) {
5708 const K* from = what.symbols();
5709 for (size_t start = 0; start < what.length();) {
5710 size_t next = what.find(pattern, start);
5711 if (next == str::npos) {
5712 next = what.length();
5713 }
5714 size_t delta = next - start;
5715 ch_traits<K>::copy(ptr, from + start, delta);
5716 ptr += delta;
5717 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5718 ptr += rlen;
5719 start = next + plen;
5720 }
5721 return ptr;
5722 }
5723 if (matches_.added_ == 0) {
5724 return what.place(ptr);
5725 } else if (matches_.added_ == -1) {
5726 // after replaces text become empty
5727 return ptr;
5728 }
5729 const K* from = what.symbols();
5730 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5731 ch_traits<K>::copy(ptr, from + start, offset - start);
5732 ptr += offset - start;
5733 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5734 ptr += rlen;
5735 start = offset + plen;
5736 if (start >= last_) {
5737 size_t tail = what.length() - start;
5738 ch_traits<K>::copy(ptr, from + start, tail);
5739 ptr += tail;
5740 break;
5741 } else {
5742 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
5743 }
5744 }
5745 return ptr;
5746 }
5747};
5748
5764template<typename K, StrExprForType<K> E>
5765struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
5766 using symb_type = K;
5767 using my_type = expr_replaced<K>;
5768 str_src<K> what;
5769 const str_src<K> pattern;
5770 mutable size_t replLen;
5771 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5772 mutable size_t last_;
5773 const E& expr;
5784 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
5785
5786 constexpr size_t length() const {
5787 size_t l = what.length(), plen = pattern.length();
5788 if (!plen) {
5789 return l;
5790 }
5791 matches_.positions_[0] = what.find(pattern);
5792 if (matches_.positions_[0] == -1) {
5793 // Не нашли вхождений, нечего менять
5794 return l;
5795 }
5796 matches_.added_ = 1;
5797 // Вхождение есть, надо теперь получить длину замены
5798 replLen = expr.length();
5799 if (replLen == plen) {
5800 // Замена той же длины, общая длина не изменится
5801 return l;
5802 }
5803 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
5804
5805 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5806 l += int(replLen - plen) * matches_.added_;
5807
5808 if (matches_.added_ == FIND_CACHE_SIZE) {
5809 for (;;) {
5810 size_t next = what.find(pattern.symbols(), plen, last_);
5811 if (next == str::npos) {
5812 break;
5813 }
5814 last_ = next + plen;
5815 l += replLen - plen;
5816 }
5817 }
5818 if (!l) {
5819 matches_.added_ = -1;
5820 }
5821 return l;
5822 }
5823 constexpr K* place(K* ptr) const noexcept {
5824 if (matches_.added_ == 0) {
5825 // не было найдено вхождений
5826 return what.place(ptr);
5827 } else if (matches_.added_ == -1) {
5828 // Строка стала пустой
5829 return ptr;
5830 }
5831 size_t plen = pattern.length();
5832 const K* from = what.symbols();
5833 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
5834 ptr += matches_.positions_[0];
5835 const K* repl = ptr;
5836 expr.place((typename E::symb_type*)ptr);
5837 ptr += replLen;
5838 size_t start = matches_.positions_[0] + plen;
5839
5840 if (plen == replLen) {
5841 for (;;) {
5842 size_t next = what.find(pattern, start);
5843 if (next == str::npos) {
5844 break;
5845 }
5846 size_t delta = next - start;
5847 ch_traits<K>::copy(ptr, from + start, delta);
5848 ptr += delta;
5849 ch_traits<K>::copy(ptr, repl, replLen);
5850 ptr += replLen;
5851 start = next + plen;
5852 }
5853 } else {
5854 for (size_t idx = 1;;) {
5855 if (start >= last_) {
5856 break;
5857 }
5858 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
5859 size_t delta = next - start;
5860 ch_traits<K>::copy(ptr, from + start, delta);
5861 ptr += delta;
5862 ch_traits<K>::copy(ptr, repl, replLen);
5863 ptr += replLen;
5864 start = next + plen;
5865 }
5866 }
5867 size_t tail = what.length() - start;
5868 ch_traits<K>::copy(ptr, from + start, tail);
5869 return ptr + tail;
5870 }
5871};
5872
5886template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, typename X>
5887 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>))
5888constexpr auto e_repl(A&& w, T&& p, X&& r) {
5889 str_src<K> pattern{std::forward<T>(p)};
5890 str_src<K> repl{std::forward<X>(r)};
5891 return expr_replaced<K>{std::forward<A>(w), pattern, repl};
5892}
5893
5907template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, StrExprForType<K> E>
5908 requires std::is_constructible_v<str_src<K>, T>
5909constexpr auto e_repl(A&& w, T&& p, const E& expr) {
5910 str_src<K> pattern{std::forward<T>(p)};
5911 return expr_replaced_e<K, E>{std::forward<A>(w), pattern, expr};
5912}
5913
5914template<bool UseVectorForReplace>
5915struct replace_search_result_store {
5916 size_t count_{};
5917 std::pair<size_t, size_t> replaces_[16];
5918};
5919
5920template<>
5921struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
5922
5923// Строковое выражение для замены символов
5924// String expression to replace characters
5925template<typename K, size_t N, bool UseVectorForReplace>
5926struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
5927 using symb_type = K;
5928 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5929 const K pattern_[N];
5930 const str_src<K> source_;
5931 const str_src<K> replaces_[N];
5932
5933 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5934
5935 [[_no_unique_address]]
5936 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
5937
5938 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
5939 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
5940
5941 size_t length() const {
5942 size_t l = source_.length();
5943 auto [fnd, num] = find_first_of(source_.str, source_.len);
5944 if (fnd == str::npos) {
5945 return l;
5946 }
5947 l += replaces_[num].len - 1;
5948 if constexpr (UseVectorForReplace) {
5949 search_results_.reserve((l >> 4) + 8);
5950 search_results_.emplace_back(fnd, num);
5951 for (size_t start = fnd + 1;;) {
5952 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5953 if (fnd == str::npos) {
5954 break;
5955 }
5956 search_results_.emplace_back(fnd, idx);
5957 start = fnd + 1;
5958 l += replaces_[idx].len - 1;
5959 }
5960 } else {
5961 const size_t max_store = std::size(search_results_.replaces_);
5962 search_results_.replaces_[0] = {fnd, num};
5963 search_results_.count_++;
5964 for (size_t start = fnd + 1;;) {
5965 auto [found, idx] = find_first_of(source_.str, source_.len, start);
5966 if (found == str::npos) {
5967 break;
5968 }
5969 if (search_results_.count_ < max_store) {
5970 search_results_.replaces_[search_results_.count_] = {found, idx};
5971 }
5972 l += replaces_[idx].len - 1;
5973 search_results_.count_++;
5974 start = found + 1;
5975 }
5976 }
5977 return l;
5978 }
5979 K* place(K* ptr) const noexcept {
5980 size_t start = 0;
5981 const K* text = source_.str;
5982 if constexpr (UseVectorForReplace) {
5983 for (const auto& [pos, num] : search_results_) {
5984 size_t delta = pos - start;
5985 ch_traits<K>::copy(ptr, text + start, delta);
5986 ptr += delta;
5987 ptr = replaces_[num].place(ptr);
5988 start = pos + 1;
5989 }
5990 } else {
5991 const size_t max_store = std::size(search_results_.replaces_);
5992 size_t founded = search_results_.count_;
5993 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
5994 const auto [pos, num] = search_results_.replaces_[idx];
5995 size_t delta = pos - start;
5996 ch_traits<K>::copy(ptr, text + start, delta);
5997 ptr += delta;
5998 ptr = replaces_[num].place(ptr);
5999 start = pos + 1;
6000 }
6001 if (founded > max_store) {
6002 founded -= max_store;
6003 while (founded--) {
6004 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6005 size_t delta = fnd - start;
6006 ch_traits<K>::copy(ptr, text + start, delta);
6007 ptr += delta;
6008 ptr = replaces_[idx].place(ptr);
6009 start = fnd + 1;
6010 }
6011 }
6012 }
6013 size_t tail = source_.len - start;
6014 ch_traits<K>::copy(ptr, text + start, tail);
6015 return ptr + tail;
6016 }
6017
6018protected:
6019 template<typename ... Repl>
6020 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
6021 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6022
6023 template<typename ... Repl> requires (sizeof...(Repl) == N)
6024 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
6025 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6026 {
6027 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6028 for (size_t idx = 0; idx < N; idx++) {
6029 uu8s s = static_cast<uu8s>(pattern_[idx]);
6030 if constexpr (sizeof(K) == 1) {
6031 bit_mask_[s >> 3] |= 1 << (s & 7);
6032 } else {
6033 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6034 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6035 } else {
6036 bit_mask_[s >> 3] |= 1 << (s & 7);
6037 }
6038 }
6039 }
6040 }
6041 }
6042
6043 template<size_t Idx>
6044 size_t index_of(K s) const {
6045 if constexpr (Idx < N) {
6046 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6047 }
6048 return -1;
6049 }
6050 bool is_in_mask(uu8s s) const {
6051 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6052 }
6053 bool is_in_mask2(uu8s s) const {
6054 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6055 }
6056 bool is_in_pattern(K s, size_t& idx) const {
6057 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6058 if constexpr (sizeof(K) == 1) {
6059 if (is_in_mask(s)) {
6060 idx = index_of<0>(s);
6061 return true;
6062 }
6063 } else {
6064 if (std::make_unsigned_t<const K>(s) > 255) {
6065 if (is_in_mask2(s)) {
6066 return (idx = index_of<0>(s)) != -1;
6067 }
6068 } else {
6069 if (is_in_mask(s)) {
6070 idx = index_of<0>(s);
6071 return true;
6072 }
6073 }
6074 }
6075 }
6076 return false;
6077 }
6078 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6079 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6080 size_t idx;
6081 while (offset < len) {
6082 if (is_in_pattern(text[offset], idx)) {
6083 return {offset, idx};
6084 }
6085 offset++;
6086 }
6087 } else {
6088 while (offset < len) {
6089 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6090 return {offset, idx};
6091 }
6092 offset++;
6093 }
6094 }
6095 return {-1, -1};
6096 }
6097};
6098
6138template<bool UseVector = false, StrSource A, typename K = symb_type_from_src_t<A>, typename ... Repl>
6139 requires (sizeof...(Repl) % 2 == 0)
6140auto e_repl_const_symbols(A&& src, Repl&& ... other) {
6141 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
6142}
6143
6183template<typename K, bool UseVectorForReplace = false>
6184struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
6185 using symb_type = K;
6186 using str_t = typename simple_str_selector<K>::type;
6187 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6188
6189 const str_src<K> source_;
6190 const std::vector<std::pair<K, str_t>>& replaces_;
6191
6192 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
6193
6194 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6195
6196 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6224 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
6225 : source_(source), replaces_(repl)
6226 {
6227 size_t pattern_len = replaces_.size();
6228 pattern_.resize(pattern_len);
6229 K* pattern = pattern_.data();
6230
6231 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6232 *pattern++ = replaces_[idx].first;
6233 }
6234
6235 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6236 for (size_t idx = 0; idx < pattern_len; idx++) {
6237 uu8s s = static_cast<uu8s>(pattern_[idx]);
6238 if constexpr (sizeof(K) == 1) {
6239 bit_mask_[s >> 3] |= (1 << (s & 7));
6240 } else {
6241 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6242 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6243 } else {
6244 bit_mask_[s >> 3] |= (1 << (s & 7));
6245 }
6246 }
6247 }
6248 }
6249 }
6250
6251 size_t length() const {
6252 size_t l = source_.length();
6253 auto [fnd, num] = find_first_of(source_.str, source_.len);
6254 if (fnd == str::npos) {
6255 return l;
6256 }
6257 l += replaces_[num].second.len - 1;
6258 if constexpr (UseVectorForReplace) {
6259 search_results_.reserve((l >> 4) + 8);
6260 search_results_.emplace_back(fnd, num);
6261 for (size_t start = fnd + 1;;) {
6262 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6263 if (fnd == str::npos) {
6264 break;
6265 }
6266 search_results_.emplace_back(fnd, idx);
6267 start = fnd + 1;
6268 l += replaces_[idx].second.len - 1;
6269 }
6270 } else {
6271 const size_t max_store = std::size(search_results_.replaces_);
6272 search_results_.replaces_[0] = {fnd, num};
6273 search_results_.count_++;
6274 for (size_t start = fnd + 1;;) {
6275 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6276 if (found == str::npos) {
6277 break;
6278 }
6279 if (search_results_.count_ < max_store) {
6280 search_results_.replaces_[search_results_.count_] = {found, idx};
6281 }
6282 l += replaces_[idx].second.len - 1;
6283 search_results_.count_++;
6284 start = found + 1;
6285 }
6286 }
6287 return l;
6288 }
6289 K* place(K* ptr) const noexcept {
6290 size_t start = 0;
6291 const K* text = source_.str;
6292 if constexpr (UseVectorForReplace) {
6293 for (const auto& [pos, num] : search_results_) {
6294 size_t delta = pos - start;
6295 ch_traits<K>::copy(ptr, text + start, delta);
6296 ptr += delta;
6297 ptr = replaces_[num].second.place(ptr);
6298 start = pos + 1;
6299 }
6300 } else {
6301 const size_t max_store = std::size(search_results_.replaces_);
6302 size_t founded = search_results_.count_;
6303 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6304 const auto [pos, num] = search_results_.replaces_[idx];
6305 size_t delta = pos - start;
6306 ch_traits<K>::copy(ptr, text + start, delta);
6307 ptr += delta;
6308 ptr = replaces_[num].second.place(ptr);
6309 start = pos + 1;
6310 }
6311 if (founded > max_store) {
6312 founded -= max_store;
6313 while (founded--) {
6314 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6315 size_t delta = fnd - start;
6316 ch_traits<K>::copy(ptr, text + start, delta);
6317 ptr += delta;
6318 ptr = replaces_[idx].second.place(ptr);
6319 start = fnd + 1;
6320 }
6321 }
6322 }
6323 size_t tail = source_.len - start;
6324 ch_traits<K>::copy(ptr, text + start, tail);
6325 return ptr + tail;
6326 }
6327
6328protected:
6329 size_t index_of(K s) const {
6330 return pattern_.find(s);
6331 }
6332
6333 bool is_in_mask(uu8s s) const {
6334 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6335 }
6336 bool is_in_mask2(uu8s s) const {
6337 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6338 }
6339
6340 bool is_in_pattern(K s, size_t& idx) const {
6341 if constexpr (sizeof(K) == 1) {
6342 if (is_in_mask(s)) {
6343 idx = index_of(s);
6344 return true;
6345 }
6346 } else {
6347 if (std::make_unsigned_t<const K>(s) > 255) {
6348 if (is_in_mask2(s)) {
6349 return (idx = index_of(s)) != -1;
6350 }
6351 } else {
6352 if (is_in_mask(s)) {
6353 idx = index_of(s);
6354 return true;
6355 }
6356 }
6357 }
6358 return false;
6359 }
6360
6361 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6362 size_t pl = pattern_.length();
6363 if (pl >= BIT_SEARCH_TRESHHOLD) {
6364 size_t idx;
6365 while (offset < len) {
6366 if (is_in_pattern(text[offset], idx)) {
6367 return {offset, idx};
6368 }
6369 offset++;
6370 }
6371 } else {
6372 while (offset < len) {
6373 if (size_t idx = index_of(text[offset]); idx != -1) {
6374 return {offset, idx};
6375 }
6376 offset++;
6377 }
6378 }
6379 return {-1, -1};
6380 }
6381};
6382
6383template<typename K, StrExprForType<K> T>
6384struct force_copy {
6385 const T& t_;
6386 force_copy(const T& t) : t_(t){}
6387};
6388
6389template<typename K, typename T>
6390struct symb_type_from_src<force_copy<K, T>> {
6391 using type = K;
6392};
6393
6394
6395template<StrExpr T>
6396force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6397
6398template<typename K, typename T>
6399constexpr auto to_subst(T&& t) {
6400 return to_strexpr<K>(std::forward<T>(t));
6401}
6402
6403template<typename K, typename T, size_t N>
6404constexpr auto to_subst(const T(&t)[N]) {
6405 return expr_literal<T, N - 1>{t};
6406}
6407
6408template<typename K, StrExprForType<K> T>
6409constexpr decltype(auto) to_subst(T&& t) {
6410 return std::forward<T>(t);
6411}
6412
6413template<typename K, typename T>
6414constexpr T to_subst(const force_copy<K, T>& t) {
6415 return t.t_;
6416}
6417
6418template<typename K, typename T>
6419constexpr T to_subst(force_copy<K, T>&& t) {
6420 return t.t_;
6421}
6422
6423template<typename K, typename T>
6424constexpr T to_subst(force_copy<K, T>& t) {
6425 return t.t_;
6426}
6427
6428template<typename K, typename T>
6429using to_str_exp_t = decltype(to_subst<K>(std::declval<T>()));
6430
6438template<typename K, typename G, typename Arg, typename...Args>
6439struct e_concat : expr_to_std_string<e_concat<K, G, Arg, Args...>> {
6440 using symb_type = K;
6441 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6442 using arg_t = to_str_exp_t<K, Arg>;
6443 to_str_exp_t<K, G> glue_;
6444 arg_t arg_;
6445 store_t args_;
6446 mutable size_t glue_len_;
6479 constexpr e_concat(G&& glue, Arg&& arg, Args&&...args)
6480 : glue_(to_subst<K>(std::forward<G>(glue)))
6481 , arg_(to_subst<K>(std::forward<Arg>(arg)))
6482 , args_(to_subst<K>(std::forward<Args>(args))...) {}
6483
6484 constexpr size_t length() const noexcept {
6485 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6486 glue_len_ = glue_.length();
6487 size_t l = arg_.length() + glue_len_ * sizeof...(Args);
6488 ((l += std::get<Indexes>(args_).length()),...);
6489 return l;
6490 }(std::make_index_sequence<sizeof...(Args)>());
6491 }
6492 constexpr K* place(K* ptr) const noexcept {
6493 return [this]<size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6494 ptr = (K*)arg_.place((typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6495 const K* glueStart = ptr;
6496 ptr = glue_.place(ptr);
6497 (
6498 (
6499 ptr = (K*)std::get<Indexes>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6500 glue_len_ > 0 && Indexes < sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) : nullptr
6501 ),
6502 ...);
6503 return ptr;
6504 }(ptr, std::make_index_sequence<sizeof...(Args)>());
6505 }
6506};
6507// CTAD deducing rule for e_concat
6508template<typename T, typename ... Args> requires (sizeof...(Args) > 1)
6509e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6510
6511struct parse_subst_string_error {
6512 parse_subst_string_error(const char*){}
6513};
6514
6515namespace details {
6516
6517template<typename K, size_t NParams>
6518constexpr size_t parse_pattern_string(str_src<K> pattern, auto&& add_part, auto&& add_param) {
6519 char used_args[NParams] = {0};
6520 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
6521 size_t all_len = 0;
6522
6523 auto find = [](const K* from, const K* last, K s) {
6524 while (from != last) {
6525 if (*from == s) {
6526 break;
6527 }
6528 from++;
6529 }
6530 return from;
6531 };
6532 size_t idx_in_params = 0;
6533
6534 while (first != last) {
6535 const K* open_pos = first;
6536 if (*first != '{') {
6537 open_pos = find(first, last, '{');
6538
6539 for (;;) {
6540 const K* close_pos = find(first, open_pos, '}');
6541 if (close_pos == open_pos) {
6542 unsigned len = open_pos - first;
6543 add_part(first - start, len);
6544 all_len += len;
6545 break;
6546 }
6547 ++close_pos;
6548 if (close_pos == open_pos || *close_pos != '}') {
6549 throw parse_subst_string_error{"unescaped }"};
6550 }
6551 unsigned len = close_pos - first;
6552 add_part(first - start, len);
6553 all_len += len;
6554 first = ++close_pos;
6555 }
6556 if (open_pos == last) {
6557 break;
6558 }
6559 }
6560 if (++open_pos == last) {
6561 throw parse_subst_string_error{"unescaped {"};
6562 }
6563 if (*open_pos == '}') {
6564 if (idx_in_params == -1) {
6565 throw parse_subst_string_error{"already used param ids"};
6566 }
6567 if (idx_in_params == NParams) {
6568 throw parse_subst_string_error{"too many params"};
6569 }
6570 used_args[idx_in_params] = 1;
6571 add_param(idx_in_params++);
6572 first = open_pos + 1;
6573 } else if (*open_pos == '{') {
6574 add_part(open_pos - start, 1);
6575 all_len++;
6576 first = open_pos + 1;
6577 } else {
6578 if (idx_in_params != 0 && idx_in_params != -1) {
6579 throw parse_subst_string_error{"already used non id params"};
6580 }
6581 idx_in_params = -1;
6582 const K* end = find(open_pos, last, '}');
6583 if (end == last) {
6584 throw parse_subst_string_error{"not found }"};
6585 }
6586 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
6587 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
6588 throw parse_subst_string_error{"bad param id"};
6589 }
6590 used_args[--p] = 1;
6591 add_param(p);
6592 first = end + 1;
6593 }
6594 }
6595 for (auto c : used_args) {
6596 if (!c) {
6597 throw parse_subst_string_error{"unused param"};
6598 }
6599 }
6600 return all_len;
6601}
6602
6603struct portion {
6604 unsigned start: 16;
6605 unsigned len: 15;
6606 unsigned is_param: 1;
6607
6608 portion() = default;
6609
6610 constexpr void set_param(unsigned param) {
6611 if (param >= (1 << 16)) {
6612 throw parse_subst_string_error{"the parameter id is too large"};
6613 }
6614 start = param;
6615 is_param = 1;
6616 }
6617 constexpr void set_part(unsigned from, unsigned l) {
6618 if (from >= (1 << 16) || len >= (1 << 15)) {
6619 throw parse_subst_string_error{"the string part is too large"};
6620 }
6621 start = from;
6622 len = l;
6623 is_param = 0;
6624 }
6625};
6626
6627template<typename K, size_t PtLen, size_t NParams>
6628struct subst_params {
6629 const K(&source_)[PtLen];
6630 unsigned all_len_{};
6631 unsigned actual_{};
6632 // The pattern string can be divided into a maximum of this number of portions.
6633 // "a{}a{}a{}a" - One portion of one symbol from the edge, and two portions for every three symbols
6634 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
6635
6636 consteval subst_params(const K(&pattern)[PtLen]) : source_(pattern) {
6637 all_len_ = parse_pattern_string<K, NParams>(pattern,
6638 [this](unsigned from, unsigned len) {
6639 portions_[actual_++].set_part(from, len);
6640 }, [this](unsigned param) {
6641 portions_[actual_++].set_param(param);
6642 });
6643 }
6644};
6645
6646} // namespace details
6647
6653template<typename K, size_t PtLen, typename ... Args>
6654struct e_subst : expr_to_std_string<e_subst<K, PtLen, Args...>> {
6655 inline static constexpr size_t NParams = sizeof...(Args);
6656 using symb_type = K;
6657 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6658
6659 const details::subst_params<K, PtLen, NParams>& subst_;
6660 store_t args_;
6701 constexpr e_subst(const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
6702 : subst_(subst)
6703 , args_(to_subst<K>(std::forward<Args>(args))...){}
6704
6705 constexpr size_t length() const noexcept {
6706 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6707 size_t idx = 0;
6708 size_t expr_length_[NParams] = {};
6709 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6710 size_t l = subst_.all_len_;
6711 for (idx = 0; idx < subst_.actual_; idx++) {
6712 if (subst_.portions_[idx].is_param) {
6713 l += expr_length_[subst_.portions_[idx].start];
6714 }
6715 }
6716 return l;
6717 }(std::make_index_sequence<sizeof...(Args)>());
6718 }
6719 template<size_t Idx>
6720 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6721 if (idx == Idx) {
6722 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6723 }
6724 if constexpr (Idx < NParams - 1) {
6725 return place_idx<Idx + 1>(ptr, idx);
6726 }
6727 return ptr;
6728 }
6729 constexpr K* place(K* ptr) const noexcept {
6730 for (size_t idx = 0; idx < subst_.actual_; idx++) {
6731 if (subst_.portions_[idx].is_param) {
6732 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
6733 } else {
6734 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
6735 ptr += subst_.portions_[idx].len;
6736 }
6737 }
6738 return ptr;
6739 }
6740};
6741
6742// CTAD deducing rule for e_subst
6743template<typename K, size_t N, typename...Args> requires (sizeof...(Args) > 0)
6744e_subst(const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
6745
6751template<typename K, typename ... Args>
6752struct e_vsubst : expr_to_std_string<e_vsubst<K, Args...>> {
6753 inline static constexpr size_t Nparams = sizeof...(Args);
6754 using symb_type = K;
6755 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6756
6757 details::portion portions_[Nparams * 3];
6758 std::vector<details::portion> more_portions_;
6759 store_t args_;
6760 str_src<K> pattern_;
6761 size_t all_len_{};
6762 unsigned actual_{};
6800 constexpr e_vsubst(str_src<K> pattern, Args&&...args)
6801 : pattern_(pattern)
6802 , args_(to_subst<K>(std::forward<Args>(args))...) {
6803
6804 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [this](unsigned from, unsigned len) {
6805 if (actual_ < std::size(portions_)) {
6806 portions_[actual_++].set_part(from, len);
6807 } else {
6808 if (actual_ == std::size(portions_)) {
6809 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6810 }
6811 more_portions_.emplace_back().set_part(from, len);
6812 }
6813 }, [this](unsigned param) {
6814 if (actual_ < std::size(portions_)) {
6815 portions_[actual_++].set_param(param);
6816 } else {
6817 if (actual_ == std::size(portions_)) {
6818 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6819 }
6820 more_portions_.emplace_back().set_param(param);
6821 }
6822 }
6823 );
6824 }
6825 constexpr size_t length() const noexcept {
6826 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6827 size_t idx = 0;
6828 size_t expr_length_[Nparams] = {};
6829 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6830 size_t l = all_len_;
6831 for (idx = 0; idx < actual_; idx++) {
6832 if (portions_[idx].is_param) {
6833 l += expr_length_[portions_[idx].start];
6834 }
6835 }
6836 for (const auto& p : more_portions_) {
6837 if (p.is_param) {
6838 l += expr_length_[p.start];
6839 }
6840 }
6841 return l;
6842 }(std::make_index_sequence<sizeof...(Args)>());
6843 }
6844 template<size_t Idx>
6845 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6846 if (idx == Idx) {
6847 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6848 }
6849 if constexpr (Idx < Nparams - 1) {
6850 return place_idx<Idx + 1>(ptr, idx);
6851 }
6852 return ptr;
6853 }
6854 constexpr K* place(K* ptr) const noexcept {
6855 for (size_t idx = 0; idx < actual_; idx++) {
6856 if (portions_[idx].is_param) {
6857 ptr = place_idx<0>(ptr, portions_[idx].start);
6858 } else {
6859 ch_traits<K>::copy(ptr, pattern_.symbols() + portions_[idx].start, portions_[idx].len);
6860 ptr += portions_[idx].len;
6861 }
6862 }
6863 for (const auto& p : more_portions_) {
6864 if (p.is_param) {
6865 ptr = place_idx<0>(ptr, p.start);
6866 } else {
6867 const K* from = pattern_.symbols() + p.start;
6868 for (size_t idx = p.len; idx--;) {
6869 *ptr++ = *from++;
6870 }
6871 //ch_traits<K>::copy(ptr, pattern_.symbols() + p.start, p.len);
6872 //ptr += p.len;
6873 }
6874 }
6875 return ptr;
6876 }
6877};
6878// CTAD deducing rule for e_vsubst
6879template<StrSourceNoLiteral T, typename...Args> requires (sizeof...(Args) > 0)
6880e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
6881
6882template<is_one_of_char_v K, bool upper>
6883struct expr_change_case_ascii : expr_to_std_string<expr_change_case_ascii<K, upper>>{
6884 using symb_type = K;
6885
6886 str_src<K> src_;
6887
6888 template<StrSource S>
6889 expr_change_case_ascii(S&& s) : src_(std::forward<S>(s)){}
6890
6891 constexpr size_t length() const noexcept {
6892 return src_.length();
6893 }
6894 constexpr K* place(K* ptr) const noexcept {
6895 const K* read = src_.str;
6896 for (size_t l = src_.len; l--;) {
6897 if constexpr (upper) {
6898 *ptr++ = makeAsciiUpper(*read++);
6899 } else {
6900 *ptr++ = makeAsciiLower(*read++);
6901 }
6902 }
6903 return ptr;
6904 }
6905};
6906
6921template<is_one_of_char_v K>
6922struct e_ascii_upper : expr_change_case_ascii<K, true> {
6923 using base = expr_change_case_ascii<K, true>;
6924 using base::base;
6925};
6926
6927template<StrSource S>
6929
6944template<is_one_of_char_v K>
6945struct e_ascii_lower : expr_change_case_ascii<K, false> {
6946 using base = expr_change_case_ascii<K, false>;
6947 using base::base;
6948};
6949
6950template<StrSource S>
6952
6957namespace str {
6958
6991template<typename K, typename A, StrExprForType<K> E>
6992std::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) {
6993 size_t expr_length = expr.length();
6994 if (!expr_length) {
6995 str.erase(from, count);
6996 return str;
6997 }
6998 size_t str_length = str.length();
6999 if (from > str_length) {
7000 from = str_length;
7001 }
7002 if (count > str_length || from + count > str_length) {
7003 count = str_length - from;
7004 }
7005 size_t new_length = str_length - count + expr_length;
7006 size_t tail_length = str_length - count - from;
7007
7008 if (new_length <= str_length) {
7009 K* data = str.data();
7010 expr.place((typename E::symb_type*)data + from);
7011 if (expr_length < count) {
7012 if (tail_length) {
7013 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7014 }
7015 str.resize(new_length);
7016 }
7017 } else {
7018 auto fill = [&](K* data, size_t) -> size_t {
7019 if (tail_length) {
7020 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7021 }
7022 expr.place((typename E::symb_type*)data + from);
7023 return new_length;
7024 };
7025 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
7026 str.resize_and_overwrite(new_length, fill);
7027 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
7028 str._Resize_and_overwrite(new_length, fill);
7029 } else {
7030 str.resize(new_length);
7031 fill(str.data(), 0);
7032 }
7033 }
7034 return str;
7035}
7036
7064template<typename K, typename A, StrExprForType<K> E>
7065std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7066 return change(str, str.length(), 0, expr);
7067}
7068
7097template<typename K, typename A, StrExprForType<K> E>
7098std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7099 return change(str, 0, 0, expr);
7100}
7101
7132template<typename K, typename A, StrExprForType<K> E>
7133std::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) {
7134 return change(str, from, 0, expr);
7135}
7136
7161template<typename K, typename A, StrExprForType<K> E>
7162std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7163 if (size_t expr_length = expr.length()) {
7164 if (expr_length <= str.length()) {
7165 K* data = str.data();
7166 expr.place((typename E::symb_type*)data);
7167 str.resize(expr_length);
7168 } else {
7169 auto fill = [&](K* data, size_t) -> size_t {
7170 expr.place((typename E::symb_type*)data);
7171 return expr_length;
7172 };
7173 if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
7174 str.resize_and_overwrite(expr_length, fill);
7175 } else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
7176 str._Resize_and_overwrite(expr_length, fill);
7177 } else {
7178 str.resize(expr_length);
7179 expr.place((typename E::symb_type*)str.data());
7180 }
7181 }
7182 } else {
7183 str.clear();
7184 }
7185 return str;
7186}
7187
7188namespace details {
7189
7190template<typename K, typename A, typename E>
7191struct replace_grow_helper {
7192 using my_type = std::basic_string<K, std::char_traits<K>, A>;
7193
7194 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)
7195 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
7196 my_type& str;
7197
7198 const str_src<K> source;
7199 const str_src<K> pattern;
7200 K* repl;
7201 const size_t replLen;
7202
7203 size_t maxCount;
7204 const size_t delta;
7205 size_t all_delta{};
7206 const E& expr;
7207
7208 K* reserve_for_copy{};
7209 size_t end_of_piece{};
7210 size_t total_length{};
7211
7212 std::optional<my_type> dst;
7213
7214 void replace(size_t offset) {
7215 size_t found[16] = {offset};
7216 maxCount--;
7217
7218 offset += pattern.len;
7219 all_delta += delta;
7220 size_t idx = 1;
7221 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
7222 found[idx] = source.find(pattern, offset);
7223 if (found[idx] == npos) {
7224 break;
7225 }
7226 offset = found[idx] + pattern.len;
7227 all_delta += delta;
7228 }
7229 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
7230 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
7231 }
7232 // Теперь делаем свои замены
7233 // Now we make our replacements
7234 if (!reserve_for_copy) {
7235 // Только начинаем
7236 // Just getting started
7237 end_of_piece = source.length();
7238 total_length = end_of_piece + all_delta;
7239 my_type* dst_str{};
7240 if (total_length <= str.capacity()) {
7241 // Строка поместится в старое место | The string will be placed in the old location.
7242 dst_str = &str;
7243 } else {
7244 // Будем создавать в другом буфере | We will create in another buffer.
7245 dst_str = &dst.emplace();
7246 }
7247 auto fill = [this](K* p, size_t) -> size_t {
7248 reserve_for_copy = p;
7249 return total_length;
7250 };
7251 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
7252 dst_str->_Resize_and_overwrite(total_length, fill);
7253 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
7254 dst_str->resize_and_overwrite(total_length, fill);
7255 } else {
7256 dst_str->resize(total_length);
7257 reserve_for_copy = dst_str->data();
7258 }
7259 }
7260 const K* src_start = str.c_str();
7261 while(idx-- > 0) {
7262 size_t pos = found[idx] + pattern.len;
7263 size_t lenOfPiece = end_of_piece - pos;
7264 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
7265 if constexpr (std::is_same_v<E, int>) {
7266 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7267 } else {
7268 if (!repl) {
7269 repl = reserve_for_copy + pos + all_delta - replLen;
7270 expr.place(repl);
7271 } else {
7272 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7273 }
7274 }
7275 all_delta -= delta;
7276 end_of_piece = found[idx];
7277 }
7278 if (!all_delta && reserve_for_copy != src_start) {
7279 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
7280 str = std::move(*dst);
7281 }
7282 }
7283};
7284
7285} // namespace details
7286
7321template<typename K, typename A, StrExprForType<K> E, typename T>
7322requires (std::is_constructible_v<str_src<K>, T>)
7323std::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) {
7324 if (!max_count) {
7325 return str;
7326 }
7327 str_src<K> src = str;
7328 str_src<K> spattern{std::forward<T>(pattern)};
7329 offset = src.find(pattern, offset);
7330 if (offset == npos) {
7331 return str;
7332 }
7333 size_t replLen = repl.length();
7334 K* replStart{};
7335 if (spattern.len == replLen) {
7336 // Заменяем inplace на подстроку такой же длины
7337 // Replace inplace with a substring of the same length
7338 K* ptr = str.data();
7339 replStart = ptr + offset;
7340 repl.place(replStart);
7341
7342 while (--max_count) {
7343 offset = src.find(spattern, offset + replLen);
7344 if (offset == npos)
7345 break;
7346 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7347 }
7348 } else if (spattern.len > replLen) {
7349 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7350 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7351 K* ptr = str.data();
7352 replStart = ptr + offset;
7353 repl.place(replStart);
7354 size_t posWrite = offset + replLen;
7355 offset += spattern.len;
7356
7357 while (--max_count) {
7358 size_t idx = src.find(spattern, offset);
7359 if (idx == npos)
7360 break;
7361 size_t lenOfPiece = idx - offset;
7362 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7363 posWrite += lenOfPiece;
7364 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7365 posWrite += replLen;
7366 offset = idx + spattern.len;
7367 }
7368 size_t tailLen = src.len - offset;
7369 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7370 str.resize(posWrite + tailLen);
7371 } else {
7372 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7373 }
7374 return str;
7375}
7376
7397template<typename K, typename A>
7398std::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) {
7399 if (!max_count) {
7400 return str;
7401 }
7402 str_src<K> src = str;
7403 offset = src.find(pattern, offset);
7404 if (offset == npos) {
7405 return str;
7406 }
7407 if (pattern.len == repl.len) {
7408 // Заменяем inplace на подстроку такой же длины
7409 // Replace inplace with a substring of the same length
7410 K* ptr = str.data();
7411 while (max_count--) {
7412 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7413 offset = src.find(pattern, offset + repl.len);
7414 if (offset == npos)
7415 break;
7416 }
7417 } else if (pattern.len > repl.len) {
7418 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7419 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7420 K* ptr = str.data();
7421 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7422 size_t posWrite = offset + repl.len;
7423 offset += pattern.len;
7424
7425 while (--max_count) {
7426 size_t idx = src.find(pattern, offset);
7427 if (idx == npos)
7428 break;
7429 size_t lenOfPiece = idx - offset;
7430 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7431 posWrite += lenOfPiece;
7432 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7433 posWrite += repl.len;
7434 offset = idx + pattern.len;
7435 }
7436 size_t tailLen = src.len - offset;
7437 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7438 str.resize(posWrite + tailLen);
7439 } else {
7440 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7441 }
7442 return str;
7443}
7444
7445template<typename K, typename A, typename T, typename M>
7446requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7447std::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) {
7448 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7449}
7450
7463template<typename K, typename A>
7464std::basic_string<K, std::char_traits<K>, A>& make_ascii_upper(std::basic_string<K, std::char_traits<K>, A>& str, size_t from = 0, size_t to = -1) {
7465 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7466 K s = *ptr;
7467 if (isAsciiLower(s)) {
7468 *ptr = s & ~0x20;
7469 }
7470 }
7471 return str;
7472}
7473
7486template<typename K, typename A>
7487std::basic_string<K, std::char_traits<K>, A>& make_ascii_lower(std::basic_string<K, std::char_traits<K>, A>& str, size_t from = 0, size_t to = -1) {
7488 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7489 K s = *ptr;
7490 if (isAsciiUpper(s)) {
7491 *ptr = s | 0x20;
7492 }
7493 }
7494 return str;
7495}
7496
7497} // namespace str
7498
7499} // namespace simstr
7500
7505namespace std {
7525template<simstr::StdStrSource T>
7527 return {str};
7528}
7529
7557template<typename K, typename A, simstr::StrExprForType<K> E>
7558std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7559 return simstr::str::change(str, str.length(), 0, expr);
7560}
7561
7589template<typename K, typename A, simstr::StrExprForType<K> E>
7590std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7591 return simstr::str::change(str, 0, 0, expr);
7592}
7593
7594} // namespace std
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3234
constexpr bool is_done() const
Find out if substrings are running out.
Definition strexpr.h:3245
constexpr str_t next()
Get the next substring.
Definition strexpr.h:3254
constexpr 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:4923
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:3973
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition strexpr.h:3655
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition strexpr.h:3540
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition strexpr.h:3877
constexpr std::optional< R > strip_suffix(str_piece suffix) const
Get a string without a suffix if it ends with one.
Definition strexpr.h:4636
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition strexpr.h:4473
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition strexpr.h:3591
constexpr std::basic_string_view< D > to_sv() const noexcept
Convert to std::basic_string_view.
Definition strexpr.h:3374
constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by a whitespace...
Definition strexpr.h:4419
constexpr R trimmed_right() const
Get a string with whitespace removed on the right.
Definition strexpr.h:4719
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix,...
Definition strexpr.h:4962
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:3773
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition strexpr.h:4355
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition strexpr.h:3580
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition strexpr.h:3352
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:3729
constexpr str_piece until(str_piece pattern, size_t offset=0) const noexcept
Get the substring str_src from the beginning to the first found occurrence of the specified substring...
Definition strexpr.h:3502
constexpr R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition strexpr.h:4841
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4119
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition strexpr.h:3678
constexpr T split(str_piece delimiter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition strexpr.h:4340
constexpr 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:4824
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition strexpr.h:3467
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:4074
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition strexpr.h:4550
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3422
constexpr bool starts_with_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified substring followed by a whitespace ASCII character.
Definition strexpr.h:4364
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:3489
constexpr 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:4905
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:4582
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition strexpr.h:3551
constexpr R trimmed_left() const
Get a string with whitespace removed on the left.
Definition strexpr.h:4707
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:4499
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:4020
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:3992
constexpr R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition strexpr.h:4869
constexpr 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:4786
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:3836
constexpr R trimmed_prefix(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:4939
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:4410
constexpr 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:4805
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:3809
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:4033
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:3896
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:4061
std::optional< double > to_double() const noexcept
Convert string to double.
Definition strexpr.h:4164
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:3931
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:3745
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition strexpr.h:4199
constexpr std::optional< R > strip_prefix(str_piece prefix) const
Get a string without a prefix if it starts with one.
Definition strexpr.h:4598
constexpr bool operator!() const noexcept
Check for emptiness.
Definition strexpr.h:3509
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition strexpr.h:4695
constexpr size_t size() const
The size of the string in characters.
Definition strexpr.h:3363
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:3954
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:4087
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:3822
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:4005
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition strexpr.h:4506
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:3709
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:5022
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:4046
constexpr R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition strexpr.h:4749
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:3759
constexpr R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition strexpr.h:4855
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition strexpr.h:3610
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition strexpr.h:3600
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition strexpr.h:3864
constexpr R trimmed_suffix_ia(str_piece suffix) const
Returns a string with the end of the string removed if it ends with the specified suffix,...
Definition strexpr.h:5005
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3448
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition strexpr.h:3522
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition strexpr.h:4562
constexpr 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:4887
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition strexpr.h:3620
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Convert to std::basic_string.
Definition strexpr.h:3394
constexpr R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition strexpr.h:4764
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:4324
constexpr 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:4734
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:3850
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition strexpr.h:4458
constexpr std::optional< R > strip_prefix_ia(str_piece prefix) const
Get a string without a prefix if it starts with it, insensitive to ASCII characters.
Definition strexpr.h:4617
constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by one of the s...
Definition strexpr.h:4432
constexpr std::optional< R > strip_suffix_ia(str_piece suffix) const
Get a string without a suffix if it ends with one in case-insensitive ASCII characters.
Definition strexpr.h:4655
constexpr R trimmed_suffix(str_piece suffix) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:4985
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition strexpr.h:3337
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4152
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:3667
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4227
constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified substring followed by one of the specified characters.
Definition strexpr.h:4377
The concept of a string expression compatible with a given character type.
Definition strexpr.h:525
Concept of "String Expressions".
Definition strexpr.h:507
Base concept of string object.
Definition strexpr.h:311
Checks whether two types are compatible string types.
Definition strexpr.h:172
constexpr expr_if< A > e_if(bool c, const A &a)
Creating a conditional string expression expr_if.
Definition strexpr.h:1544
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Unary operator + for converting standard strings to string expressions.
Definition strexpr.h:7526
constexpr expr_spaces< uws, N > e_spcw()
Generates a string of N wchar_t spaces.
Definition strexpr.h:1104
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:2699
constexpr empty_expr< uws > eew
Empty string expression of type wchar_t.
Definition strexpr.h:862
constexpr expr_spaces< u8s, N > e_spca()
Generates a string of N char spaces.
Definition strexpr.h:1086
constexpr empty_expr< u32s > eeuu
Empty string expression of type char32_t.
Definition strexpr.h:874
constexpr expr_num< K, T > e_num(T t)
Convert an integer to a string expression.
Definition strexpr.h:1720
HexFlags
Flags for the e_hex function.
Definition strexpr.h:2527
constexpr auto e_hex(T v)
Creates an object that can be converted to a string expression that generates a hexadecimal represent...
Definition strexpr.h:2584
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:719
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:1218
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:991
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:2725
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:1466
constexpr expr_char< K > e_char(K s)
Generates a string of 1 given character.
Definition strexpr.h:918
constexpr empty_expr< u8s > eea
Empty string expression of type char.
Definition strexpr.h:850
constexpr expr_pad< K > e_c(size_t l, K s)
Generates a string of l characters s of type K.
Definition strexpr.h:1149
constexpr empty_expr< u16s > eeu
Empty string expression of type char16_t.
Definition strexpr.h:868
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:6140
constexpr auto e_int(T v)
Creates an object that is converted to a string expression that generates a string representation of ...
Definition strexpr.h:2383
constexpr empty_expr< u8s > eeb
Empty string expression of type char8_t.
Definition strexpr.h:856
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:5647
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:2803
@ Short
without leading zeroes
Definition strexpr.h:2528
Small namespace for standard string methods.
Definition strexpr.h:1600
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:7065
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:7098
std::basic_string< K, std::char_traits< K >, A > & make_ascii_upper(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change lowercase ASCII string characters (a-z) to uppercase.
Definition strexpr.h:7464
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:6992
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:7323
std::basic_string< K, std::char_traits< K >, A > & make_ascii_lower(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change uppercase ASCII string characters (A-Z) to lowercase.
Definition strexpr.h:7487
std::basic_string< K, std::char_traits< K >, A > & overwrite(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Rewrite the entire string with the given string expression, resizing the string if necessary.
Definition strexpr.h:7162
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:7133
Library namespace.
Definition sstring.cpp:12
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition strexpr.h:2870
@ Overflow
Переполнение, число не помещается в заданный тип
Definition strexpr.h:2873
@ Success
Успешно
Definition strexpr.h:2871
@ NotNumber
Вообще не число
Definition strexpr.h:2874
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition strexpr.h:2872
Some methods for working with standard strings.
Definition strexpr.h:7505
Generate a string based on the original one, replacing all ASCII uppercase letters (A-Z) with lowerca...
Definition strexpr.h:6945
Generate a string based on the original one, replacing all ASCII lowercase letters (a-z) with upperca...
Definition strexpr.h:6922
constexpr e_concat(G &&glue, Arg &&arg, Args &&...args)
Create a string expression concatenating the specified string expressions using the specified delimit...
Definition strexpr.h:6479
constexpr e_subst(const details::subst_params< K, PtLen, NParams > &subst, Args &&...args)
Creates a string expression that substitutes the values ​​of the passed string expressions into the s...
Definition strexpr.h:6701
constexpr e_vsubst(str_src< K > pattern, Args &&...args)
Creates a string expression that substitutes the values ​​of the passed string expressions into the s...
Definition strexpr.h:6800
An "empty" string expression.
Definition strexpr.h:835
Conditional selection string expression.
Definition strexpr.h:1345
Conditional selection string expression.
Definition strexpr.h:1411
Conditional selection string expression.
Definition strexpr.h:1254
Conditional selection string expression.
Definition strexpr.h:1283
A type of string expression that returns N specified characters.
Definition strexpr.h:1120
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Expression constructor.
Definition strexpr.h:6224
A string expression that generates a string replacing all occurrences of the given substring to strin...
Definition strexpr.h:5765
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Constructor.
Definition strexpr.h:5784
A string expression that generates a string replacing all occurrences of the given substring to anoth...
Definition strexpr.h:5659
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Constructor.
Definition strexpr.h:5677
A type of string expression that returns N specified characters.
Definition strexpr.h:1060
A type for using std::basic_string and std::basic_string_view as sources in string expressions.
Definition strexpr.h:1569
Base class for converting string expressions to standard strings.
Definition strexpr.h:659
A class that claims to refer to a null-terminated string.
Definition strexpr.h:5193
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:5256
constexpr str_src_nt(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5227
constexpr str_src_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition strexpr.h:5218
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5245
constexpr str_src_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5233
The simplest class of an immutable non-owning string.
Definition strexpr.h:5057
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5086
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition strexpr.h:5111
constexpr size_t length() const noexcept
Get the length of the string.
Definition strexpr.h:5097
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition strexpr.h:5140
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition strexpr.h:5151
constexpr str_src(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5076
constexpr bool is_same(str_src< K > other) const noexcept
Check if two objects point to the same string.
Definition strexpr.h:5120
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition strexpr.h:5091
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5104
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition strexpr.h:5164
constexpr bool is_part_of(str_src< K > other) const noexcept
Check if a string is part of another string.
Definition strexpr.h:5129
constexpr str_src(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5070
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:744
Template class for concatenating two string expressions into one using operator +
Definition strexpr.h:682