simstr 2.1.3
Yet another strings library
 
Loading...
Searching...
No Matches
strexpr.h
1/*
2 * ver. 2.1.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#include <array>
20
21#if defined __has_builtin
22# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
23# define HAS_BUILTIN_OVERFLOW
24# endif
25#endif
26
27#ifdef _MSC_VER
28#define _no_unique_address msvc::no_unique_address
29#define decl_empty_bases __declspec(empty_bases)
30#else
31#define _no_unique_address no_unique_address
32#define decl_empty_bases
33#endif
34
35#ifdef _MSC_VER
36/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
37Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
38Пишут, что баг исправлен, но видимо не до конца.
39Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
40/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
41Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
42They write that the bug has been fixed, but apparently not completely.
43Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
44#define SS_CONSTEVAL constexpr
45#else
46#define SS_CONSTEVAL consteval
47#endif
48
53namespace simstr {
54
55// Выводим типы для 16 и 32 битных символов в зависимости от размера wchar_t
56// Infer types for 16 and 32 bit characters depending on the size of wchar_t
57inline constexpr bool wchar_is_u16 = sizeof(wchar_t) == sizeof(char16_t);
58
59using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
60
61inline wchar_type* to_w(wchar_t* p) {
62 return (reinterpret_cast<wchar_type*>(p));
63}
64
65inline const wchar_type* to_w(const wchar_t* p) {
66 return (reinterpret_cast<const wchar_type*>(p));
67}
68
69inline wchar_t* from_w(wchar_type* p) {
70 return (reinterpret_cast<wchar_t*>(p));
71}
72
73inline const wchar_t* from_w(const wchar_type* p) {
74 return (reinterpret_cast<const wchar_t*>(p));
75}
76
77using u8s = char;
78using ubs = char8_t;
79using uws = wchar_t;
80using u16s = char16_t;
81using u32s = char32_t;
82
83using uu8s = std::make_unsigned<u8s>::type;
84
85template<typename T, typename K = void, typename... Types>
86struct is_one_of_type {
87 static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
88};
89template<typename T>
90struct is_one_of_type<T, void> : std::false_type {};
91
92template<typename K>
93concept is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
94
95template<typename K>
96concept is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
97
98template<is_one_of_std_char_v From>
99auto to_one_of_std_char(From* from) {
100 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
101 return from;
102 } else if constexpr (std::is_same_v<From, ubs>) {
103 return reinterpret_cast<u8s*>(from);
104 } else {
105 return from_w(from);
106 }
107}
108
109template<is_one_of_std_char_v From>
110auto to_one_of_std_char(const From* from) {
111 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
112 return from;
113 } else if constexpr (std::is_same_v<From, ubs>) {
114 return reinterpret_cast<const u8s*>(from);
115 } else {
116 return from_w(from);
117 }
118}
119
120template<typename K>
121struct to_std_char_type : std::type_identity<K>{};
122
123template<>
124struct to_std_char_type<char8_t>{
125 using type = char;
126};
127
128template<>
129struct to_std_char_type<char16_t>{
130 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
131};
132
133template<>
134struct to_std_char_type<char32_t>{
135 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
136};
137
138template<typename K>
139using to_std_char_t = typename to_std_char_type<K>::type;
140
141template<typename K>
142struct to_base_char_type : std::type_identity<K>{};
143
144template<>
145struct to_base_char_type<char8_t>{
146 using type = char;
147};
148
149template<>
150struct to_base_char_type<wchar_t>{
151 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), char16_t, char32_t>;
152};
153
154template<typename K>
155using to_base_char_t = typename to_base_char_type<K>::type;
156
172template<typename K1, typename K2>
173concept is_equal_str_type_v = sizeof(K1) == sizeof(K2) && is_one_of_char_v<K1> && is_one_of_char_v<K2>;
174
175/*
176Вспомогательные шаблоны для определения строковых литералов.
177Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
178Если пишем
179template<size_t N>
180void func(const char(&lit)[N]);
181то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
182
183// Выделили место под символы
184char buf[100];
185// Как то наполнили буфер, допустим, до половины.
186
187stringa text = buf;
188Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
189для строкового литерала, в text запишется просто указатель на buf и длина 100.
190
191Поэтому такие параметры объявляем как
192template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
193void func(T&& lit);
194
195Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
196и выражение с параметром char[100] - не скомпилируется.
197
198Helper templates for defining string literals.
199They are used to limit types in function parameters strictly as `const K(&)[N]`
200If we write
201template<size_t N>
202void func(const char(&lit)[N]);
203then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
204
205// Allocate space for symbols
206char buf[100];
207// Somehow the buffer was filled, say, to half.
208
209stringa text = buf;
210Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
211for a string literal, text will simply contain a pointer to buf and length 100.
212
213Therefore, we declare such parameters as
214template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
215void func(T&& lit);
216
217Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
218and an expression with the char[100] parameter will not compile.
219*/
220
221template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
222// Для правильных типов параметров есть определение, в виде специализации шаблона
223// There is a definition for the correct parameter types, in the form of a template specialization
224template<is_one_of_char_v T, size_t N>
225struct const_lit<const T(&)[N]> {
226 using symb_type = T;
227 constexpr static size_t Count = N;
228};
229
230template<typename T>
231concept is_const_lit_v = requires {
232 typename const_lit<T>::symb_type;
233};
234
235// Тут ещё дополнительно ограничиваем тип литерала
236// Here we further restrict the type of the literal
237template<typename K, typename T> struct const_lit_for;
238
239template<typename K, is_equal_str_type_v<K> P, size_t N>
240struct const_lit_for<K, const P(&)[N]> {
241 constexpr static size_t Count = N;
242};
243
244template<typename K, size_t N>
245class const_lit_to_array {
246
247 template<size_t Idx>
248 constexpr size_t find(K s) const {
249 if constexpr (Idx < N) {
250 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
251 }
252 return -1;
253 }
254
255 template<size_t Idx>
256 constexpr bool exist(K s) const {
257 if constexpr (Idx < N) {
258 return s == symbols_[Idx] || exist<Idx + 1>(s);
259 }
260 return false;
261 }
262public:
263 const K (&symbols_)[N + 1];
264
265 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
266 constexpr const_lit_to_array(T&& s)
267 : symbols_((const K(&)[M])s) {}
268
269 constexpr bool contain(K s) const {
270 return exist<0>(s);
271 }
272 constexpr size_t index_of(K s) const {
273 return find<0>(s);
274 }
275};
276
277template<typename A>
278concept StrTypeCommon = requires(const A& a) {
279 typename std::remove_cvref_t<A>::symb_type;
280 { a.is_empty() } -> std::same_as<bool>;
281 { a.length() } -> std::convertible_to<size_t>;
282 { a.symbols() };
283};
284
285template<typename A>
286concept HasSymbType = requires {
287 typename std::remove_cvref_t<A>::symb_type;
288};
289
311template<typename A, typename K>
312concept StrType = StrTypeCommon<A> && requires(const A& a) {
313 { a.symbols() } -> std::same_as<const K*>;
314} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
315
316template<typename T> struct is_std_string_source : std::false_type{};
317
318template<typename K, typename A>
319struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
320
321template<typename K>
322struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
323
324template<typename T>
325concept is_std_string_source_v = is_std_string_source<T>::value;
326
327template<typename T>
328concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
329
330template<typename T, typename K>
331concept StdStrSourceForType = StdStrSource<T> && is_equal_str_type_v<K, typename T::value_type>;
332
333template<typename T>
334concept StrSource = StdStrSource<T> || is_const_lit_v<T> || StrTypeCommon<T>;
335
336template<typename T>
337concept StrSourceNoLiteral = StdStrSource<T> || StrTypeCommon<T>;
338
339template<typename T> struct is_std_string : std::false_type{};
340
341template<typename K, typename A>
342struct is_std_string<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
343
344template<typename T>
345concept is_std_string_v = is_std_string<T>::value;
346template<typename T, typename K>
347concept StdStringForType = is_std_string_v<T> && is_equal_str_type_v<K, typename T::value_type>;
348
349template<typename T>
350struct symb_type_from_src {
351 using type = void;
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, StrExprForType<K> 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> requires (std::is_constructible_v<B, 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<ubs> 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 (symb_type*)a.place((atype*)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, S>> {
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 mutable size_t len_;
1179 constexpr expr_repeat_expr(size_t repeat, const A& expr) : repeat_(repeat), expr_(expr){}
1180 constexpr size_t length() const noexcept {
1181 if (repeat_) {
1182 len_ = expr_.length();
1183 return repeat_ * len_;
1184 }
1185 return len_ = 0;
1186 }
1187 constexpr symb_type* place(symb_type* p) const noexcept {
1188 if (repeat_ && len_) {
1189 symb_type* start = p;
1190 p = expr_.place(p);
1191 for (size_t i = repeat_; --i; ) {
1192 std::char_traits<symb_type>::copy(p, start, len_);
1193 p += len_;
1194 }
1195 }
1196 return p;
1197 }
1198};
1199
1213template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
1214constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
1215 return { l, s };
1216}
1217
1231template<StrExpr A>
1232constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
1233 return { l, s };
1234}
1235
1249template<StrExpr A, StrExprForType<typename A::symb_type> B>
1250struct expr_choice : expr_to_std_string<expr_choice<A, B>> {
1251 using symb_type = typename A::symb_type;
1252 using my_type = expr_choice<A, B>;
1253 const A& a;
1254 const B& b;
1255 bool choice;
1256
1257 constexpr expr_choice(const A& _a, const B& _b, bool _choice) : a(_a), b(_b), choice(_choice){}
1258
1259 constexpr size_t length() const noexcept {
1260 return choice ? a.length() : b.length();
1261 }
1262 constexpr symb_type* place(symb_type* ptr) const noexcept {
1263 return choice ? a.place(ptr) : (symb_type*)b.place((typename B::symb_type*)ptr);
1264 }
1265};
1266
1278template<StrExpr A>
1279struct expr_if : expr_to_std_string<expr_if<A>> {
1280 using symb_type = typename A::symb_type;
1281 using my_type = expr_if<A>;
1282 const A& a;
1283 bool choice;
1284 constexpr expr_if(const A& _a, bool _choice) : a(_a), choice(_choice){}
1285
1286 constexpr size_t length() const noexcept {
1287 return choice ? a.length() : 0;
1288 }
1289 constexpr symb_type* place(symb_type* ptr) const noexcept {
1290 return choice ? a.place(ptr) : ptr;
1291 }
1292};
1293
1340template<typename L, StrExprForType<L> A, size_t N, bool Compare>
1341struct expr_choice_one_lit : expr_to_std_string<expr_choice_one_lit<L, A, N, Compare>> {
1342 using symb_type = L;
1343 const symb_type (&str)[N + 1];
1344 const A& a;
1345 bool choice;
1346 constexpr expr_choice_one_lit(const symb_type (&_str)[N + 1], const A& _a, bool _choice) : str(_str), a(_a), choice(_choice){}
1347
1348 constexpr size_t length() const noexcept {
1349 return choice == Compare ? a.length() : N;
1350 }
1351 constexpr symb_type* place(symb_type* ptr) const noexcept {
1352 if (choice == Compare) {
1353 return (L*)a.place((typename A::symb_type*)ptr);
1354 }
1355 if constexpr (N != 0) {
1356 std::char_traits<symb_type>::copy(ptr, str, N);
1357 }
1358 return ptr + N;
1359 }
1360};
1361
1406template<typename K, size_t N, typename P, size_t M>
1407struct expr_choice_two_lit : expr_to_std_string<expr_choice_two_lit<K, N, P, M>> {
1408 using symb_type = K;
1409 const K (&str_a)[N + 1];
1410 const P (&str_b)[M + 1];
1411 bool choice;
1412 constexpr expr_choice_two_lit(const K(&_str_a)[N + 1], const P(&_str_b)[M + 1], bool _choice)
1413 : str_a(_str_a), str_b(_str_b), choice(_choice){}
1414
1415 constexpr size_t length() const noexcept {
1416 return choice ? N : M;
1417 }
1418 constexpr symb_type* place(symb_type* ptr) const noexcept {
1419 if (choice) {
1420 if constexpr (N != 0) {
1421 std::char_traits<symb_type>::copy(ptr, str_a, N);
1422 }
1423 return ptr + N;
1424 }
1425 if constexpr (M != 0) {
1426 std::char_traits<symb_type>::copy(ptr, (const K(&)[M + 1])str_b, M);
1427 }
1428 return ptr + M;
1429 }
1430};
1431
1461template<StrExpr A, StrExprForType<typename A::symb_type> B>
1462constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
1463 return {a, b, c};
1464}
1465
1471template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1472constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
1473 return {str, a, c};
1474}
1475
1481template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1482constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
1483 return {str, a, c};
1484}
1485
1490template<typename T, typename L, typename K = typename const_lit<T>::symb_type, typename P = typename const_lit<L>::symb_type,
1491 size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
1492 requires is_equal_str_type_v<K, P>
1493constexpr expr_choice_two_lit<K, N -1, P, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
1494 return {str_a, str_b, c};
1495}
1496
1539template<StrExpr A>
1540constexpr expr_if<A> e_if(bool c, const A& a) {
1541 return {a, c};
1542}
1543
1548template<typename T, size_t N = const_lit<T>::Count>
1549constexpr auto e_if(bool c, T&& str) {
1550 using K = typename const_lit<T>::symb_type;
1551 const K empty[1] = {0};
1552 return expr_choice_two_lit<K, N - 1, K, 0>{str, empty, c};
1553}
1554
1564template<typename K, StdStrSourceForType<K> T>
1565struct expr_stdstr {
1566 using symb_type = K;
1567 const T& t_;
1568
1569 expr_stdstr(const T& t) : t_(t){}
1570
1571 constexpr size_t length() const noexcept {
1572 return t_.size();
1573 }
1574 constexpr symb_type* place(symb_type* p) const noexcept {
1575 size_t s = t_.size();
1576 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
1577 return p + s;
1578 }
1579};
1580
1591template<typename K, StdStrSource T>
1592struct convert_to_strexpr<K, T> {
1593 using type = expr_stdstr<K, T>;
1594};
1595
1596namespace str {
1597constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
1598} // namespace str
1599
1600template<typename K>
1601std::char_traits<K> char_traits_selector(...);
1602
1603template<typename K>
1604using ch_traits = decltype(char_traits_selector<K>(int(0)));
1605
1606template<typename T>
1607concept FromIntNumber =
1608 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;
1609
1610template<typename T>
1611concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1612
1613template<typename K, bool I, typename T>
1614struct need_sign { // NOLINT
1615 bool negate;
1616 std::make_unsigned_t<T> val;
1617 constexpr need_sign(T t) : negate(t < 0), val(t < 0 ? std::make_unsigned_t<T>{} - t : t) {}
1618 constexpr void after(K*& ptr) {
1619 if (negate)
1620 *--ptr = '-';
1621 }
1622};
1623
1624template<typename K, typename T>
1625struct need_sign<K, false, T> {
1626 T val;
1627 constexpr need_sign(T t) : val(t){}
1628 constexpr void after(K*&) {}
1629};
1630
1631template<typename K, typename T>
1632constexpr size_t fromInt(K* bufEnd, T val) {
1633 const char* twoDigit =
1634 "0001020304050607080910111213141516171819"
1635 "2021222324252627282930313233343536373839"
1636 "4041424344454647484950515253545556575859"
1637 "6061626364656667686970717273747576777879"
1638 "8081828384858687888990919293949596979899";
1639 if (val) {
1640 need_sign<K, std::is_signed_v<T>, T> store(val);
1641 K* itr = bufEnd;
1642 while (store.val >= 100) {
1643 const char* ptr = twoDigit + (store.val % 100) * 2;
1644 *--itr = static_cast<K>(ptr[1]);
1645 *--itr = static_cast<K>(ptr[0]);
1646 store.val /= 100;
1647 }
1648 if (store.val < 10) {
1649 *--itr = static_cast<K>('0' + store.val);
1650 } else {
1651 const char* ptr = twoDigit + store.val * 2;
1652 *--itr = static_cast<K>(ptr[1]);
1653 *--itr = static_cast<K>(ptr[0]);
1654 }
1655 store.after(itr);
1656 return size_t(bufEnd - itr);
1657 }
1658 bufEnd[-1] = '0';
1659 return 1;
1660}
1661
1662template<typename K, typename T>
1663struct expr_num : expr_to_std_string<expr_num<K, T>> {
1664 using symb_type = K;
1665 using my_type = expr_num<K, T>;
1666
1667 enum { bufSize = 24 };
1668 mutable K buf[bufSize];
1669 mutable T value;
1670
1671 constexpr expr_num(T t) : value(t) {}
1672 constexpr expr_num(expr_num&& t) noexcept : value(t.value) {}
1673
1674 constexpr size_t length() const noexcept {
1675 value = static_cast<T>(fromInt(buf + bufSize, value));
1676 return static_cast<size_t>(value);
1677 }
1678 constexpr K* place(K* ptr) const noexcept {
1679 size_t len = static_cast<size_t>(value);
1680 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1681 return ptr + len;
1682 }
1683};
1684
1695template<typename K, FromIntNumber T>
1696struct convert_to_strexpr<K, T> {
1697 using type = expr_num<K, T>;
1698};
1699
1715template<typename K, FromIntNumber T>
1716constexpr expr_num<K, T> e_num(T t) {
1717 return {t};
1718}
1719
1720namespace f{
1721
1722enum class int_align { none, left, right, center };
1723enum class int_plus_sign {none, plus, space};
1724enum class int_prefix {none, lcase, ucase};
1725
1726enum fmt_kinds : unsigned {
1727 fmt_none = 0,
1728 fmt_align = 1,
1729 fmt_width = 2,
1730 fmt_fill = 4,
1731 fmt_sign = 8,
1732 fmt_upper = 16,
1733 fmt_prefix = 32,
1734 fmt_zero = 64,
1735};
1736
1737template<typename A, typename B, unsigned Kind>
1738struct fmt_info {
1739 using a_t = A;
1740 using b_t = B;
1741 inline static constexpr unsigned kind = Kind;
1742};
1743
1744struct fmt_default {
1745 inline static constexpr unsigned kind = fmt_none;
1746};
1747
1748template<int_align A>
1749struct align_info{
1750 inline static constexpr unsigned kind = fmt_align;
1751};
1752
1753template<size_t Width = 0>
1754struct width_info {
1755 inline static constexpr unsigned kind = fmt_width;
1756};
1757
1758template<unsigned Fill = ' '>
1759struct fill_info {
1760 inline static constexpr unsigned kind = fmt_fill;
1761};
1762
1763template<int_plus_sign S>
1764struct sign_info {
1765 inline static constexpr unsigned kind = fmt_sign;
1766};
1767
1768struct upper_info {
1769 inline static constexpr unsigned kind = fmt_upper;
1770};
1771
1772template<int_prefix>
1773struct prefix_info {
1774 inline static constexpr unsigned kind = fmt_prefix;
1775};
1776
1777struct zero_info {
1778 inline static constexpr unsigned kind = fmt_zero;
1779};
1780
1781template<typename T>
1782concept FmtParam = requires {
1783 {std::remove_cvref_t<T>::kind} -> std::same_as<const unsigned&>;
1784};
1785
1786template<FmtParam A, FmtParam B>
1787requires((A::kind & B::kind) == 0)
1788constexpr auto operator | (const A&, const B&) {
1789 return fmt_info<A, B, A::kind | B::kind>{};
1790}
1791
1792inline constexpr align_info<int_align::left> l;
1793inline constexpr align_info<int_align::right> r;
1794inline constexpr align_info<int_align::center> c;
1795
1796template<size_t W>
1797inline constexpr width_info<W> w;
1798
1799template<unsigned F>
1800inline constexpr fill_info<F> f;
1801
1802inline constexpr sign_info<int_plus_sign::plus> sp;
1803inline constexpr sign_info<int_plus_sign::space> ss;
1804inline constexpr upper_info u;
1805inline constexpr prefix_info<int_prefix::lcase> p;
1806inline constexpr prefix_info<int_prefix::ucase> P;
1807inline constexpr zero_info z;
1808
1809inline constexpr width_info<unsigned(-1)> wp;
1810inline constexpr fmt_default df;
1811
1812template<typename T>
1813struct extract_align : std::false_type {
1814 inline static constexpr int_align align = int_align::none;
1815};
1816
1817template<int_align A>
1818struct extract_align<align_info<A>> : std::true_type {
1819 inline static constexpr int_align align = A;
1820};
1821
1822template<typename A, typename B, unsigned U>
1823struct extract_align<fmt_info<A, B, U>> {
1824 inline static constexpr bool in_a = extract_align<A>::value, in_b = extract_align<B>::value, value = in_a | in_b;
1825 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;
1826};
1827
1828template<typename T>
1829struct extract_width : std::false_type {
1830 inline static constexpr size_t width = 0;
1831};
1832
1833template<size_t W>
1834struct extract_width<width_info<W>> : std::true_type {
1835 inline static constexpr size_t width = W;
1836};
1837
1838template<typename A, typename B, unsigned U>
1839struct extract_width<fmt_info<A, B, U>> {
1840 inline static constexpr bool in_a = extract_width<A>::value, in_b = extract_width<B>::value, value = in_a | in_b;
1841 inline static constexpr size_t width = extract_width<std::conditional_t<in_a, A, std::conditional_t<in_b, B, width_info<0>>>>::width;
1842};
1843
1844template<typename T>
1845struct extract_fill : std::false_type {
1846 inline static constexpr unsigned fill = ' ';
1847};
1848
1849template<unsigned F>
1850struct extract_fill<fill_info<F>> : std::true_type {
1851 inline static constexpr unsigned fill = F;
1852};
1853
1854template<typename A, typename B, unsigned U>
1855struct extract_fill<fmt_info<A, B, U>> {
1856 inline static constexpr bool in_a = extract_fill<A>::value, in_b = extract_fill<B>::value, value = in_a | in_b;
1857 inline static constexpr unsigned fill = extract_fill<std::conditional_t<in_a, A, std::conditional_t<in_b, B, fill_info<' '>>>>::fill;
1858};
1859
1860template<typename T>
1861struct extract_sign : std::false_type {
1862 inline static constexpr int_plus_sign sign = int_plus_sign::none;
1863};
1864
1865template<int_plus_sign S>
1866struct extract_sign<sign_info<S>> : std::true_type {
1867 inline static constexpr int_plus_sign sign = S;
1868};
1869
1870template<typename A, typename B, unsigned U>
1871struct extract_sign<fmt_info<A, B, U>> {
1872 inline static constexpr bool in_a = extract_sign<A>::value, in_b = extract_sign<B>::value, value = in_a | in_b;
1873 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;
1874};
1875
1876template<typename T>
1877struct extract_upper : std::false_type {};
1878
1879template<>
1880struct extract_upper<upper_info> : std::true_type {};
1881
1882template<typename A, typename B, unsigned U>
1883struct extract_upper<fmt_info<A, B, U>> {
1884 inline static constexpr bool in_a = extract_upper<A>::value, in_b = extract_upper<B>::value,
1885 value = extract_upper<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1886};
1887
1888template<typename T>
1889struct extract_prefix : std::false_type{
1890 inline static constexpr int_prefix prefix = int_prefix::none;
1891};
1892
1893template<int_prefix P>
1894struct extract_prefix<prefix_info<P>> : std::true_type {
1895 inline static constexpr int_prefix prefix = P;
1896};
1897
1898template<typename A, typename B, unsigned U>
1899struct extract_prefix<fmt_info<A, B, U>> {
1900 inline static constexpr bool in_a = extract_prefix<A>::value, in_b = extract_prefix<B>::value, value = in_a | in_b;
1901 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;
1902};
1903
1904template<typename T>
1905struct extract_zero : std::false_type{};
1906
1907template<>
1908struct extract_zero<zero_info> : std::true_type {};
1909
1910template<typename A, typename B, unsigned U>
1911struct extract_zero<fmt_info<A, B, U>> {
1912 inline static constexpr bool in_a = extract_zero<A>::value, in_b = extract_zero<B>::value,
1913 value = extract_zero<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1914};
1915
1916template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1917struct fmt_params {
1918 inline static constexpr int_align align = Align;
1919 inline static constexpr unsigned width = Width;
1920 inline static constexpr unsigned fill = Fill;
1921 inline static constexpr int_plus_sign sign = Sign;
1922 inline static constexpr bool upper = Upper;
1923 inline static constexpr int_prefix prefix = Prefix;
1924 inline static constexpr bool zero = Zero;
1925};
1926
1927template<FmtParam T>
1928using p_to_fmt_t = fmt_params<
1929 extract_align<T>::align,
1930 extract_width<T>::width,
1931 extract_fill<T>::fill,
1932 extract_sign<T>::sign,
1933 extract_upper<T>::value,
1934 extract_prefix<T>::prefix,
1935 extract_zero<T>::value>;
1936
1937template<FmtParam T>
1938using to_fmt_t = p_to_fmt_t<std::remove_cvref_t<T>>;
1939
1940template<typename T>
1941struct is_fmt_params : std::false_type{};
1942
1943template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1944struct is_fmt_params<fmt_params<Align, Width, Fill, Sign, Upper, Prefix, Zero>> : std::true_type{};
1945
1946template<typename T>
1947concept FmtParamSet = is_fmt_params<T>::value;
1948
1949struct fmt_end{};
1950
1951template<f::FmtParam F>
1952constexpr auto operator|(const F& f, fmt_end) {
1953 return f;
1954}
1955
1956template<char C = 0, char...Chars>
1957constexpr auto parse_fmt_symbol();
1958
1959template<unsigned N, char C = 'Z', char...Chars>
1960constexpr auto parse_width() {
1961 if constexpr (C >= '0' && C <= '9') {
1962 return parse_width<N * 10 + C - '0', Chars...>();
1963 } else {
1964 return w<N> | parse_fmt_symbol<C, Chars...>();
1965 }
1966}
1967
1968template<unsigned N, char C = 'Z', char...Chars>
1969constexpr auto parse_fill() {
1970 if constexpr (C >= '0' && C <= '9') {
1971 return parse_fill<N * 16 + C - '0', Chars...>();
1972 } else if constexpr (C >= 'a' && C <= 'f') {
1973 return parse_fill<N * 16 + C - 'a' + 10, Chars...>();
1974 } else if constexpr (C >= 'A' && C <= 'F') {
1975 return parse_fill<N * 16 + C - 'A' + 10, Chars...>();
1976 } else {
1977 return f<N> | parse_fmt_symbol<C, Chars...>();
1978 }
1979}
1980
1981template<char C, char...Chars>
1982constexpr auto parse_fmt_symbol() {
1983 if constexpr (C == '0') {
1984 return z | parse_fmt_symbol<Chars...>();
1985 } else if constexpr (C == 'a') {
1986 return p | parse_fmt_symbol<Chars...>();
1987 } else if constexpr (C == 'A') {
1988 return P | parse_fmt_symbol<Chars...>();
1989 } else if constexpr (C == 'b') {
1990 return l | parse_fmt_symbol<Chars...>();
1991 } else if constexpr (C == 'c') {
1992 return c | parse_fmt_symbol<Chars...>();
1993 } else if constexpr (C == 'd') {
1994 return r | parse_fmt_symbol<Chars...>();
1995 } else if constexpr (C == 'e') {
1996 return sp | parse_fmt_symbol<Chars...>();
1997 } else if constexpr (C == 'f') {
1998 return ss | parse_fmt_symbol<Chars...>();
1999 } else if constexpr (C == 'E') {
2000 return u | parse_fmt_symbol<Chars...>();
2001 } else if constexpr (C == '\'') {
2002 return parse_fmt_symbol<Chars...>();
2003 } else if constexpr (C == 'F') {
2004 return parse_fill<0, Chars...>();
2005 } else if constexpr (C >= '1' && C <= '9') {
2006 return parse_width<C - '0', Chars...>();
2007 } else {
2008 return fmt_end{};
2009 }
2010}
2011
2012template<unsigned R, typename T>
2013struct fmt_radix_info {};
2014
2015template<unsigned N, char C = 0, char...Chars>
2016constexpr auto parse_radix() {
2017 if constexpr (C >='0' && C <= '9') {
2018 return parse_radix<N * 10 + C - '0', Chars...>();
2019 } else if constexpr (C == 0) {
2020 return fmt_radix_info<N, to_fmt_t<fmt_default>>{};
2021 } else {
2022 return fmt_radix_info<N, decltype(parse_fmt_symbol<C, Chars...>())>{};
2023 }
2024};
2025
2026template<char C1, char C2, char C3, char...Chars>
2027constexpr auto skip_0x() {
2028 static_assert(C1 == '0' && C2 == 'x' && "Fmt symbols must begin with 0x");
2029 static_assert(C3 >= '1' && C3 <= '9' && "Radix must begin with 1-9");
2030 return parse_radix<C3 - '0', Chars...>();
2031}
2032
2033} // namespace f
2034
2035template<typename K, bool Ucase>
2036inline 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'),
2037 K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'), K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'),
2038 K(Ucase ? 'F' : 'f'), K(Ucase ? 'G' : 'g'), K(Ucase ? 'H' : 'h'), K(Ucase ? 'I' : 'i'), K(Ucase ? 'J' : 'j'),
2039 K(Ucase ? 'K' : 'k'), K(Ucase ? 'L' : 'l'), K(Ucase ? 'M' : 'm'), K(Ucase ? 'N' : 'n'), K(Ucase ? 'O' : 'o'),
2040 K(Ucase ? 'P' : 'p'), K(Ucase ? 'Q' : 'q'), K(Ucase ? 'R' : 'r'), K(Ucase ? 'S' : 's'), K(Ucase ? 'T' : 't'),
2041 K(Ucase ? 'U' : 'u'), K(Ucase ? 'V' : 'v'), K(Ucase ? 'W' : 'w'), K(Ucase ? 'X' : 'x'), K(Ucase ? 'Y' : 'y'),
2042 K(Ucase ? 'Z' : 'z'),
2043};
2044
2045template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2046struct expr_integer_src {
2047 T value_;
2048 unsigned width_{};
2049 constexpr expr_integer_src(T v) : value_(v){}
2050 constexpr expr_integer_src(T v, unsigned w) : value_(v), width_(w){}
2051
2052 template<is_std_string_v S>
2053 operator S() const;
2054
2055 template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2056 operator S() const;
2057};
2058
2059template<is_one_of_char_v K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2060requires (Radix > 1 && Radix <= 36)
2061struct expr_integer : expr_to_std_string<expr_integer<K, T, Radix, FP>> {
2062 using symb_type = K;
2063
2064 enum { bufSize = 64 };
2065 mutable K buf[bufSize];
2066 mutable T value_;
2067 unsigned width_{};
2068
2069 mutable bool negate_{};
2070
2071 constexpr expr_integer(T t) : value_(t){}
2072 constexpr expr_integer(T t, unsigned w) : value_(t), width_(w){}
2073 constexpr expr_integer(const expr_integer_src<T, Radix, FP>& v) : value_(v.value_), width_(v.width_){}
2074
2075 constexpr expr_integer(expr_integer&& t) noexcept : value_(t.value_), width_(t.width_){}
2076
2077 constexpr size_t length() const noexcept {
2078 K* bufEnd = std::end(buf), *itr = bufEnd;
2079
2080 if (value_) {
2081 need_sign<K, std::is_signed_v<T>, T> store(value_);
2082 while (store.val) {
2083 *--itr = digits_symbols<K, FP::upper>[store.val % Radix];
2084 store.val /= Radix;
2085 }
2086 if constexpr (std::is_signed_v<T>) {
2087 negate_ = store.negate;
2088 }
2089 } else {
2090 *--itr = digits_symbols<K, FP::upper>[0];
2091 }
2092 size_t len = bufEnd - itr;
2093 value_ = T(len);
2094 if constexpr (std::is_signed_v<T>) {
2095 if constexpr (FP::sign != f::int_plus_sign::none) {
2096 len++;
2097 } else {
2098 if (negate_) {
2099 len++;
2100 }
2101 }
2102 }
2103 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2104 len++; // add 0
2105 if constexpr (Radix != 8) { // for octo just add 0,
2106 len++; // for other b or x
2107 }
2108 }
2109 if constexpr (FP::width == unsigned(-1)) {
2110 return std::max<size_t>(width_, len);
2111 } else {
2112 return std::max<size_t>(FP::width, len);
2113 }
2114 }
2115 constexpr K* place(K* ptr) const noexcept {
2116 size_t len = static_cast<size_t>(value_), all_len = len;
2117 if constexpr (std::is_signed_v<T>) {
2118 if constexpr (FP::sign != f::int_plus_sign::none) {
2119 all_len++;
2120 } else {
2121 if (negate_) {
2122 all_len++;
2123 }
2124 }
2125 }
2126 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2127 all_len++; // add 0
2128 if constexpr (Radix != 8) { // for octo just add 0,
2129 all_len++; // for other b or x
2130 }
2131 }
2132 if constexpr (FP::zero) {
2133 if constexpr (std::is_signed_v<T>) {
2134 if (negate_) {
2135 *ptr++ = K('-');
2136 } else {
2137 if constexpr (FP::sign == f::int_plus_sign::plus) {
2138 *ptr++ = K('+');
2139 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2140 *ptr++ = K(' ');
2141 }
2142 }
2143 }
2144 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2145 *ptr++ = K('0');
2146 if constexpr (Radix == 2) {
2147 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2148 } else if constexpr (Radix == 16) {
2149 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2150 }
2151 }
2152 size_t before = 0;
2153 if constexpr (FP::width == unsigned(-1)) {
2154 if (width_ > all_len) {
2155 before = width_ - all_len;
2156 }
2157 } else {
2158 if (FP::width > all_len) {
2159 before = FP::width - all_len;
2160 }
2161 }
2162 if (before) {
2163 ch_traits<K>::assign(ptr, before, K('0'));
2164 ptr += before;
2165 }
2166 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2167 ptr += len;
2168 } else {
2169 size_t before = 0, after = 0;
2170 if constexpr (FP::width == unsigned(-1)) {
2171 if (width_ > all_len) {
2172 if constexpr (FP::align == f::int_align::left) {
2173 after = width_ - all_len;
2174 } else if constexpr (FP::align == f::int_align::center) {
2175 before = (width_ - all_len) / 2;
2176 after = width_ - all_len - before;
2177 } else {
2178 before = width_ - all_len;
2179 }
2180 }
2181 } else {
2182 if (FP::width > all_len) {
2183 if constexpr (FP::align == f::int_align::left) {
2184 after = FP::width - all_len;
2185 } else if constexpr (FP::align == f::int_align::center) {
2186 before = (FP::width - all_len) / 2;
2187 after = FP::width - all_len - before;
2188 } else {
2189 before = FP::width - all_len;
2190 }
2191 }
2192 }
2193 if (before) {
2194 ch_traits<K>::assign(ptr, before, K(FP::fill));
2195 ptr += before;
2196 }
2197 if constexpr (std::is_signed_v<T>) {
2198 if (negate_) {
2199 *ptr++ = K('-');
2200 } else {
2201 if constexpr (FP::sign == f::int_plus_sign::plus) {
2202 *ptr++ = K('+');
2203 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2204 *ptr++ = K(' ');
2205 }
2206 }
2207 }
2208 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2209 *ptr++ = K('0');
2210 if constexpr (Radix == 2) {
2211 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2212 } else if constexpr (Radix == 16) {
2213 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2214 }
2215 }
2216 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2217 ptr += len;
2218
2219 if (after) {
2220 ch_traits<K>::assign(ptr, after, K(FP::fill));
2221 ptr += after;
2222 }
2223 }
2224 return ptr;
2225 }
2226};
2227
2228template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2229template<is_std_string_v S>
2230expr_integer_src<T, Radix, FP>::operator S() const {
2231 using st = typename S::value_type;
2232 using Al = typename S::allocator_type;
2233 return to_std_string<st, Al>(expr_integer<st, T, Radix, FP>{value_, width_});
2234}
2235
2236template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2237template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2238expr_integer_src<T, Radix, FP>::operator S() const {
2239 using st = typename S::symb_type;
2240 return S{expr_integer<st, T, Radix, FP>{value_, width_}};
2241}
2242
2243template<typename K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2244struct convert_to_strexpr<K, expr_integer_src<T, Radix, FP>> {
2245 using type = expr_integer<K, T, Radix, FP>;
2246};
2247
2248template<unsigned R, typename T, typename F>
2249struct flags_checker {
2250 using val_t = T;
2251 using flags_t = f::to_fmt_t<F>;
2252 inline static constexpr unsigned Radix = R;
2253};
2254
2255template<typename T, bool ArgWidth>
2256concept good_int_flags =
2257 T::Radix > 1 && T::Radix <= 36
2258 // Должно быть задано только или выравнивание, или заполнение нулём.
2259 // Only either alignment or zero-padding must be specified.
2260 && (T::flags_t::align == f::int_align::none || !T::flags_t::zero)
2261 // Флаг вывода знака должен задаваться только для знаковых чисел
2262 // The sign output flag should only be set for signed numbers.
2263 && (std::is_signed_v<typename T::val_t> || T::flags_t::sign == f::int_plus_sign::none)
2264 // Неверное поле ширины
2265 // Invalid width field
2266 && (T::flags_t::width == unsigned(-1)) == ArgWidth
2267 // Префикс может быть только при основании 2, 8, 16
2268 // Prefix may by only for radix 2, 8, 16
2269 && (T::flags_t::prefix == f::int_prefix::none || T::Radix == 2 || T::Radix == 8 || T::Radix == 16);
2270
2378template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, false>
2379constexpr auto e_int(T v) {
2380 using fmt_param = f::to_fmt_t<decltype(fp)>;
2381 return expr_integer_src<T, R, fmt_param>{v};
2382}
2383
2387template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, true>
2388constexpr auto e_int(T v, unsigned w) {
2389 using fmt_param = f::to_fmt_t<decltype(fp)>;
2390 return expr_integer_src<T, R, fmt_param>{v, w};
2391}
2392
2393template<typename T, unsigned R, typename F> requires good_int_flags<flags_checker<R, T, F>, false>
2394auto operator / (T v, const f::fmt_radix_info<R, F>&) {
2395 return expr_integer_src<T, R, f::to_fmt_t<F>>{v};
2396}
2397
2398template<typename K>
2399struct expr_real : expr_to_std_string<expr_real<K>> {
2400 using symb_type = K;
2401 mutable u8s buf[40];
2402 mutable size_t l;
2403 double v;
2404 constexpr expr_real(double d) : v(d) {}
2405 constexpr expr_real(float d) : v(d) {}
2406
2407 size_t length() const noexcept {
2408 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
2409 l = ec != std::errc{} ? 0 : ptr - buf;
2410 return l;
2411 }
2412 K* place(K* ptr) const noexcept {
2413 if constexpr (sizeof(K) == sizeof(buf[0])) {
2414 ch_traits<K>::copy(ptr, (K*)buf, l);
2415 } else {
2416 for (size_t i = 0; i < l; i++) {
2417 ptr[i] = buf[i];
2418 }
2419 }
2420 return ptr + l;
2421 }
2422};
2423
2434template<typename K, std::floating_point T>
2435struct convert_to_strexpr<K, T> {
2436 using type = expr_real<K>;
2437};
2438
2450template<typename K> requires is_one_of_char_v<K>
2451inline constexpr expr_real<K> e_num(double t) {
2452 return {t};
2453}
2454
2455template<FromIntNumber Val, bool All, bool Ucase, bool Ox>
2456struct expr_hex_src {
2457 explicit constexpr expr_hex_src(Val v) : v_(v){}
2458 Val v_;
2459};
2460
2461template<typename K, FromIntNumber Val, bool All, bool Ucase, bool Ox>
2462struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
2463 using symb_type = K;
2464 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
2465 mutable K buf_[sizeof(Val) * 2]{};
2466
2467 explicit constexpr expr_hex(Val v) : v_(v){}
2468 constexpr expr_hex(const expr_hex_src<Val, All, Ucase, Ox>& v) : v_(v.v_){}
2469
2470 constexpr size_t length() const noexcept {
2471 K *ptr = buf_ + std::size(buf_);
2472 size_t l = 0;
2473 for (;;) {
2474 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2475 v_.val >>= 4;
2476 l++;
2477 if (v_.val) {
2478 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2479 v_.val >>= 4;
2480 l++;
2481 }
2482 if (!v_.val) {
2483 if constexpr (All) {
2484 if (size_t need = sizeof(Val) * 2 - l) {
2485 ch_traits<K>::assign(buf_, need, K('0'));
2486 }
2487 l = sizeof(Val) * 2;
2488 }
2489 break;
2490 }
2491 }
2492 v_.val = l;
2493 if constexpr (std::is_signed_v<Val>) {
2494 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
2495 }
2496 return l + (Ox ? 2 : 0);
2497 }
2498 constexpr K* place(K* ptr) const noexcept {
2499 if constexpr (std::is_signed_v<Val>) {
2500 if (v_.negate) {
2501 *ptr++ = K('-');
2502 }
2503 }
2504 if constexpr (Ox) {
2505 *ptr++ = K('0');
2506 *ptr++ = K('x');
2507 }
2508 if constexpr (All) {
2509 ch_traits<K>::copy(ptr, buf_, sizeof(Val) * 2);
2510 return ptr + sizeof(Val) * 2;
2511 } else {
2512 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
2513 return ptr + v_.val;
2514 }
2515 }
2516};
2517
2523enum HexFlags : unsigned {
2524 Short = 1,
2525 No0x = 2, //< without 0x prefix
2526 Lcase = 4, //< Use lower case
2527};
2528
2579template<unsigned Flags = 0, FromIntNumber T>
2580constexpr auto e_hex(T v) {
2581 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2582}
2583
2584template<char c = 0, char...Chars>
2585constexpr unsigned parse_f16_flags() {
2586 if constexpr (c == '1') {
2587 return HexFlags::Short | parse_f16_flags<Chars...>();
2588 } else if constexpr (c == '2') {
2589 return HexFlags::Lcase | parse_f16_flags<Chars...>();
2590 } else if constexpr (c == '3') {
2591 return HexFlags::No0x | parse_f16_flags<Chars...>();
2592 } else {
2593 return 0;
2594 }
2595}
2596
2597template<unsigned N>
2598struct f16flags{};
2599
2600template<FromIntNumber T, unsigned Flags>
2601constexpr auto operator/(T v, const f16flags<Flags>&) {
2602 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2603}
2604
2605inline namespace literals {
2606template<char...Chars>
2607constexpr auto operator""_f16() {
2608 return f16flags<parse_f16_flags<Chars...>()>{};
2609}
2610} // namespace literals
2611
2620template<typename K, typename Val, bool All, bool Ucase, bool Ox>
2621struct convert_to_strexpr<K, expr_hex_src<Val, All, Ucase, Ox>> {
2622 using type = expr_hex<K, Val, All, Ucase, Ox>;
2623};
2624
2625template<typename K>
2626struct expr_pointer : expr_hex<K, uintptr_t, true, true, true> {
2627 constexpr expr_pointer(const void* ptr) : expr_hex<K, uintptr_t, true, true, true>((uintptr_t)ptr){}
2628};
2629
2638template<typename K, typename T>
2639struct convert_to_strexpr<K, const T*> {
2640 using type = expr_pointer<K>;
2641};
2642
2643template<typename K, StrExprForType<K> A, bool Left>
2644struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
2645 using symb_type = K;
2646 K symbol_;
2647 size_t width_;
2648 const A& a_;
2649 mutable size_t alen_{};
2650 constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
2651
2652 constexpr size_t length() const noexcept {
2653 alen_ = a_.length();
2654 return std::max(alen_, width_);
2655 }
2656 constexpr K* place(K* ptr) const noexcept {
2657 if (alen_ >= width_) {
2658 return (K*)a_.place((typename A::symb_type*)ptr);
2659 }
2660 size_t w = width_ - alen_;
2661 if constexpr (Left) {
2662 ch_traits<K>::assign(ptr, w, symbol_);
2663 ptr += w;
2664 return (K*)a_.place((typename A::symb_type*)ptr);
2665 } else {
2666 ptr = (K*)a_.place((typename A::symb_type*)ptr);
2667 ch_traits<K>::assign(ptr, w, symbol_);
2668 return ptr + w;
2669 }
2670 }
2671};
2672
2694template<StrExpr A, typename K = typename A::symb_type>
2695expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
2696 return {symbol, width, a};
2697}
2698
2720template<StrExpr A, typename K = typename A::symb_type>
2721expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
2722 return {symbol, width, a};
2723}
2724
2725/*
2726* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
2727* K - тип символов строки
2728* T - тип контейнера строк (vector, list)
2729* I - длина разделителя в символах
2730* tail - добавлять разделитель после последнего элемента контейнера.
2731* Если контейнер пустой, разделитель в любом случае не добавляется
2732* skip_empty - пропускать пустые строки без добавления разделителя
2733* To create string concatenations with vectors and lists joined by a constant delimiter
2734* K is the symbols
2735* T - type of string container (vector, list)
2736* I - length of separator in characters
2737* tail - add a separator after the last element of the container.
2738* If the container is empty, the separator is not added anyway
2739* skip_empty - skip empty lines without adding a separator
2740*/
2741template<typename K, typename T, size_t I, bool tail, bool skip_empty>
2742struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
2743 using symb_type = K;
2744 using my_type = expr_join<K, T, I, tail, skip_empty>;
2745
2746 const T& s;
2747 const K* delim;
2748 constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
2749
2750 constexpr size_t length() const noexcept {
2751 size_t l = 0;
2752 for (const auto& t: s) {
2753 size_t len = t.length();
2754 if (len > 0 || !skip_empty) {
2755 if (I > 0 && l > 0) {
2756 l += I;
2757 }
2758 l += len;
2759 }
2760 }
2761 return l + (tail && I > 0 && (l > 0 || (!skip_empty && std::size(s) > 0))? I : 0);
2762 }
2763 constexpr K* place(K* ptr) const noexcept {
2764 if (s.empty()) {
2765 return ptr;
2766 }
2767 K* write = ptr;
2768 for (const auto& t: s) {
2769 size_t copyLen = t.length();
2770 if ((copyLen > 0 || !skip_empty) && I > 0 && write != ptr) {
2771 ch_traits<K>::copy(write, delim, I);
2772 write += I;
2773 }
2774 ch_traits<K>::copy(write, t.data(), copyLen);
2775 write += copyLen;
2776 }
2777 if (tail && I > 0 && (write != ptr || (!skip_empty && std::size(s) > 0))) {
2778 ch_traits<K>::copy(write, delim, I);
2779 write += I;
2780 }
2781 return write;
2782 }
2783};
2784
2798template<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>
2799inline constexpr auto e_join(const T& s, L&& d) {
2800 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
2801}
2802
2803template<size_t N>
2804concept is_const_pattern = N > 1 && N <= 17;
2805
2806template<typename K, size_t I>
2807struct _ascii_mask { // NOLINT
2808 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
2809};
2810
2811template<typename K>
2812struct _ascii_mask<K, 0> {
2813 constexpr static const size_t value = 0;
2814};
2815
2816template<typename K>
2817struct ascii_mask { // NOLINT
2818 using uns = std::make_unsigned_t<K>;
2819 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
2820 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
2821};
2822
2823template<typename K>
2824constexpr inline bool isAsciiUpper(K k) {
2825 return k >= 'A' && k <= 'Z';
2826}
2827
2828template<typename K>
2829constexpr inline bool isAsciiLower(K k) {
2830 return k >= 'a' && k <= 'z';
2831}
2832
2833template<typename K>
2834constexpr inline K makeAsciiLower(K k) {
2835 return isAsciiUpper(k) ? k | 0x20 : k;
2836}
2837
2838template<typename K>
2839constexpr inline K makeAsciiUpper(K k) {
2840 return isAsciiLower(k) ? k & ~0x20 : k;
2841}
2842
2843enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
2844template<TrimSides S, typename K, size_t N, bool withSpaces = false>
2845struct trim_operator;
2846
2847template<size_t I>
2848struct digits_selector {
2849 using wider_type = uint16_t;
2850};
2851
2852template<>
2853struct digits_selector<2> {
2854 using wider_type = uint32_t;
2855};
2856
2857template<>
2858struct digits_selector<4> {
2859 using wider_type = uint64_t;
2860};
2861
2872
2873template<bool CanNegate, bool CheckOverflow, typename T>
2874struct result_type_selector { // NOLINT
2875 using type = T;
2876};
2877
2878template<typename T>
2879struct result_type_selector<true, false, T> {
2880 using type = std::make_unsigned_t<T>;
2881};
2882
2883template<unsigned Base>
2884constexpr unsigned digit_width() {
2885 if (Base <=2) {
2886 return 1;
2887 }
2888 if (Base <= 4) {
2889 return 2;
2890 }
2891 if (Base <= 8) {
2892 return 3;
2893 }
2894 if (Base <= 16) {
2895 return 4;
2896 }
2897 if (Base <= 32) {
2898 return 5;
2899 }
2900 return 6;
2901}
2902
2903template<typename T, unsigned Base>
2904constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
2905
2906template<typename T>
2907struct convert_result {
2908 T value;
2910 size_t read;
2911};
2912
2913struct int_convert { // NOLINT
2914 inline static constexpr uint8_t NUMBERS[] = {
2915 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,
2916 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,
2917 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,
2918 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,
2919 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,
2920 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,
2921 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,
2922 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,
2923 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,
2924 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
2925
2926 template<typename K, unsigned Base>
2927 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2928 auto us = static_cast<std::make_unsigned_t<K>>(s);
2929 if constexpr (Base <= 10) {
2930 return us - '0';
2931 } else {
2932 if constexpr (sizeof(K) == 1) {
2933 return NUMBERS[us];
2934 } else {
2935 return us < 256 ? NUMBERS[us] : us;
2936 }
2937 }
2938 }
2939
2940 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
2941 requires(Base != 0)
2942 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
2943 using u_type = std::make_unsigned_t<T>;
2944 #ifndef HAS_BUILTIN_OVERFLOW
2945 u_type maxMult = 0, maxAdd = 0;
2946 if constexpr (CheckOverflow) {
2947 maxMult = std::numeric_limits<u_type>::max() / Base;
2948 maxAdd = std::numeric_limits<u_type>::max() % Base;
2949 }
2950 #endif
2951 u_type number = 0;
2952 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2954 const K* from = current;
2955
2956 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2957
2958 if (no_need_check_o_f) {
2959 for (;;) {
2960 const u_type digit = toDigit<K, Base>(*current);
2961 if (digit >= Base) {
2962 break;
2963 }
2964 number = number * Base + digit;
2965 if (++current == end) {
2967 break;
2968 }
2969 }
2970 } else {
2971 for (;maxDigits; maxDigits--) {
2972 const u_type digit = toDigit<K, Base>(*current);
2973 if (digit >= Base) {
2974 break;
2975 }
2976 number = number * Base + digit;
2977 ++current;
2978 }
2979 if (!maxDigits) {
2980 // Прошли все цифры, дальше надо с проверкой на overflow
2981 // All numbers have passed, then we need to check for overflow
2982 for (;;) {
2983 const u_type digit = toDigit<K, Base>(*current);
2984 if (digit >= Base) {
2985 break;
2986 }
2987 #ifdef HAS_BUILTIN_OVERFLOW
2988 if (__builtin_mul_overflow(number, Base, &number) ||
2989 __builtin_add_overflow(number, digit, &number)) {
2990 #else
2991 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2992 number = number * Base + digit;
2993 } else {
2994 #endif
2996 while(++current < end) {
2997 if (toDigit<K, Base>(*current) >= Base) {
2998 break;
2999 }
3000 }
3001 break;
3002 }
3003 if (++current == end) {
3005 break;
3006 }
3007 }
3008 }
3009 }
3010 T result;
3011 if constexpr (std::is_signed_v<T>) {
3012 result = negate ? 0 - number : number;
3013 if constexpr (CheckOverflow) {
3014 if (error != IntConvertResult::Overflow) {
3015 if (number > (u_type)std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
3017 }
3018 }
3019 }
3020 } else {
3021 result = number;
3022 }
3023 if (error == IntConvertResult::NotNumber && current > from) {
3025 }
3026 return {result, error, size_t(current - start)};
3027 }
3028public:
3029 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
3030 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
3031 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
3032 // 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
3033 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
3034 requires(Base == unsigned(-1) || (Base < 37 && Base != 1))
3035 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
3036 const K *ptr = start, *end = ptr + len;
3037 bool negate = false;
3038 if constexpr (SkipWs) {
3039 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
3040 ptr++;
3041 }
3042 if (ptr != end) {
3043 if constexpr (std::is_signed_v<T>) {
3044 if constexpr (AllowSign) {
3045 // Может быть число, +число или -число
3046 // Can be a number, +number or -number
3047 if (*ptr == '+') {
3048 ptr++;
3049 } else if (*ptr == '-') {
3050 negate = true;
3051 ptr++;
3052 }
3053 } else {
3054 // Может быть число или -число
3055 // Can be a number or -number
3056 if (*ptr == '-') {
3057 negate = true;
3058 ptr++;
3059 }
3060 }
3061 } else if constexpr (AllowSign) {
3062 // Может быть число или +число
3063 // Can be a number or +number
3064 if (*ptr == '+') {
3065 ptr++;
3066 }
3067 }
3068 }
3069 if (ptr != end) {
3070 if constexpr (Base == 0 || Base == unsigned(-1)) {
3071 if (*ptr == '0') {
3072 ptr++;
3073 if (ptr != end) {
3074 if (*ptr == 'x' || *ptr == 'X') {
3075 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
3076 }
3077 if constexpr (Base == unsigned(-1)) {
3078 if (*ptr == 'b' || *ptr == 'B') {
3079 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
3080 }
3081 if (*ptr == 'o' || *ptr == 'O') {
3082 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
3083 }
3084 }
3085 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
3086 }
3087 return {0, IntConvertResult::Success, size_t(ptr - start)};
3088 }
3089 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
3090 } else
3091 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
3092 }
3093 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
3094 }
3095};
3096
3097template<typename K, typename Impl>
3098class null_terminated {
3099public:
3106 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
3107};
3108
3109template<typename K, typename Impl, bool Mutable> class buffer_pointers;
3110
3119template<typename K, typename Impl>
3120class buffer_pointers<K, Impl, false> {
3121 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
3122public:
3129 constexpr const K* data() const { return d().symbols(); }
3136 constexpr const K* begin() const { return d().symbols(); }
3143 constexpr const K* end() const { return d().symbols() + d().length(); }
3150 constexpr const K* cbegin() const { return d().symbols(); }
3157 constexpr const K* cend() const { return d().symbols() + d().length(); }
3158};
3159
3160template<typename K, typename Impl>
3161class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
3162 constexpr Impl& d() { return *static_cast<Impl*>(this); }
3163 using base = buffer_pointers<K, Impl, false>;
3164public:
3171 constexpr const K* data() const { return base::data(); }
3178 constexpr const K* begin() const { return base::begin(); }
3185 constexpr const K* end() const { return base::end(); }
3192 constexpr const K* cbegin() const { return base::cbegin(); }
3199 constexpr const K* cend() const { return base::cend(); }
3206 constexpr K* data() { return d().str(); }
3213 constexpr K* begin() { return d().str(); }
3220 constexpr K* end() { return d().str() + d().length(); }
3221};
3222
3229template<typename K, typename StrSrc>
3230class SplitterBase {
3231 using str_t = StrSrc;
3232 str_t text_;
3233 str_t delim_;
3234
3235public:
3236 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
3241 constexpr bool is_done() const {
3242 return text_.length() == str::npos;
3243 }
3244
3250 constexpr str_t next() {
3251 if (!text_.length()) {
3252 auto ret = text_;
3253 text_.str++;
3254 text_.len--;
3255 return ret;
3256 } else if (text_.length() == str::npos) {
3257 return {nullptr, 0};
3258 }
3259 size_t pos = text_.find(delim_), next = 0;
3260 if (pos == str::npos) {
3261 pos = text_.length();
3262 next = pos + 1;
3263 } else {
3264 next = pos + delim_.length();
3265 }
3266 str_t result{text_.str, pos};
3267 text_.str += next;
3268 text_.len -= next;
3269 return result;
3270 }
3271};
3272
3273struct from_begin {
3274 size_t idx_;
3275};
3276struct from_end {
3277 size_t idx_;
3278};
3279inline constexpr from_end to_end{0};
3280
3281template<typename T, typename K>
3282concept is_slice_find = requires (const T& t) {
3283 requires is_equal_str_type_v<K, symb_type_from_src_t<decltype(t.pattern_)>>;
3284 { T::to_end_of_pattern }->std::same_as<const bool&>;
3285 { T::offset_from_found }->std::same_as<const bool&>;
3286 { T::find_reverse }->std::same_as<const bool&>;
3287 { T::all_at_fail }->std::same_as<const bool&>;
3288 { t.offset_ } -> std::convertible_to<size_t>;
3289 { t.skip_ } -> std::convertible_to<size_t>;
3290 { t.find(t.pattern_, size_t(0)) } -> std::convertible_to<size_t>;
3291};
3292
3293template<typename T>
3294using slice_find_func = size_t(*)(T, T, size_t);
3295
3296template<typename Pattern, bool ToEndOfPattern, bool OffsetFromFound, bool AllAtFail, bool FindReverse, slice_find_func<Pattern> F>
3297struct slice_find {
3298 inline static constexpr const bool to_end_of_pattern = ToEndOfPattern;
3299 inline static constexpr const bool offset_from_found = OffsetFromFound;
3300 inline static constexpr const bool find_reverse = FindReverse;
3301 inline static constexpr const bool all_at_fail = AllAtFail;
3302
3303 template<StrSource S>
3304 constexpr slice_find(S&& pattern, size_t offset = 0, size_t skip = 0) {
3305 pattern_ = std::forward<S>(pattern);
3306 offset_ = offset;
3307 skip_ = skip;
3308 }
3309
3310 constexpr size_t find(Pattern where, size_t offset) const {
3311 return F(where, pattern_, offset);
3312 }
3313 Pattern pattern_;
3314 size_t offset_;
3315 size_t skip_;
3316};
3317
3318template<typename T, typename K>
3319concept first_index_in_str = std::is_same_v<T, from_end> || std::is_convertible_v<T, size_t> || is_slice_find<T, K>;
3320
3321template<typename T, typename K>
3322concept last_index_in_str = first_index_in_str<T, K> || std::is_same_v<T, from_begin>;
3323
3348template<typename K, typename StrRef, typename Impl, bool Mutable>
3349class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
3350 constexpr const Impl& d() const noexcept {
3351 return *static_cast<const Impl*>(this);
3352 }
3353 constexpr size_t _len() const noexcept {
3354 return d().length();
3355 }
3356 constexpr const K* _str() const noexcept {
3357 return d().symbols();
3358 }
3359 constexpr bool _is_empty() const noexcept {
3360 return d().is_empty();
3361 }
3362
3363public:
3364 using symb_type = K;
3365 using str_piece = StrRef;
3366 using traits = ch_traits<K>;
3367 using uns_type = std::make_unsigned_t<K>;
3368 using my_type = Impl;
3369 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3370 str_src_algs() = default;
3371
3384 constexpr K* place(K* ptr) const noexcept {
3385 size_t myLen = _len();
3386 traits::copy(ptr, _str(), myLen);
3387 return ptr + myLen;
3388 }
3389
3399 void copy_to(K* buffer, size_t bufSize) {
3400 size_t tlen = std::min(_len(), bufSize - 1);
3401 traits::copy(buffer, _str(), tlen);
3402 buffer[tlen] = 0;
3403 }
3404
3410 constexpr size_t size() const {
3411 return _len();
3412 }
3413
3420 template<typename D = K> requires is_equal_str_type_v<K, D>
3421 constexpr std::basic_string_view<D> to_sv() const noexcept {
3422 return {(const D*)_str(), _len()};
3423 }
3424
3430 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
3431 constexpr operator std::basic_string_view<D, Traits>() const {
3432 return {(const D*)_str(), _len()};
3433 }
3434
3440 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
3441 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
3442 return {(const D*)_str(), _len()};
3443 }
3444
3450 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
3451 constexpr operator std::basic_string<D, Traits, Allocator>() const {
3452 return {(const D*)_str(), _len()};
3453 }
3454
3460 constexpr operator str_piece() const noexcept {
3461 return str_piece{_str(), _len()};
3462 }
3463
3469 constexpr str_piece to_str() const noexcept {
3470 return {_str(), _len()};
3471 }
3472
3495 [[deprecated("Use 'slice' or [start, end] in C++23")]]
3496 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3497 size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
3498 idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
3499 if (idxEnd > myLen)
3500 idxEnd = myLen;
3501 if (idxStart > idxEnd)
3502 idxStart = idxEnd;
3503 return str_piece{_str() + idxStart, idxEnd - idxStart};
3504 }
3505
3573 template<first_index_in_str<K> S, last_index_in_str<K> E>
3574 constexpr str_piece slice(S start, E end) const noexcept {
3575 size_t idxStart, idxEnd, len = _len();
3576 if constexpr (is_slice_find<S, K>) {
3577 size_t offset = std::min(S::find_reverse ? len - start.offset_ : start.offset_, len), skip = start.skip_;
3578 for (;;) {
3579 size_t find = start.find(d(), offset);
3580 if (find == str::npos) {
3581 if constexpr (S::all_at_fail) {
3582 idxStart = 0;
3583 } else {
3584 idxStart = len;
3585 }
3586 break;
3587 }
3588 if (!skip--) {
3589 if constexpr (S::to_end_of_pattern) {
3590 idxStart = find + start.pattern_.length();
3591 } else {
3592 idxStart = find;
3593 }
3594 break;
3595 }
3596 offset = find;
3597 if constexpr (!S::find_reverse) {
3598 offset += start.pattern_.length();
3599 }
3600 }
3601 } else if constexpr (std::is_same_v<S, from_end>) {
3602 idxStart = start.idx_ >= len ? 0 : len - start.idx_;
3603 } else {
3604 idxStart = std::min(size_t(start), len);
3605 }
3606
3607 if constexpr (is_slice_find<E, K>) {
3608 size_t offset = std::min(E::offset_from_found ? idxStart + end.offset_ : E::find_reverse ? len - end.offset_ : end.offset_, len), skip = end.skip_;
3609 for (;;) {
3610 size_t find = end.find(d(), offset);
3611 if (find == str::npos) {
3612 if constexpr (E::all_at_fail) {
3613 idxEnd = len;
3614 } else {
3615 idxEnd = idxStart;
3616 }
3617 break;
3618 }
3619 if (!skip--) {
3620 if constexpr (E::to_end_of_pattern) {
3621 idxEnd = find + end.pattern_.length();
3622 } else {
3623 idxEnd = find;
3624 }
3625 break;
3626 }
3627 offset = find;
3628 if constexpr (!E::find_reverse) {
3629 offset += end.pattern_.length();
3630 }
3631 }
3632 } else if constexpr (std::is_same_v<E, from_begin>) {
3633 idxEnd = std::max(idxStart, std::min(end.idx_, len));
3634 } else if constexpr (std::is_same_v<E, from_end>) {
3635 idxEnd = std::max(idxStart, end.idx_ >= len ? 0 : len - end.idx_);
3636 } else {
3637 size_t e = end;
3638 idxEnd = len - idxStart <= e ? len : idxStart + e;
3639 }
3640
3641 return str_piece{_str() + idxStart, idxEnd - idxStart};
3642 }
3643
3644#if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202211L
3649 template<first_index_in_str<K> S, last_index_in_str<K> E>
3650 constexpr str_piece operator[](S start, E end) const noexcept {
3651 return slice(start, end);
3652 }
3653#endif
3658 template<typename E> requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3659 constexpr str_piece prefix(E count) const noexcept {
3660 return slice(0, count);
3661 }
3662
3666 template<typename E> requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3667 constexpr str_piece tail(E count) const noexcept {
3668 return slice(count, to_end);
3669 }
3670
3674 template<typename E> requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3675 constexpr str_piece suffix(E count) const noexcept {
3676 if constexpr (std::is_convertible_v<E, size_t>) {
3677 return slice(from_end(static_cast<size_t>(count)), to_end);
3678 } else {
3679 return slice(count, to_end);
3680 }
3681 }
3682
3692 [[deprecated("Use 'slice' or [start, len] in C++23")]]
3693 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
3694 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3695 if (idxEnd > myLen)
3696 idxEnd = myLen;
3697 if (idxStart > idxEnd)
3698 idxStart = idxEnd;
3699 return str_piece{_str() + idxStart, idxEnd - idxStart};
3700 }
3701
3715 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
3716 return str_piece{_str() + from, to - from};
3717 }
3718
3728 [[deprecated("Use 'slice(0, begin_or_all(pattern, offset))' or '[0, begin_or_all(pattern, offset)]' in C++23, or 'slice(0, \"pattern\"_boa))'")]]
3729 constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
3730 return slice(0, find_or_all(pattern, offset));
3731 }
3732
3736 constexpr bool operator!() const noexcept {
3737 return _is_empty();
3738 }
3739
3749 constexpr K at(ptrdiff_t idx) const {
3750 return _str()[idx >= 0 ? idx : _len() + idx];
3751 }
3752 // Сравнение строк
3753 // String comparison
3754 constexpr int compare(const K* text, size_t len) const {
3755 size_t myLen = _len();
3756 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3757 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3758 }
3767 constexpr int compare(str_piece o) const {
3768 return compare(o.symbols(), o.length());
3769 }
3770
3778 constexpr int strcmp(const K* text) const {
3779 size_t myLen = _len(), idx = 0;
3780 const K* ptr = _str();
3781 for (; idx < myLen; idx++) {
3782 uns_type s1 = (uns_type)text[idx];
3783 if (!s1) {
3784 return 1;
3785 }
3786 uns_type s2 = (uns_type)ptr[idx];
3787 if (s1 < s2) {
3788 return 1;
3789 } else if (s1 > s2) {
3790 return -1;
3791 }
3792 }
3793 return text[idx] == 0 ? 0 : -1;
3794 }
3795
3796 constexpr bool equal(const K* text, size_t len) const noexcept {
3797 return len == _len() && traits::compare(_str(), text, len) == 0;
3798 }
3807 constexpr bool equal(str_piece other) const noexcept {
3808 return equal(other.symbols(), other.length());
3809 }
3810
3818 constexpr bool operator==(const base& other) const noexcept {
3819 return equal(other._str(), other._len());
3820 }
3821
3827 constexpr auto operator<=>(const base& other) const noexcept {
3828 return compare(other._str(), other._len()) <=> 0;
3829 }
3830
3836 template<typename T, size_t N = const_lit_for<K, T>::Count>
3837 constexpr bool operator==(T&& other) const noexcept {
3838 return N - 1 == _len() && traits::compare(_str(), (const K*)other, N - 1) == 0;
3839 }
3840
3846 template<typename T, size_t N = const_lit_for<K, T>::Count>
3847 constexpr auto operator<=>(T&& other) const noexcept {
3848 size_t myLen = _len();
3849 int cmp = traits::compare(_str(), (const K*)other, std::min(myLen, N - 1));
3850 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3851 return res <=> 0;
3852 }
3853
3854 // Сравнение ascii строк без учёта регистра
3855 // Compare ascii strings without taking into account case
3856 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
3857 if (!len)
3858 return _is_empty() ? 0 : 1;
3859 size_t myLen = _len(), checkLen = std::min(myLen, len);
3860 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
3861 while (checkLen--) {
3862 uns_type s1 = *ptr1++, s2 = *ptr2++;
3863 if (s1 == s2)
3864 continue;
3865 s1 = makeAsciiLower(s1);
3866 s2 = makeAsciiLower(s2);
3867 if (s1 > s2)
3868 return 1;
3869 else if (s1 < s2)
3870 return -1;
3871 }
3872 return myLen == len ? 0 : myLen > len ? 1 : -1;
3873 }
3882 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
3883 return compare_ia(text.symbols(), text.length());
3884 }
3885
3894 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
3895 size_t len = _len();
3896 if (len != text.length())
3897 return false;
3898 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text.symbols());
3899 while (len--) {
3900 uns_type s1 = *ptr1++, s2 = *ptr2++;
3901 if (s1 == s2)
3902 continue;
3903 if (isAsciiUpper(s1)) {
3904 if (s1 == (s2 & ~0x20)) {
3905 continue;
3906 }
3907 } else if (isAsciiLower(s1) && s1 == (s2 | 0x20)) {
3908 continue;
3909 }
3910 return false;
3911 }
3912 return true;
3913 }
3914
3922 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
3923 return compare_ia(text.symbols(), text.length()) < 0;
3924 }
3925
3926 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3927 size_t lenText = _len();
3928 // Образец, не вмещающийся в строку и пустой образец не находим
3929 // We don't look for an empty string or a string longer than the text.
3930 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3931 return str::npos;
3932 lenPattern--;
3933 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3934 pattern++;
3935 for (const K* fnd = text + offset;; ++fnd) {
3936 fnd = traits::find(fnd, last - fnd, first);
3937 if (!fnd)
3938 return str::npos;
3939 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3940 return static_cast<size_t>(fnd - text);
3941 }
3942 }
3953 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
3954 return find(pattern.symbols(), pattern.length(), offset);
3955 }
3956
3972 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
3973 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const {
3974 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3975 return fnd;
3976 }
3977 throw Exc(std::forward<Args>(args)...);
3978 }
3979
3989 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
3990 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3991 return fnd == str::npos ? fnd : fnd + pattern.length();
3992 }
3993
4003 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
4004 auto fnd = find(pattern.symbols(), pattern.length(), offset);
4005 return fnd == str::npos ? _len() : fnd;
4006 }
4007
4017 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
4018 auto fnd = find(pattern.symbols(), pattern.length(), offset);
4019 return fnd == str::npos ? _len() : fnd + pattern.length();
4020 }
4021
4022 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
4023 if (lenPattern == 1)
4024 return find_last(pattern[0], offset);
4025 size_t lenText = std::min(_len(), offset);
4026 // Образец, не вмещающийся в строку и пустой образец не находим
4027 // We don't look for an empty string or a string longer than the text.
4028 if (!lenPattern || lenPattern > lenText)
4029 return str::npos;
4030
4031 lenPattern--;
4032 const K *text = _str() + lenPattern, last = pattern[lenPattern];
4033 lenText -= lenPattern;
4034 while(lenText) {
4035 if (text[--lenText] == last) {
4036 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
4037 return lenText;
4038 }
4039 }
4040 }
4041 return str::npos;
4042 }
4053 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
4054 return find_last(pattern.symbols(), pattern.length(), offset);
4055 }
4056
4066 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
4067 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
4068 return fnd == str::npos ? fnd : fnd + pattern.length();
4069 }
4070
4080 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
4081 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
4082 return fnd == str::npos ? _len() : fnd;
4083 }
4084
4094 constexpr size_t find_end_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
4095 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
4096 return fnd == str::npos ? _len() : fnd + pattern.length();
4097 }
4098
4108 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
4109 return find(pattern, offset) != str::npos;
4110 }
4111
4121 constexpr size_t find(K s, size_t offset = 0) const noexcept {
4122 size_t len = _len();
4123 if (offset < len) {
4124 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
4125 if (fnd)
4126 return static_cast<size_t>(fnd - str);
4127 }
4128 return str::npos;
4129 }
4130
4140 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
4141 size_t len = _len();
4142 if (offset < len) {
4143 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
4144 if (fnd)
4145 return static_cast<size_t>(fnd - str);
4146 }
4147 return len;
4148 }
4149
4150 static const K* find_first_by_case(const K* from, const K* last, K symbol, K symbolInOtherCase) {
4151 while (from < last) {
4152 K f = *from;
4153 if (f == symbol || f == symbolInOtherCase) {
4154 return from;
4155 }
4156 from++;
4157 }
4158 return nullptr;
4159 }
4160
4171 constexpr size_t find_ia(str_piece pattern, size_t offset = 0) const noexcept {
4172 size_t lenPattern = pattern.length();
4173 size_t lenText = _len();
4174 // Образец, не вмещающийся в строку и пустой образец не находим
4175 // We don't look for an empty string or a string longer than the text.
4176 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
4177 return str::npos;
4178 const K* patternStr = pattern.symbols();
4179 lenPattern--;
4180 const K *text = _str(), *last = text + lenText - lenPattern, first = patternStr[0];
4181 K firstInOtherCase;
4182 patternStr++;
4183 bool firstHasCase = isAsciiLower(first);
4184 if (firstHasCase) {
4185 firstInOtherCase = first & ~0x20;
4186 } else if (isAsciiUpper(first)) {
4187 firstHasCase = true;
4188 firstInOtherCase = first | 0x20;
4189 }
4190
4191 auto equal = [&](const K* from) {
4192 for (size_t idx = 0; idx < lenPattern; idx++) {
4193 K s1 = from[idx], s2 = patternStr[idx];
4194 if (s1 == s2)
4195 continue;
4196 if (isAsciiUpper(s1)) {
4197 if (s1 == (s2 & ~0x20)) {
4198 continue;
4199 }
4200 } else if (isAsciiLower(s1) && s1 == (s2 | 0x20)) {
4201 continue;
4202 }
4203 return false;
4204 }
4205 return true;
4206 };
4207
4208 for (const K* fnd = text + offset;;) {
4209 fnd = firstHasCase
4210 ? find_first_by_case(fnd, last, first, firstInOtherCase)
4211 : traits::find(fnd, last - fnd, first);
4212 if (!fnd)
4213 return str::npos;
4214 if (equal(++fnd)) {
4215 return fnd - text - 1;
4216 }
4217 }
4218 }
4219
4232 constexpr size_t find_ia_lower(str_piece pattern_lower, size_t offset = 0) const noexcept {
4233 size_t lenPattern = pattern_lower.length();
4234 size_t lenText = _len();
4235 // Образец, не вмещающийся в строку и пустой образец не находим
4236 // We don't look for an empty string or a string longer than the text.
4237 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
4238 return str::npos;
4239 const K* patternStr = pattern_lower.symbols();
4240 lenPattern--;
4241 const K *text = _str(), *last = text + lenText - lenPattern, first = patternStr[0];
4242 K firstInOtherCase;
4243 patternStr++;
4244 bool firstHasCase = isAsciiLower(first);
4245 if (firstHasCase) {
4246 firstInOtherCase = first & ~0x20;
4247 }
4248
4249 auto equal = [&](const K* from) {
4250 for (size_t idx = 0; idx < lenPattern; idx++) {
4251 K s1 = from[idx], s2 = patternStr[idx];
4252 if (!(s1 == s2 || (isAsciiUpper(s1) && (s1 | 0x20) == s2))) {
4253 return false;
4254 }
4255 }
4256 return true;
4257 };
4258
4259 for (const K* fnd = text + offset;;) {
4260 fnd = firstHasCase
4261 ? find_first_by_case(fnd, last, first, firstInOtherCase)
4262 : traits::find(fnd, last - fnd, first);
4263 if (!fnd)
4264 return str::npos;
4265 if (equal(++fnd)) {
4266 return fnd - text - 1;
4267 }
4268 }
4269 }
4270
4271 template<typename Op>
4272 constexpr void for_all_founded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
4273 if (!maxCount)
4274 maxCount--;
4275 while (maxCount-- > 0) {
4276 size_t fnd = find(pattern, patternLen, offset);
4277 if (fnd == str::npos)
4278 break;
4279 op(fnd);
4280 offset = fnd + patternLen;
4281 }
4282 }
4295 template<typename Op>
4296 constexpr void for_all_founded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4297 for_all_founded(op, pattern.symbols(), pattern.length(), offset, maxCount);
4298 }
4299
4300 template<typename To = std::vector<size_t>>
4301 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
4302 To result;
4303 for_all_founded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
4304 return result;
4305 }
4318 template<typename To = std::vector<size_t>>
4319 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4320 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
4321 }
4322 template<typename To = std::vector<size_t>>
4323 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
4324 return for_all_founded([&](size_t pos) {
4325 to.emplace_back(pos);
4326 }, pattern, len, offset, maxCount);
4327 }
4338 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
4339 size_t len = std::min(_len(), offset);
4340 const K *text = _str();
4341 while (len > 0) {
4342 if (text[--len] == s)
4343 return len;
4344 }
4345 return str::npos;
4346 }
4347
4357 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
4358 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4359 }
4360
4370 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
4371 const K* text = _str();
4372 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4373 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4374 }
4375
4385 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
4386 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4387 }
4388
4398 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4399 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4400 }
4401
4411 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
4412 const K* text = _str();
4413 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4414 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4415 }
4416
4426 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4427 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4428 }
4429
4439 [[deprecated("Use substring(start, end)")]]
4440 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
4441 return my_type{d()(from, len)};
4442 }
4443
4451 template<typename T, first_index_in_str<K> S, last_index_in_str<K> E> requires std::is_constructible_v<T, str_piece>
4452 constexpr T slice_to(S start, E end) const noexcept {
4453 str_piece res = slice(start, end);
4454 if constexpr (std::is_constructible_v<T, const my_type&> && !std::is_same_v<T, str_piece>) {
4455 if (res.is_same(*this)) {
4456 return T{*this};
4457 }
4458 }
4459 return T{res};
4460 }
4461
4465 template<first_index_in_str<K> S, last_index_in_str<K> E>
4466 constexpr my_type substring(S start, E end) const noexcept {
4467 return slice_to<my_type>(start, end);
4468 }
4469
4479 [[deprecated("Use slice_to(start, end)")]]
4480 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
4481 return my_type{d().mid(from, len)};
4482 }
4483
4511 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4512 constexpr T as_int() const noexcept {
4513 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4514 return err == IntConvertResult::Overflow ? 0 : res;
4515 }
4516
4544 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4545 constexpr convert_result<T> to_int() const noexcept {
4546 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4547 }
4548
4554 template<bool SkipWS = true, bool AllowPlus = true>
4555 requires(sizeof(K) == 1 &&
4556 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4557 std::optional<double> to_double() const noexcept {
4558 size_t len = _len();
4559 const K* ptr = _str();
4560 if constexpr (SkipWS) {
4561 while (len && uns_type(*ptr) <= ' ') {
4562 len--;
4563 ptr++;
4564 }
4565 }
4566 if constexpr (AllowPlus) {
4567 if (len && *ptr == K('+')) {
4568 ptr++;
4569 len--;
4570 }
4571 }
4572 if (!len) {
4573 return {};
4574 }
4575 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4576 double d{};
4577 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
4578 return d;
4579 }
4580 }
4581 return {};
4582 }
4583
4589 template<bool SkipWS = true>
4590 requires(sizeof(K) == 1 &&
4591 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4592 std::optional<double> to_double_hex() const noexcept {
4593 size_t len = _len();
4594 const K* ptr = _str();
4595 if constexpr (SkipWS) {
4596 while (len && uns_type(*ptr) <= ' ') {
4597 len--;
4598 ptr++;
4599 }
4600 }
4601 if (len) {
4602 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4603 double d{};
4604 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4605 return d;
4606 }
4607 }
4608 }
4609 return {};
4610 }
4611
4619 template<ToIntNumber T>
4620 constexpr void as_number(T& t) const {
4621 t = as_int<T>();
4622 }
4623
4624 template<typename T, typename Op>
4625 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
4626 size_t mylen = _len();
4627 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4628 str_piece me{_str(), mylen};
4629 for (size_t i = 0;; i++) {
4630 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4631 if (beginOfDelim == str::npos) {
4632 str_piece last{me.symbols() + offset, me.length() - offset};
4633 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4634 beforeFunc(last);
4635 }
4636 if constexpr (requires { results.emplace_back(last); }) {
4637 if (last.is_same(me)) {
4638 // Пробуем положить весь объект.
4639 // Try to put the entire object.
4640 results.emplace_back(d());
4641 } else {
4642 results.emplace_back(last);
4643 }
4644 } else if constexpr (requires { results.push_back(last); }) {
4645 if (last.is_same(me)) {
4646 // Пробуем положить весь объект.
4647 // Try to put the entire object.
4648 results.push_back(d());
4649 } else {
4650 results.push_back(last);
4651 }
4652 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
4653 if (i < std::size(results)) {
4654 if (last.is_same(me)) {
4655 // Пробуем положить весь объект.
4656 // Try to put the entire object.
4657 results[i] = d();
4658 } else
4659 results[i] = last;
4660 }
4661 }
4662 break;
4663 }
4664 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4665 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4666 beforeFunc(piece);
4667 }
4668 if constexpr (requires { results.emplace_back(piece); }) {
4669 results.emplace_back(piece);
4670 } else if constexpr (requires { results.push_back(piece); }) {
4671 results.push_back(piece);
4672 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
4673 if (i < std::size(results)) {
4674 results[i] = piece;
4675 if (i == results.size() - 1) {
4676 break;
4677 }
4678 }
4679 }
4680 offset = beginOfDelim + lendelimiter;
4681 }
4682 if constexpr (!std::is_same_v<T, void>) {
4683 return results;
4684 }
4685 }
4716 template<typename T, typename Op>
4717 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
4718 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4719 }
4720
4732 template<typename T>
4733 constexpr T split(str_piece delimiter, size_t offset = 0) const {
4734 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4735 }
4736
4737 // Начинается ли эта строка с указанной подстроки
4738 // Does this string start with the specified substring
4739 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
4740 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4741 }
4748 constexpr bool starts_with(str_piece prefix) const noexcept {
4749 return starts_with(prefix.symbols(), prefix.length());
4750 }
4751
4757 constexpr bool starts_with_and_ws(str_piece prefix) const noexcept {
4758 return _len() > prefix.length() &&
4759 starts_with(prefix) &&
4760 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4761 }
4762
4770 constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4771 return _len() > prefix.length() &&
4772 starts_with(prefix) &&
4773 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4774 }
4775 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4776 constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4777 return _len() >= N &&
4778 starts_with(prefix) &&
4779 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4780 }
4781
4782 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
4783 size_t myLen = _len();
4784 if (myLen < len) {
4785 return false;
4786 }
4787 const K* ptr1 = _str();
4788 while (len--) {
4789 K s1 = *ptr1++, s2 = *prefix++;
4790 if (s1 == s2)
4791 continue;
4792 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4793 return false;
4794 }
4795 return true;
4796 }
4803 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
4804 return starts_with_ia(prefix.symbols(), prefix.length());
4805 }
4806
4812 constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept {
4813 return _len() > prefix.length() &&
4814 starts_with_ia(prefix) &&
4815 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4816 }
4817
4825 constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4826 return _len() > prefix.length() &&
4827 starts_with_ia(prefix) &&
4828 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4829 }
4830 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4831 constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4832 return _len() >= N &&
4833 starts_with_ia(prefix) &&
4834 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4835 }
4836
4837 // Является ли эта строка началом указанной строки
4838 // Is this string the beginning of the specified string
4839 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
4840 size_t myLen = _len();
4841 if (myLen > len)
4842 return false;
4843 return !myLen || 0 == traits::compare(text, _str(), myLen);
4844 }
4851 constexpr bool prefix_in(str_piece text) const noexcept {
4852 return prefix_in(text.symbols(), text.length());
4853 }
4854 // Заканчивается ли строка указанной подстрокой
4855 // Does the string end with the specified substring
4856 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
4857 size_t myLen = _len();
4858 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4859 }
4866 constexpr bool ends_with(str_piece suffix) const noexcept {
4867 return ends_with(suffix.symbols(), suffix.length());
4868 }
4869 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
4870 // Whether the string ends with the specified substring, case insensitive ASCII
4871 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
4872 size_t myLen = _len();
4873 if (myLen < len) {
4874 return false;
4875 }
4876 const K* ptr1 = _str() + myLen - len;
4877 while (len--) {
4878 K s1 = *ptr1++, s2 = *suffix++;
4879 if (s1 == s2)
4880 continue;
4881 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4882 return false;
4883 }
4884 return true;
4885 }
4892 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
4893 return ends_with_ia(suffix.symbols(), suffix.length());
4894 }
4895
4899 constexpr bool is_ascii() const noexcept {
4900 if (_is_empty())
4901 return true;
4902 if (std::is_constant_evaluated()) {
4903 for (size_t idx = 0; idx < _len(); idx++) {
4904 if (uns_type(_str()[idx]) > 127) {
4905 return false;
4906 }
4907 }
4908 return true;
4909 }
4910 const int sl = ascii_mask<K>::WIDTH;
4911 const size_t mask = ascii_mask<K>::VALUE;
4912 size_t len = _len();
4913 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
4914 if constexpr (sl > 1) {
4915 const size_t roundMask = sizeof(size_t) - 1;
4916 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4917 if (*ptr++ > 127)
4918 return false;
4919 len--;
4920 }
4921 while (len >= sl) {
4922 if (*reinterpret_cast<const size_t*>(ptr) & mask)
4923 return false;
4924 ptr += sl;
4925 len -= sl;
4926 }
4927 }
4928 while (len--) {
4929 if (*ptr++ > 127)
4930 return false;
4931 }
4932 return true;
4933 }
4934
4942 template<typename R = my_type>
4944 return R::upperred_only_ascii_from(d());
4945 }
4946
4954 template<typename R = my_type>
4956 return R::lowered_only_ascii_from(d());
4957 }
4958
4974 template<typename R = my_type>
4975 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
4976 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4977 }
4978
4990 template<typename R = str_piece>
4991 constexpr std::optional<R> strip_prefix(str_piece prefix) const {
4992 if (starts_with(prefix)) {
4993 return R{slice(prefix.length(), to_end)};
4994 }
4995 return {};
4996 }
4997
5009 template<typename R = str_piece>
5010 constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
5011 if (starts_with_ia(prefix)) {
5012 return R{slice(prefix.length(), to_end)};
5013 }
5014 return {};
5015 }
5016
5028 template<typename R = str_piece>
5029 constexpr std::optional<R> strip_suffix(str_piece suffix) const {
5030 if (ends_with(suffix)) {
5031 return R{slice(0, from_end{suffix.length()})};
5032 }
5033 return {};
5034 }
5035
5047 template<typename R = str_piece>
5048 constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
5049 if (ends_with_ia(suffix)) {
5050 return R{slice(0, from_end{suffix.length()})};
5051 }
5052 return {};
5053 }
5054
5055 template<StrType<K> From>
5056 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
5057 str_piece sfrom = from, newPos = opTrim(sfrom);
5058 if (newPos.is_same(sfrom)) {
5059 my_type res = from;
5060 return res;
5061 }
5062 return my_type{newPos};
5063 }
5064 template<TrimSides S, StrType<K> From>
5065 constexpr static my_type trim_static(const From& from) {
5066 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
5067 }
5068
5069 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
5070 requires is_const_pattern<N>
5071 constexpr static my_type trim_static(const From& from, T&& pattern) {
5072 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
5073 }
5074
5075 template<TrimSides S, bool withSpaces, StrType<K> From>
5076 constexpr static my_type trim_static(const From& from, str_piece pattern) {
5077 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
5078 }
5087 template<typename R = str_piece>
5088 constexpr R trimmed() const {
5089 return R::template trim_static<TrimSides::TrimAll>(d());
5090 }
5091
5099 template<typename R = str_piece>
5100 constexpr R trimmed_left() const {
5101 return R::template trim_static<TrimSides::TrimLeft>(d());
5102 }
5103
5111 template<typename R = str_piece>
5112 constexpr R trimmed_right() const {
5113 return R::template trim_static<TrimSides::TrimRight>(d());
5114 }
5115
5125 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5126 requires is_const_pattern<N>
5127 constexpr R trimmed(T&& pattern) const {
5128 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
5129 }
5130
5140 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5141 requires is_const_pattern<N>
5142 constexpr R trimmed_left(T&& pattern) const {
5143 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
5144 }
5145
5155 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5156 requires is_const_pattern<N>
5157 constexpr R trimmed_right(T&& pattern) const {
5158 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
5159 }
5160 // Триминг по символам в литерале и пробелам
5161 // Trimming by characters in literal and spaces
5162
5177 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5178 requires is_const_pattern<N>
5179 constexpr R trimmed_with_spaces(T&& pattern) const {
5180 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
5181 }
5182
5196 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5197 requires is_const_pattern<N>
5198 constexpr R trimmed_left_with_spaces(T&& pattern) const {
5199 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5200 }
5201
5215 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5216 requires is_const_pattern<N>
5217 constexpr R trimmed_right_with_spaces(T&& pattern) const {
5218 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5219 }
5220 // Триминг по динамическому источнику
5221 // Trimming by dynamic source
5222
5233 template<typename R = str_piece>
5234 constexpr R trimmed(str_piece pattern) const {
5235 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
5236 }
5237
5247 template<typename R = str_piece>
5248 constexpr R trimmed_left(str_piece pattern) const {
5249 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
5250 }
5251
5261 template<typename R = str_piece>
5262 constexpr R trimmed_right(str_piece pattern) const {
5263 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
5264 }
5265
5279 template<typename R = str_piece>
5280 constexpr R trimmed_with_spaces(str_piece pattern) const {
5281 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
5282 }
5283
5297 template<typename R = str_piece>
5298 constexpr R trimmed_left_with_spaces(str_piece pattern) const {
5299 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5300 }
5301
5315 template<typename R = str_piece>
5316 constexpr R trimmed_right_with_spaces(str_piece pattern) const {
5317 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5318 }
5319
5331 template<typename R = str_piece>
5332 constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
5333 str_piece res = *this;
5334 while(res.starts_with(prefix)) {
5335 res.remove_prefix(prefix.length());
5336 if (--max_count == 0) {
5337 break;
5338 }
5339 }
5340 return res;
5341 }
5342
5354 template<typename R = str_piece>
5355 constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
5356 str_piece res = *this;
5357 while(res.starts_with_ia(prefix)) {
5358 res.remove_prefix(prefix.length());
5359 if (--max_count == 0) {
5360 break;
5361 }
5362 }
5363 return res;
5364 }
5365
5377 template<typename R = str_piece>
5378 constexpr R trimmed_suffix(str_piece suffix) const {
5379 str_piece res = *this;
5380 while(res.ends_with(suffix)) {
5381 res.remove_suffix(suffix.length());
5382 }
5383 return res;
5384 }
5385
5397 template<typename R = str_piece>
5398 constexpr R trimmed_suffix_ia(str_piece suffix) const {
5399 str_piece res = *this;
5400 while(res.ends_with_ia(suffix)) {
5401 res.remove_suffix(suffix.length());
5402 }
5403 return res;
5404 }
5405
5415 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
5416 return SplitterBase<K, str_piece>{*this, delimiter};
5417 }
5418};
5419
5420template<size_t N> requires (N > 1)
5421struct find_all_container {
5422 static constexpr size_t max_capacity = N;
5423 size_t positions_[N];
5424 size_t added_{};
5425
5426 constexpr void emplace_back(size_t pos) {
5427 positions_[added_++] = pos;
5428 }
5429};
5430
5449template<typename K>
5450struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
5451 using symb_type = K;
5452 using my_type = str_src<K>;
5453
5454 const symb_type* str;
5455 size_t len;
5456
5457 str_src() = default;
5462 template<typename T, size_t N = const_lit_for<K, T>::Count>
5463 constexpr str_src(T&& v) noexcept : str((const K*)v), len(N - 1) {}
5464
5469 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
5470
5471 template<StrType<K> T>
5472 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
5473
5478 template<typename A>
5479 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
5484 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
5485
5490 constexpr size_t length() const noexcept {
5491 return len;
5492 }
5493
5497 constexpr const symb_type* symbols() const noexcept {
5498 return str;
5499 }
5500
5504 constexpr bool is_empty() const noexcept {
5505 return len == 0;
5506 }
5507
5513 constexpr bool is_same(str_src<K> other) const noexcept {
5514 return str == other.str && len == other.len;
5515 }
5516
5522 constexpr bool is_part_of(str_src<K> other) const noexcept {
5523 return str >= other.str && str + len <= other.str + other.len;
5524 }
5525
5533 constexpr K operator[](size_t idx) const {
5534 return str[idx];
5535 }
5536
5544 constexpr my_type& remove_prefix(size_t delta) {
5545 str += delta;
5546 len -= delta;
5547 return *this;
5548 }
5549
5557 constexpr my_type& remove_suffix(size_t delta) {
5558 len -= delta;
5559 return *this;
5560 }
5561};
5562
5585template<typename K>
5586struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
5587 using symb_type = K;
5588 using my_type = str_src_nt<K>;
5589 using base = str_src<K>;
5590
5591 constexpr static const K empty_string[1] = {0};
5592
5593 str_src_nt() = default;
5610 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
5611 constexpr explicit str_src_nt(T&& p) noexcept {
5612 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
5613 base::str = base::len ? p : empty_string;
5614 }
5615
5619 template<typename T, size_t N = const_lit_for<K, T>::Count>
5620 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
5621
5626 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
5627
5628 template<StrType<K> T>
5629 constexpr str_src_nt(T&& t) {
5630 base::str = t.symbols();
5631 base::len = t.length();
5632 }
5637 template<typename A>
5638 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5639
5640 static const my_type empty_str;
5649 constexpr my_type to_nts(size_t from) {
5650 if (from > base::len) {
5651 from = base::len;
5652 }
5653 return {base::str + from, base::len - from};
5654 }
5655};
5656
5657template<typename K>
5658inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5659template<typename K> struct simple_str_selector;
5660
5661inline namespace literals {
5721template<char...Chars>
5722SS_CONSTEVAL auto operator""_fmt() {
5723 return f::skip_0x<Chars...>();
5724}
5725
5726} // namespace literals
5727
5728#ifndef IN_FULL_SIMSTR
5729
5730template<typename K>
5731using simple_str = str_src<K>;
5732
5733template<typename K>
5734struct simple_str_selector {
5735 using type = simple_str<K>;
5736};
5737
5738template<typename K>
5739using simple_str_nt = str_src_nt<K>;
5740
5741template<typename K>
5742using Splitter = SplitterBase<K, str_src<K>>;
5743
5744using ssa = str_src<u8s>;
5745using ssb = str_src<ubs>;
5746using ssw = str_src<wchar_t>;
5747using ssu = str_src<u16s>;
5748using ssuu = str_src<u32s>;
5749using stra = str_src_nt<u8s>;
5750using strb = str_src_nt<ubs>;
5751using strw = str_src_nt<wchar_t>;
5752using stru = str_src_nt<u16s>;
5753using struu = str_src_nt<u32s>;
5754
5755template<typename K>
5756consteval 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) {
5757 if constexpr (std::is_same_v<K, u8s>)
5758 return s8;
5759 if constexpr (std::is_same_v<K, ubs>)
5760 return sb;
5761 if constexpr (std::is_same_v<K, uws>)
5762 return sw;
5763 if constexpr (std::is_same_v<K, u16s>)
5764 return s16;
5765 if constexpr (std::is_same_v<K, u32s>)
5766 return s32;
5767}
5768
5769#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5770
5771inline namespace literals {
5772
5783SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
5784 return str_src_nt<u8s>{ptr, l};
5785}
5786
5796SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
5797 return str_src_nt<ubs>{ptr, l};
5798}
5799
5809SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
5810 return str_src_nt<uws>{ptr, l};
5811}
5812
5822SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
5823 return str_src_nt<u16s>{ptr, l};
5824}
5825
5836SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
5837 return str_src_nt<u32s>{ptr, l};
5838}
5839
5840} // namespace literals
5841
5842#endif
5843
5844template<typename K>
5845constexpr size_t slice_simple_find_func(str_src<K> str, str_src<K> pattern, size_t offset){return str.find(pattern, offset);}
5846template<typename K>
5847constexpr size_t slice_simple_find_rev_func(str_src<K> str, str_src<K> pattern, size_t offset){return str.find_last(pattern, offset);}
5848
5849template<typename K, bool ToEndOfPattern, bool OffsetFromFound, bool AllAtFail, auto F = slice_simple_find_func<K>>
5850struct slice_simple_find : slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, false, F> {
5851 using slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, false, F>::slice_find;
5852};
5853
5854template<typename K>
5855struct begin_or_all : slice_simple_find<K, false, true, true> {
5856 using base = slice_simple_find<K, false, true, true>;
5857 using base::base;
5858};
5859template<typename K>
5860struct end_or_all : slice_simple_find<K, true, true, true> {
5861 using base = slice_simple_find<K, true, true, true>;
5862 using base::base;
5863};
5864
5865template<typename K>
5866struct begin_or_empty : slice_simple_find<K, false, true, false> {
5867 using base = slice_simple_find<K, false, true, false>;
5868 using base::base;
5869};
5870template<typename K>
5871struct end_or_empty : slice_simple_find<K, true, true, false> {
5872 using base = slice_simple_find<K, true, true, false>;
5873 using base::base;
5874};
5875
5876template<typename K, bool ToEndOfPattern, bool OffsetFromFound, bool AllAtFail, auto F = slice_simple_find_rev_func<K>>
5877struct slice_simple_find_rev : slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, true, F> {
5878 using slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, true, F>::slice_find;
5879};
5880
5881template<typename K>
5882struct begin_last_or_all : slice_simple_find_rev<K, false, false, true> {
5883 using base = slice_simple_find_rev<K, false, false, true>;
5884 using base::base;
5885};
5886
5887template<typename K>
5888struct end_last_or_all : slice_simple_find_rev<K, true, false, true> {
5889 using base = slice_simple_find_rev<K, true, false, true>;
5890 using base::base;
5891};
5892
5893template<typename K>
5894struct begin_last_or_empty : slice_simple_find_rev<K, false, false, false> {
5895 using base = slice_simple_find_rev<K, false, false, false>;
5896 using base::base;
5897};
5898
5899template<typename K>
5900struct end_last_or_empty : slice_simple_find_rev<K, true, false, false> {
5901 using base = slice_simple_find_rev<K, true, false, false>;
5902 using base::base;
5903};
5904
5905#define CTAD_SLICE_FIND(name) template<StrSource S> name(S&&) -> name<symb_type_from_src_t<S>>;\
5906 template<StrSource S> name(S&&, size_t) -> name<symb_type_from_src_t<S>>;\
5907 template<StrSource S> name(S&&, size_t, size_t) -> name<symb_type_from_src_t<S>>
5908
5909CTAD_SLICE_FIND(begin_or_all);
5910CTAD_SLICE_FIND(end_or_all);
5911CTAD_SLICE_FIND(begin_or_empty);
5912CTAD_SLICE_FIND(end_or_empty);
5913CTAD_SLICE_FIND(begin_last_or_all);
5914CTAD_SLICE_FIND(end_last_or_all);
5915CTAD_SLICE_FIND(begin_last_or_empty);
5916CTAD_SLICE_FIND(end_last_or_empty);
5917
5918inline namespace literals {
5919
5920#define FIND_LITERAL(name, type, suffix) SS_CONSTEVAL name<type> operator ""_##suffix(const type* ptr, size_t l) {return str_src<type>{ptr, l};}
5921#define FIND_LITERALS(name, suffix) \
5922 FIND_LITERAL(name, u8s, suffix) \
5923 FIND_LITERAL(name, ubs, suffix) \
5924 FIND_LITERAL(name, u16s, suffix) \
5925 FIND_LITERAL(name, u32s, suffix) \
5926 FIND_LITERAL(name, wchar_t, suffix)
5927
5928FIND_LITERALS(begin_or_all, boa) // Begin of pattern Or All
5929FIND_LITERALS(end_or_all, eoa) // End of pattern Or All
5930FIND_LITERALS(begin_or_empty, boe) // Begin of pattern Or Empty
5931FIND_LITERALS(end_or_empty, eoe) // End of pattern Or Empty
5932FIND_LITERALS(begin_last_or_all, bloa) // Begin of Last pattern Or All
5933FIND_LITERALS(end_last_or_all, eloa) // End of Last pattern Or All
5934FIND_LITERALS(begin_last_or_empty, bloe) // Begin of Last pattern Or Empty
5935FIND_LITERALS(end_last_or_empty, eloe) // End of Last pattern Or Empty
5936
5937} // namespace literals
5938
5939template<typename K, bool withSpaces>
5940struct CheckSpaceTrim {
5941 constexpr bool is_trim_spaces(K s) const {
5942 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
5943 }
5944};
5945template<typename K>
5946struct CheckSpaceTrim<K, false> {
5947 constexpr bool is_trim_spaces(K) const {
5948 return false;
5949 }
5950};
5951
5952template<typename K>
5953struct CheckSymbolsTrim {
5954 str_src<K> symbols;
5955 constexpr bool is_trim_symbols(K s) const {
5956 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
5957 }
5958};
5959
5960template<typename K, size_t N>
5961struct CheckConstSymbolsTrim {
5962 const const_lit_to_array<K, N> symbols;
5963
5964 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
5965 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5966
5967 constexpr bool is_trim_symbols(K s) const noexcept {
5968 return symbols.contain(s);
5969 }
5970};
5971
5972template<typename K>
5973struct CheckConstSymbolsTrim<K, 0> {
5974 constexpr bool is_trim_symbols(K) const {
5975 return false;
5976 }
5977};
5978
5979template<typename K, size_t N>
5980struct SymbSelector {
5981 using type = CheckConstSymbolsTrim<K, N>;
5982};
5983
5984template<typename K>
5985struct SymbSelector<K, 0> {
5986 using type = CheckSymbolsTrim<K>;
5987};
5988
5989template<typename K>
5990struct SymbSelector<K, static_cast<size_t>(-1)> {
5991 using type = CheckConstSymbolsTrim<K, 0>;
5992};
5993
5994template<TrimSides S, typename K, size_t N, bool withSpaces>
5995struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5996 constexpr bool isTrim(K s) const {
5997 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5998 }
5999 constexpr str_src<K> operator()(str_src<K> from) const {
6000 if constexpr ((S & TrimSides::TrimLeft) != 0) {
6001 while (from.len) {
6002 if (isTrim(*from.str)) {
6003 from.str++;
6004 from.len--;
6005 } else
6006 break;
6007 }
6008 }
6009 if constexpr ((S & TrimSides::TrimRight) != 0) {
6010 const K* back = from.str + from.len - 1;
6011 while (from.len) {
6012 if (isTrim(*back)) {
6013 back--;
6014 from.len--;
6015 } else
6016 break;
6017 }
6018 }
6019 return from;
6020 }
6021};
6022
6023template<TrimSides S, typename K>
6024using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
6025
6026template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
6027 requires is_const_pattern<N>
6028constexpr inline auto trimOp(T&& pattern) {
6029 return trim_operator<S, K, N - 1, withSpaces>{pattern};
6030}
6031
6032template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
6033constexpr inline auto trimOp(str_src<K> pattern) {
6034 return trim_operator<S, K, 0, withSpaces>{pattern};
6035}
6036
6037static constexpr size_t FIND_CACHE_SIZE = 16;
6038
6039template<typename K, size_t N, size_t L>
6040struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
6041 using symb_type = K;
6042 using my_type = expr_replaces<K, N, L>;
6043 str_src<K> what;
6044 const K(&pattern)[N + 1];
6045 const K(&repl)[L + 1];
6046 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6047 mutable size_t last_;
6048
6049 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
6050
6051 constexpr size_t length() const {
6052 size_t l = what.length();
6053 if constexpr (N == L) {
6054 return l;
6055 }
6056 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
6057 if (matches_.added_) {
6058 last_ = matches_.positions_[matches_.added_ - 1] + N;
6059 l += int(L - N) * matches_.added_;
6060
6061 if (matches_.added_ == FIND_CACHE_SIZE) {
6062 for (;;) {
6063 size_t next = what.find(pattern, N, last_);
6064 if (next == str::npos) {
6065 break;
6066 }
6067 last_ = next + N;
6068 l += L - N;
6069 }
6070 }
6071 }
6072 if (!l) {
6073 matches_.added_ = -1;
6074 }
6075 return l;
6076 }
6077 constexpr K* place(K* ptr) const noexcept {
6078 if constexpr (N == L) {
6079 const K* from = what.symbols();
6080 for (size_t start = 0; start < what.length();) {
6081 size_t next = what.find(pattern, N, start);
6082 if (next == str::npos) {
6083 next = what.length();
6084 }
6085 size_t delta = next - start;
6086 ch_traits<K>::copy(ptr, from + start, delta);
6087 ptr += delta;
6088 ch_traits<K>::copy(ptr, repl, L);
6089 ptr += L;
6090 start = next + N;
6091 }
6092 return ptr;
6093 }
6094 if (matches_.added_ == 0) {
6095 return what.place(ptr);
6096 } else if (matches_.added_ == size_t(-1)) {
6097 // after replaces text become empty
6098 return ptr;
6099 }
6100 const K* from = what.symbols();
6101 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
6102 ch_traits<K>::copy(ptr, from + start, offset - start);
6103 ptr += offset - start;
6104 ch_traits<K>::copy(ptr, repl, L);
6105 ptr += L;
6106 start = offset + N;
6107 if (start >= last_) {
6108 size_t tail = what.length() - last_;
6109 ch_traits<K>::copy(ptr, from + last_, tail);
6110 ptr += tail;
6111 break;
6112 } else {
6113 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
6114 }
6115 }
6116 return ptr;
6117 }
6118};
6119
6133template<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>
6134 requires(N > 1)
6135constexpr auto e_repl(A&& w, T&& p, X&& r) {
6136 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
6137}
6138
6146template<typename K>
6147struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
6148 using symb_type = K;
6149 using my_type = expr_replaced<K>;
6150 str_src<K> what;
6151 const str_src<K> pattern;
6152 const str_src<K> repl;
6153 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6154 mutable size_t last_;
6165 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
6166
6167 constexpr size_t length() const {
6168 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
6169 if (!plen || plen == rlen) {
6170 return l;
6171 }
6172 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
6173 if (matches_.added_) {
6174 last_ = matches_.positions_[matches_.added_ - 1] + plen;
6175 l += int(rlen - plen) * matches_.added_;
6176
6177 if (matches_.added_ == FIND_CACHE_SIZE) {
6178 for (;;) {
6179 size_t next = what.find(pattern.symbols(), plen, last_);
6180 if (next == str::npos) {
6181 break;
6182 }
6183 last_ = next + plen;
6184 l += rlen - plen;
6185 }
6186 }
6187 }
6188 if (!l) {
6189 matches_.added_ = -1;
6190 }
6191 return l;
6192 }
6193 constexpr K* place(K* ptr) const noexcept {
6194 size_t plen = pattern.length(), rlen = repl.length();
6195 if (plen == rlen) {
6196 const K* from = what.symbols();
6197 for (size_t start = 0; start < what.length();) {
6198 size_t next = what.find(pattern, start);
6199 if (next == str::npos) {
6200 next = what.length();
6201 }
6202 size_t delta = next - start;
6203 ch_traits<K>::copy(ptr, from + start, delta);
6204 ptr += delta;
6205 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
6206 ptr += rlen;
6207 start = next + plen;
6208 }
6209 return ptr;
6210 }
6211 if (matches_.added_ == 0) {
6212 return what.place(ptr);
6213 } else if (matches_.added_ == -1) {
6214 // after replaces text become empty
6215 return ptr;
6216 }
6217 const K* from = what.symbols();
6218 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
6219 ch_traits<K>::copy(ptr, from + start, offset - start);
6220 ptr += offset - start;
6221 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
6222 ptr += rlen;
6223 start = offset + plen;
6224 if (start >= last_) {
6225 size_t tail = what.length() - start;
6226 ch_traits<K>::copy(ptr, from + start, tail);
6227 ptr += tail;
6228 break;
6229 } else {
6230 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
6231 }
6232 }
6233 return ptr;
6234 }
6235};
6236
6252template<typename K, StrExprForType<K> E>
6253struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
6254 using symb_type = K;
6255 using my_type = expr_replaced<K>;
6256 str_src<K> what;
6257 const str_src<K> pattern;
6258 mutable size_t replLen;
6259 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6260 mutable size_t last_;
6261 const E& expr;
6272 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
6273
6274 constexpr size_t length() const {
6275 size_t l = what.length(), plen = pattern.length();
6276 if (!plen) {
6277 return l;
6278 }
6279 matches_.positions_[0] = what.find(pattern);
6280 if (matches_.positions_[0] == -1) {
6281 // Не нашли вхождений, нечего менять
6282 return l;
6283 }
6284 matches_.added_ = 1;
6285 // Вхождение есть, надо теперь получить длину замены
6286 replLen = expr.length();
6287 if (replLen == plen) {
6288 // Замена той же длины, общая длина не изменится
6289 return l;
6290 }
6291 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
6292
6293 last_ = matches_.positions_[matches_.added_ - 1] + plen;
6294 l += int(replLen - plen) * matches_.added_;
6295
6296 if (matches_.added_ == FIND_CACHE_SIZE) {
6297 for (;;) {
6298 size_t next = what.find(pattern.symbols(), plen, last_);
6299 if (next == str::npos) {
6300 break;
6301 }
6302 last_ = next + plen;
6303 l += replLen - plen;
6304 }
6305 }
6306 if (!l) {
6307 matches_.added_ = -1;
6308 }
6309 return l;
6310 }
6311 constexpr K* place(K* ptr) const noexcept {
6312 if (matches_.added_ == 0) {
6313 // не было найдено вхождений
6314 return what.place(ptr);
6315 } else if (matches_.added_ == -1) {
6316 // Строка стала пустой
6317 return ptr;
6318 }
6319 size_t plen = pattern.length();
6320 const K* from = what.symbols();
6321 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
6322 ptr += matches_.positions_[0];
6323 const K* repl = ptr;
6324 expr.place((typename E::symb_type*)ptr);
6325 ptr += replLen;
6326 size_t start = matches_.positions_[0] + plen;
6327
6328 if (plen == replLen) {
6329 for (;;) {
6330 size_t next = what.find(pattern, start);
6331 if (next == str::npos) {
6332 break;
6333 }
6334 size_t delta = next - start;
6335 ch_traits<K>::copy(ptr, from + start, delta);
6336 ptr += delta;
6337 ch_traits<K>::copy(ptr, repl, replLen);
6338 ptr += replLen;
6339 start = next + plen;
6340 }
6341 } else {
6342 for (size_t idx = 1;;) {
6343 if (start >= last_) {
6344 break;
6345 }
6346 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
6347 size_t delta = next - start;
6348 ch_traits<K>::copy(ptr, from + start, delta);
6349 ptr += delta;
6350 ch_traits<K>::copy(ptr, repl, replLen);
6351 ptr += replLen;
6352 start = next + plen;
6353 }
6354 }
6355 size_t tail = what.length() - start;
6356 ch_traits<K>::copy(ptr, from + start, tail);
6357 return ptr + tail;
6358 }
6359};
6360
6374template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, typename X>
6375 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>))
6376constexpr auto e_repl(A&& w, T&& p, X&& r) {
6377 str_src<K> pattern{std::forward<T>(p)};
6378 str_src<K> repl{std::forward<X>(r)};
6379 return expr_replaced<K>{std::forward<A>(w), pattern, repl};
6380}
6381
6395template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, StrExprForType<K> E>
6396 requires std::is_constructible_v<str_src<K>, T>
6397constexpr auto e_repl(A&& w, T&& p, const E& expr) {
6398 str_src<K> pattern{std::forward<T>(p)};
6399 return expr_replaced_e<K, E>{std::forward<A>(w), pattern, expr};
6400}
6401
6402template<bool UseVectorForReplace>
6403struct replace_search_result_store {
6404 size_t count_{};
6405 std::pair<size_t, size_t> replaces_[16];
6406};
6407
6408template<>
6409struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6410
6411// Строковое выражение для замены символов
6412// String expression to replace characters
6413template<typename K, size_t N, bool UseVectorForReplace>
6414struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
6415 using symb_type = K;
6416 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6417 const K pattern_[N];
6418 const str_src<K> source_;
6419 const str_src<K> replaces_[N];
6420
6421 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6422
6423 [[_no_unique_address]]
6424 std::array<uu8s, N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0> bit_mask_{};
6425
6426 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
6427 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6428
6429 size_t length() const {
6430 size_t l = source_.length();
6431 auto [fnd, num] = find_first_of(source_.str, source_.len);
6432 if (fnd == str::npos) {
6433 return l;
6434 }
6435 l += replaces_[num].len - 1;
6436 if constexpr (UseVectorForReplace) {
6437 search_results_.reserve((l >> 4) + 8);
6438 search_results_.emplace_back(fnd, num);
6439 for (size_t start = fnd + 1;;) {
6440 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6441 if (fnd == str::npos) {
6442 break;
6443 }
6444 search_results_.emplace_back(fnd, idx);
6445 start = fnd + 1;
6446 l += replaces_[idx].len - 1;
6447 }
6448 } else {
6449 const size_t max_store = std::size(search_results_.replaces_);
6450 search_results_.replaces_[0] = {fnd, num};
6451 search_results_.count_++;
6452 for (size_t start = fnd + 1;;) {
6453 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6454 if (found == str::npos) {
6455 break;
6456 }
6457 if (search_results_.count_ < max_store) {
6458 search_results_.replaces_[search_results_.count_] = {found, idx};
6459 }
6460 l += replaces_[idx].len - 1;
6461 search_results_.count_++;
6462 start = found + 1;
6463 }
6464 }
6465 return l;
6466 }
6467 K* place(K* ptr) const noexcept {
6468 size_t start = 0;
6469 const K* text = source_.str;
6470 if constexpr (UseVectorForReplace) {
6471 for (const auto& [pos, num] : search_results_) {
6472 size_t delta = pos - start;
6473 ch_traits<K>::copy(ptr, text + start, delta);
6474 ptr += delta;
6475 ptr = replaces_[num].place(ptr);
6476 start = pos + 1;
6477 }
6478 } else {
6479 const size_t max_store = std::size(search_results_.replaces_);
6480 size_t founded = search_results_.count_;
6481 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6482 const auto [pos, num] = search_results_.replaces_[idx];
6483 size_t delta = pos - start;
6484 ch_traits<K>::copy(ptr, text + start, delta);
6485 ptr += delta;
6486 ptr = replaces_[num].place(ptr);
6487 start = pos + 1;
6488 }
6489 if (founded > max_store) {
6490 founded -= max_store;
6491 while (founded--) {
6492 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6493 size_t delta = fnd - start;
6494 ch_traits<K>::copy(ptr, text + start, delta);
6495 ptr += delta;
6496 ptr = replaces_[idx].place(ptr);
6497 start = fnd + 1;
6498 }
6499 }
6500 }
6501 size_t tail = source_.len - start;
6502 ch_traits<K>::copy(ptr, text + start, tail);
6503 return ptr + tail;
6504 }
6505
6506protected:
6507 template<typename ... Repl>
6508 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
6509 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6510
6511 template<typename ... Repl> requires (sizeof...(Repl) == N)
6512 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
6513 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6514 {
6515 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6516 for (size_t idx = 0; idx < N; idx++) {
6517 uu8s s = static_cast<uu8s>(pattern_[idx]);
6518 if constexpr (sizeof(K) == 1) {
6519 bit_mask_[s >> 3] |= 1 << (s & 7);
6520 } else {
6521 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6522 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6523 } else {
6524 bit_mask_[s >> 3] |= 1 << (s & 7);
6525 }
6526 }
6527 }
6528 }
6529 }
6530
6531 template<size_t Idx>
6532 size_t index_of(K s) const {
6533 if constexpr (Idx < N) {
6534 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6535 }
6536 return -1;
6537 }
6538 bool is_in_mask(uu8s s) const {
6539 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6540 }
6541 bool is_in_mask2(uu8s s) const {
6542 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6543 }
6544 bool is_in_pattern(K s, size_t& idx) const {
6545 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6546 if constexpr (sizeof(K) == 1) {
6547 if (is_in_mask(s)) {
6548 idx = index_of<0>(s);
6549 return true;
6550 }
6551 } else {
6552 if (std::make_unsigned_t<const K>(s) > 255) {
6553 if (is_in_mask2(s)) {
6554 return (idx = index_of<0>(s)) != -1;
6555 }
6556 } else {
6557 if (is_in_mask(s)) {
6558 idx = index_of<0>(s);
6559 return true;
6560 }
6561 }
6562 }
6563 }
6564 return false;
6565 }
6566 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6567 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6568 size_t idx;
6569 while (offset < len) {
6570 if (is_in_pattern(text[offset], idx)) {
6571 return {offset, idx};
6572 }
6573 offset++;
6574 }
6575 } else {
6576 while (offset < len) {
6577 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6578 return {offset, idx};
6579 }
6580 offset++;
6581 }
6582 }
6583 return {-1, -1};
6584 }
6585};
6586
6626template<bool UseVector = false, StrSource A, typename K = symb_type_from_src_t<A>, typename ... Repl>
6627 requires (sizeof...(Repl) % 2 == 0)
6628auto e_repl_const_symbols(A&& src, Repl&& ... other) {
6629 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
6630}
6631
6671template<typename K, bool UseVectorForReplace = false>
6672struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
6673 using symb_type = K;
6674 using str_t = typename simple_str_selector<K>::type;
6675 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6676
6677 const str_src<K> source_;
6678 const std::vector<std::pair<K, str_t>>& replaces_;
6679
6680 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
6681
6682 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6683
6684 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6712 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
6713 : source_(source), replaces_(repl)
6714 {
6715 size_t pattern_len = replaces_.size();
6716 pattern_.resize(pattern_len);
6717 K* pattern = pattern_.data();
6718
6719 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6720 *pattern++ = replaces_[idx].first;
6721 }
6722
6723 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6724 for (size_t idx = 0; idx < pattern_len; idx++) {
6725 uu8s s = static_cast<uu8s>(pattern_[idx]);
6726 if constexpr (sizeof(K) == 1) {
6727 bit_mask_[s >> 3] |= (1 << (s & 7));
6728 } else {
6729 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6730 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6731 } else {
6732 bit_mask_[s >> 3] |= (1 << (s & 7));
6733 }
6734 }
6735 }
6736 }
6737 }
6738
6739 size_t length() const {
6740 size_t l = source_.length();
6741 auto [fnd, num] = find_first_of(source_.str, source_.len);
6742 if (fnd == str::npos) {
6743 return l;
6744 }
6745 l += replaces_[num].second.len - 1;
6746 if constexpr (UseVectorForReplace) {
6747 search_results_.reserve((l >> 4) + 8);
6748 search_results_.emplace_back(fnd, num);
6749 for (size_t start = fnd + 1;;) {
6750 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6751 if (fnd == str::npos) {
6752 break;
6753 }
6754 search_results_.emplace_back(fnd, idx);
6755 start = fnd + 1;
6756 l += replaces_[idx].second.len - 1;
6757 }
6758 } else {
6759 const size_t max_store = std::size(search_results_.replaces_);
6760 search_results_.replaces_[0] = {fnd, num};
6761 search_results_.count_++;
6762 for (size_t start = fnd + 1;;) {
6763 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6764 if (found == str::npos) {
6765 break;
6766 }
6767 if (search_results_.count_ < max_store) {
6768 search_results_.replaces_[search_results_.count_] = {found, idx};
6769 }
6770 l += replaces_[idx].second.len - 1;
6771 search_results_.count_++;
6772 start = found + 1;
6773 }
6774 }
6775 return l;
6776 }
6777 K* place(K* ptr) const noexcept {
6778 size_t start = 0;
6779 const K* text = source_.str;
6780 if constexpr (UseVectorForReplace) {
6781 for (const auto& [pos, num] : search_results_) {
6782 size_t delta = pos - start;
6783 ch_traits<K>::copy(ptr, text + start, delta);
6784 ptr += delta;
6785 ptr = replaces_[num].second.place(ptr);
6786 start = pos + 1;
6787 }
6788 } else {
6789 const size_t max_store = std::size(search_results_.replaces_);
6790 size_t founded = search_results_.count_;
6791 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6792 const auto [pos, num] = search_results_.replaces_[idx];
6793 size_t delta = pos - start;
6794 ch_traits<K>::copy(ptr, text + start, delta);
6795 ptr += delta;
6796 ptr = replaces_[num].second.place(ptr);
6797 start = pos + 1;
6798 }
6799 if (founded > max_store) {
6800 founded -= max_store;
6801 while (founded--) {
6802 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6803 size_t delta = fnd - start;
6804 ch_traits<K>::copy(ptr, text + start, delta);
6805 ptr += delta;
6806 ptr = replaces_[idx].second.place(ptr);
6807 start = fnd + 1;
6808 }
6809 }
6810 }
6811 size_t tail = source_.len - start;
6812 ch_traits<K>::copy(ptr, text + start, tail);
6813 return ptr + tail;
6814 }
6815
6816protected:
6817 size_t index_of(K s) const {
6818 return pattern_.find(s);
6819 }
6820
6821 bool is_in_mask(uu8s s) const {
6822 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6823 }
6824 bool is_in_mask2(uu8s s) const {
6825 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6826 }
6827
6828 bool is_in_pattern(K s, size_t& idx) const {
6829 if constexpr (sizeof(K) == 1) {
6830 if (is_in_mask(s)) {
6831 idx = index_of(s);
6832 return true;
6833 }
6834 } else {
6835 if (std::make_unsigned_t<const K>(s) > 255) {
6836 if (is_in_mask2(s)) {
6837 return (idx = index_of(s)) != -1;
6838 }
6839 } else {
6840 if (is_in_mask(s)) {
6841 idx = index_of(s);
6842 return true;
6843 }
6844 }
6845 }
6846 return false;
6847 }
6848
6849 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6850 size_t pl = pattern_.length();
6851 if (pl >= BIT_SEARCH_TRESHHOLD) {
6852 size_t idx;
6853 while (offset < len) {
6854 if (is_in_pattern(text[offset], idx)) {
6855 return {offset, idx};
6856 }
6857 offset++;
6858 }
6859 } else {
6860 while (offset < len) {
6861 if (size_t idx = index_of(text[offset]); idx != -1) {
6862 return {offset, idx};
6863 }
6864 offset++;
6865 }
6866 }
6867 return {-1, -1};
6868 }
6869};
6870
6871template<typename K, StrExprForType<K> T>
6872struct force_copy {
6873 const T& t_;
6874 force_copy(const T& t) : t_(t){}
6875};
6876
6877template<typename K, typename T>
6878struct symb_type_from_src<force_copy<K, T>> {
6879 using type = K;
6880};
6881
6882
6883template<StrExpr T>
6884force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6885
6886template<typename K, typename T>
6887constexpr auto to_subst(T&& t) {
6888 return to_strexpr<K>(std::forward<T>(t));
6889}
6890
6891template<typename K, typename T, size_t N>
6892constexpr auto to_subst(const T(&t)[N]) {
6893 return expr_literal<T, N - 1>{t};
6894}
6895
6896template<typename K, StrExprForType<K> T>
6897constexpr decltype(auto) to_subst(T&& t) {
6898 return std::forward<T>(t);
6899}
6900
6901template<typename K, typename T>
6902constexpr T to_subst(const force_copy<K, T>& t) {
6903 return t.t_;
6904}
6905
6906template<typename K, typename T>
6907constexpr T to_subst(force_copy<K, T>&& t) {
6908 return t.t_;
6909}
6910
6911template<typename K, typename T>
6912constexpr T to_subst(force_copy<K, T>& t) {
6913 return t.t_;
6914}
6915
6916template<typename K, typename T>
6917using to_str_exp_t = decltype(to_subst<K>(std::declval<T>()));
6918
6926template<typename K, typename G, typename Arg, typename...Args>
6927struct e_concat : expr_to_std_string<e_concat<K, G, Arg, Args...>> {
6928 using symb_type = K;
6929 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6930 using arg_t = to_str_exp_t<K, Arg>;
6931 to_str_exp_t<K, G> glue_;
6932 arg_t arg_;
6933 store_t args_;
6934 mutable size_t glue_len_;
6967 constexpr e_concat(G&& glue, Arg&& arg, Args&&...args)
6968 : glue_(to_subst<K>(std::forward<G>(glue)))
6969 , arg_(to_subst<K>(std::forward<Arg>(arg)))
6970 , args_(to_subst<K>(std::forward<Args>(args))...) {}
6971
6972 constexpr size_t length() const noexcept {
6973 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6974 glue_len_ = glue_.length();
6975 size_t l = arg_.length() + glue_len_ * sizeof...(Args);
6976 ((l += std::get<Indexes>(args_).length()),...);
6977 return l;
6978 }(std::make_index_sequence<sizeof...(Args)>());
6979 }
6980 constexpr K* place(K* ptr) const noexcept {
6981 return [this]<size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6982 ptr = (K*)arg_.place((typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6983 const K* glueStart = ptr;
6984 ptr = glue_.place(ptr);
6985 (
6986 (
6987 ptr = (K*)std::get<Indexes>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6988 glue_len_ > 0 && Indexes < sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) : nullptr
6989 ),
6990 ...);
6991 return ptr;
6992 }(ptr, std::make_index_sequence<sizeof...(Args)>());
6993 }
6994};
6995// CTAD deducing rule for e_concat
6996template<typename T, typename ... Args> requires (sizeof...(Args) > 1)
6997e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6998
6999struct parse_subst_string_error {
7000 parse_subst_string_error(const char*){}
7001};
7002
7003namespace details {
7004
7005template<typename K, size_t NParams>
7006constexpr size_t parse_pattern_string(str_src<K> pattern, auto&& add_part, auto&& add_param) {
7007 char used_args[NParams] = {0};
7008 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
7009 size_t all_len = 0;
7010
7011 auto find = [](const K* from, const K* last, K s) {
7012 while (from != last) {
7013 if (*from == s) {
7014 break;
7015 }
7016 from++;
7017 }
7018 return from;
7019 };
7020 size_t idx_in_params = 0;
7021
7022 while (first != last) {
7023 const K* open_pos = first;
7024 if (*first != '{') {
7025 open_pos = find(first, last, '{');
7026
7027 for (;;) {
7028 const K* close_pos = find(first, open_pos, '}');
7029 if (close_pos == open_pos) {
7030 unsigned len = open_pos - first;
7031 add_part(first - start, len);
7032 all_len += len;
7033 break;
7034 }
7035 ++close_pos;
7036 if (close_pos == open_pos || *close_pos != '}') {
7037 throw parse_subst_string_error{"unescaped }"};
7038 }
7039 unsigned len = close_pos - first;
7040 add_part(first - start, len);
7041 all_len += len;
7042 first = ++close_pos;
7043 }
7044 if (open_pos == last) {
7045 break;
7046 }
7047 }
7048 if (++open_pos == last) {
7049 throw parse_subst_string_error{"unescaped {"};
7050 }
7051 if (*open_pos == '}') {
7052 if (idx_in_params == -1) {
7053 throw parse_subst_string_error{"already used param ids"};
7054 }
7055 if (idx_in_params == NParams) {
7056 throw parse_subst_string_error{"too many params"};
7057 }
7058 used_args[idx_in_params] = 1;
7059 add_param(idx_in_params++);
7060 first = open_pos + 1;
7061 } else if (*open_pos == '{') {
7062 add_part(open_pos - start, 1);
7063 all_len++;
7064 first = open_pos + 1;
7065 } else {
7066 if (idx_in_params != 0 && idx_in_params != -1) {
7067 throw parse_subst_string_error{"already used non id params"};
7068 }
7069 idx_in_params = -1;
7070 const K* end = find(open_pos, last, '}');
7071 if (end == last) {
7072 throw parse_subst_string_error{"not found }"};
7073 }
7074 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
7075 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
7076 throw parse_subst_string_error{"bad param id"};
7077 }
7078 used_args[--p] = 1;
7079 add_param(p);
7080 first = end + 1;
7081 }
7082 }
7083 for (auto c : used_args) {
7084 if (!c) {
7085 throw parse_subst_string_error{"unused param"};
7086 }
7087 }
7088 return all_len;
7089}
7090
7091struct portion {
7092 unsigned start: 16;
7093 unsigned len: 15;
7094 unsigned is_param: 1;
7095
7096 portion() = default;
7097
7098 constexpr void set_param(unsigned param) {
7099 if (param >= (1 << 16)) {
7100 throw parse_subst_string_error{"the parameter id is too large"};
7101 }
7102 start = param;
7103 is_param = 1;
7104 }
7105 constexpr void set_part(unsigned from, unsigned l) {
7106 if (from >= (1 << 16) || l >= (1 << 15)) {
7107 throw parse_subst_string_error{"the string part is too large"};
7108 }
7109 start = from;
7110 len = l;
7111 is_param = 0;
7112 }
7113};
7114
7115template<typename K, size_t PtLen, size_t NParams>
7116struct subst_params {
7117 const K(&source_)[PtLen];
7118 unsigned all_len_{};
7119 unsigned actual_{};
7120 // The pattern string can be divided into a maximum of this number of portions.
7121 // "a{}a{}a{}a" - One portion of one symbol from the edge, and two portions for every three symbols
7122 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
7123
7124 consteval subst_params(const K(&pattern)[PtLen]) : source_(pattern) {
7125 all_len_ = parse_pattern_string<K, NParams>(pattern,
7126 [this](unsigned from, unsigned len) {
7127 portions_[actual_++].set_part(from, len);
7128 }, [this](unsigned param) {
7129 portions_[actual_++].set_param(param);
7130 });
7131 }
7132};
7133
7134} // namespace details
7135
7141template<typename K, size_t PtLen, typename ... Args>
7142struct e_subst : expr_to_std_string<e_subst<K, PtLen, Args...>> {
7143 inline static constexpr size_t NParams = sizeof...(Args);
7144 using symb_type = K;
7145 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
7146
7147 const details::subst_params<K, PtLen, NParams>& subst_;
7148 store_t args_;
7189 constexpr e_subst(const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
7190 : subst_(subst)
7191 , args_(to_subst<K>(std::forward<Args>(args))...){}
7192
7193 constexpr size_t length() const noexcept {
7194 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
7195 size_t idx = 0;
7196 size_t expr_length_[NParams] = {};
7197 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
7198 size_t l = subst_.all_len_;
7199 for (idx = 0; idx < subst_.actual_; idx++) {
7200 if (subst_.portions_[idx].is_param) {
7201 l += expr_length_[subst_.portions_[idx].start];
7202 }
7203 }
7204 return l;
7205 }(std::make_index_sequence<sizeof...(Args)>());
7206 }
7207 template<size_t Idx>
7208 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
7209 if (idx == Idx) {
7210 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
7211 }
7212 if constexpr (Idx < NParams - 1) {
7213 return place_idx<Idx + 1>(ptr, idx);
7214 }
7215 return ptr;
7216 }
7217 constexpr K* place(K* ptr) const noexcept {
7218 for (size_t idx = 0; idx < subst_.actual_; idx++) {
7219 if (subst_.portions_[idx].is_param) {
7220 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
7221 } else {
7222 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
7223 ptr += subst_.portions_[idx].len;
7224 }
7225 }
7226 return ptr;
7227 }
7228};
7229
7230// CTAD deducing rule for e_subst
7231template<typename K, size_t N, typename...Args> requires (sizeof...(Args) > 0)
7232e_subst(const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
7233
7239template<typename K, typename ... Args>
7240struct e_vsubst : expr_to_std_string<e_vsubst<K, Args...>> {
7241 inline static constexpr size_t Nparams = sizeof...(Args);
7242 using symb_type = K;
7243 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
7244
7245 details::portion portions_[Nparams * 3];
7246 std::vector<details::portion> more_portions_;
7247 store_t args_;
7248 str_src<K> pattern_;
7249 size_t all_len_{};
7250 unsigned actual_{};
7288 constexpr e_vsubst(str_src<K> pattern, Args&&...args)
7289 : pattern_(pattern)
7290 , args_(to_subst<K>(std::forward<Args>(args))...) {
7291
7292 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [this](unsigned from, unsigned len) {
7293 if (actual_ < std::size(portions_)) {
7294 portions_[actual_++].set_part(from, len);
7295 } else {
7296 if (actual_ == std::size(portions_)) {
7297 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
7298 }
7299 more_portions_.emplace_back().set_part(from, len);
7300 }
7301 }, [this](unsigned param) {
7302 if (actual_ < std::size(portions_)) {
7303 portions_[actual_++].set_param(param);
7304 } else {
7305 if (actual_ == std::size(portions_)) {
7306 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
7307 }
7308 more_portions_.emplace_back().set_param(param);
7309 }
7310 }
7311 );
7312 }
7313 constexpr size_t length() const noexcept {
7314 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
7315 size_t idx = 0;
7316 size_t expr_length_[Nparams] = {};
7317 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
7318 size_t l = all_len_;
7319 for (idx = 0; idx < actual_; idx++) {
7320 if (portions_[idx].is_param) {
7321 l += expr_length_[portions_[idx].start];
7322 }
7323 }
7324 for (const auto& p : more_portions_) {
7325 if (p.is_param) {
7326 l += expr_length_[p.start];
7327 }
7328 }
7329 return l;
7330 }(std::make_index_sequence<sizeof...(Args)>());
7331 }
7332 template<size_t Idx>
7333 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
7334 if (idx == Idx) {
7335 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
7336 }
7337 if constexpr (Idx < Nparams - 1) {
7338 return place_idx<Idx + 1>(ptr, idx);
7339 }
7340 return ptr;
7341 }
7342 constexpr K* place(K* ptr) const noexcept {
7343 for (size_t idx = 0; idx < actual_; idx++) {
7344 if (portions_[idx].is_param) {
7345 ptr = place_idx<0>(ptr, portions_[idx].start);
7346 } else {
7347 ch_traits<K>::copy(ptr, pattern_.symbols() + portions_[idx].start, portions_[idx].len);
7348 ptr += portions_[idx].len;
7349 }
7350 }
7351 for (const auto& p : more_portions_) {
7352 if (p.is_param) {
7353 ptr = place_idx<0>(ptr, p.start);
7354 } else {
7355 const K* from = pattern_.symbols() + p.start;
7356 for (size_t idx = p.len; idx--;) {
7357 *ptr++ = *from++;
7358 }
7359 //ch_traits<K>::copy(ptr, pattern_.symbols() + p.start, p.len);
7360 //ptr += p.len;
7361 }
7362 }
7363 return ptr;
7364 }
7365};
7366// CTAD deducing rule for e_vsubst
7367template<StrSourceNoLiteral T, typename...Args> requires (sizeof...(Args) > 0)
7368e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
7369
7370template<is_one_of_char_v K, bool upper>
7371struct expr_change_case_ascii : expr_to_std_string<expr_change_case_ascii<K, upper>>{
7372 using symb_type = K;
7373
7374 str_src<K> src_;
7375
7376 template<StrSource S>
7377 expr_change_case_ascii(S&& s) : src_(std::forward<S>(s)){}
7378
7379 constexpr size_t length() const noexcept {
7380 return src_.length();
7381 }
7382 constexpr K* place(K* ptr) const noexcept {
7383 const K* read = src_.str;
7384 for (size_t l = src_.len; l--;) {
7385 if constexpr (upper) {
7386 *ptr++ = makeAsciiUpper(*read++);
7387 } else {
7388 *ptr++ = makeAsciiLower(*read++);
7389 }
7390 }
7391 return ptr;
7392 }
7393};
7394
7409template<is_one_of_char_v K>
7410struct e_ascii_upper : expr_change_case_ascii<K, true> {
7411 using base = expr_change_case_ascii<K, true>;
7412 using base::base;
7413};
7414
7415template<StrSource S>
7417
7432template<is_one_of_char_v K>
7433struct e_ascii_lower : expr_change_case_ascii<K, false> {
7434 using base = expr_change_case_ascii<K, false>;
7435 using base::base;
7436};
7437
7438template<StrSource S>
7440
7445namespace str {
7446
7479template<typename K, typename A, StrExprForType<K> E>
7480std::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) {
7481 size_t expr_length = expr.length();
7482 if (!expr_length) {
7483 str.erase(from, count);
7484 return str;
7485 }
7486 size_t str_length = str.length();
7487 if (from > str_length) {
7488 from = str_length;
7489 }
7490 if (count > str_length || from + count > str_length) {
7491 count = str_length - from;
7492 }
7493 size_t new_length = str_length - count + expr_length;
7494 size_t tail_length = str_length - count - from;
7495
7496 if (new_length <= str_length) {
7497 K* data = str.data();
7498 expr.place((typename E::symb_type*)data + from);
7499 if (expr_length < count) {
7500 if (tail_length) {
7501 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7502 }
7503 str.resize(new_length);
7504 }
7505 } else {
7506 auto fill = [&](K* data, size_t) -> size_t {
7507 if (tail_length) {
7508 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7509 }
7510 expr.place((typename E::symb_type*)data + from);
7511 return new_length;
7512 };
7513 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
7514 str.resize_and_overwrite(new_length, fill);
7515 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
7516 str._Resize_and_overwrite(new_length, fill);
7517 } else {
7518 str.resize(new_length);
7519 fill(str.data(), 0);
7520 }
7521 }
7522 return str;
7523}
7524
7552template<typename K, typename A, StrExprForType<K> E>
7553std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7554 return change(str, str.length(), 0, expr);
7555}
7556
7585template<typename K, typename A, StrExprForType<K> E>
7586std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7587 return change(str, 0, 0, expr);
7588}
7589
7620template<typename K, typename A, StrExprForType<K> E>
7621std::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) {
7622 return change(str, from, 0, expr);
7623}
7624
7649template<typename K, typename A, StrExprForType<K> E>
7650std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7651 if (size_t expr_length = expr.length()) {
7652 if (expr_length <= str.length()) {
7653 K* data = str.data();
7654 expr.place((typename E::symb_type*)data);
7655 str.resize(expr_length);
7656 } else {
7657 auto fill = [&](K* data, size_t) -> size_t {
7658 expr.place((typename E::symb_type*)data);
7659 return expr_length;
7660 };
7661 if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
7662 str.resize_and_overwrite(expr_length, fill);
7663 } else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
7664 str._Resize_and_overwrite(expr_length, fill);
7665 } else {
7666 str.resize(expr_length);
7667 expr.place((typename E::symb_type*)str.data());
7668 }
7669 }
7670 } else {
7671 str.clear();
7672 }
7673 return str;
7674}
7675
7676namespace details {
7677
7678template<typename K, typename A, typename E>
7679struct replace_grow_helper {
7680 using my_type = std::basic_string<K, std::char_traits<K>, A>;
7681
7682 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)
7683 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
7684 my_type& str;
7685
7686 const str_src<K> source;
7687 const str_src<K> pattern;
7688 K* repl;
7689 const size_t replLen;
7690
7691 size_t maxCount;
7692 const size_t delta;
7693 size_t all_delta{};
7694 const E& expr;
7695
7696 K* reserve_for_copy{};
7697 size_t end_of_piece{};
7698 size_t total_length{};
7699
7700 std::optional<my_type> dst;
7701
7702 void replace(size_t offset) {
7703 size_t found[16] = {offset};
7704 maxCount--;
7705
7706 offset += pattern.len;
7707 all_delta += delta;
7708 size_t idx = 1;
7709 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
7710 found[idx] = source.find(pattern, offset);
7711 if (found[idx] == npos) {
7712 break;
7713 }
7714 offset = found[idx] + pattern.len;
7715 all_delta += delta;
7716 }
7717 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
7718 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
7719 }
7720 // Теперь делаем свои замены
7721 // Now we make our replacements
7722 if (!reserve_for_copy) {
7723 // Только начинаем
7724 // Just getting started
7725 end_of_piece = source.length();
7726 total_length = end_of_piece + all_delta;
7727 my_type* dst_str{};
7728 if (total_length <= str.capacity()) {
7729 // Строка поместится в старое место | The string will be placed in the old location.
7730 dst_str = &str;
7731 } else {
7732 // Будем создавать в другом буфере | We will create in another buffer.
7733 dst_str = &dst.emplace();
7734 }
7735 auto fill = [this](K* p, size_t) -> size_t {
7736 reserve_for_copy = p;
7737 return total_length;
7738 };
7739 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
7740 dst_str->_Resize_and_overwrite(total_length, fill);
7741 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
7742 dst_str->resize_and_overwrite(total_length, fill);
7743 } else {
7744 dst_str->resize(total_length);
7745 reserve_for_copy = dst_str->data();
7746 }
7747 }
7748 const K* src_start = str.c_str();
7749 while(idx-- > 0) {
7750 size_t pos = found[idx] + pattern.len;
7751 size_t lenOfPiece = end_of_piece - pos;
7752 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
7753 if constexpr (std::is_same_v<E, int>) {
7754 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7755 } else {
7756 if (!repl) {
7757 repl = reserve_for_copy + pos + all_delta - replLen;
7758 expr.place(repl);
7759 } else {
7760 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7761 }
7762 }
7763 all_delta -= delta;
7764 end_of_piece = found[idx];
7765 }
7766 if (!all_delta && reserve_for_copy != src_start) {
7767 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
7768 str = std::move(*dst);
7769 }
7770 }
7771};
7772
7773} // namespace details
7774
7809template<typename K, typename A, StrExprForType<K> E, typename T>
7810requires (std::is_constructible_v<str_src<K>, T>)
7811std::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) {
7812 if (!max_count) {
7813 return str;
7814 }
7815 str_src<K> src = str;
7816 str_src<K> spattern{std::forward<T>(pattern)};
7817 offset = src.find(pattern, offset);
7818 if (offset == npos) {
7819 return str;
7820 }
7821 size_t replLen = repl.length();
7822 K* replStart{};
7823 if (spattern.len == replLen) {
7824 // Заменяем inplace на подстроку такой же длины
7825 // Replace inplace with a substring of the same length
7826 K* ptr = str.data();
7827 replStart = ptr + offset;
7828 repl.place(replStart);
7829
7830 while (--max_count) {
7831 offset = src.find(spattern, offset + replLen);
7832 if (offset == npos)
7833 break;
7834 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7835 }
7836 } else if (spattern.len > replLen) {
7837 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7838 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7839 K* ptr = str.data();
7840 replStart = ptr + offset;
7841 repl.place(replStart);
7842 size_t posWrite = offset + replLen;
7843 offset += spattern.len;
7844
7845 while (--max_count) {
7846 size_t idx = src.find(spattern, offset);
7847 if (idx == npos)
7848 break;
7849 size_t lenOfPiece = idx - offset;
7850 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7851 posWrite += lenOfPiece;
7852 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7853 posWrite += replLen;
7854 offset = idx + spattern.len;
7855 }
7856 size_t tailLen = src.len - offset;
7857 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7858 str.resize(posWrite + tailLen);
7859 } else {
7860 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7861 }
7862 return str;
7863}
7864
7885template<typename K, typename A>
7886std::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) {
7887 if (!max_count) {
7888 return str;
7889 }
7890 str_src<K> src = str;
7891 offset = src.find(pattern, offset);
7892 if (offset == npos) {
7893 return str;
7894 }
7895 if (pattern.len == repl.len) {
7896 // Заменяем inplace на подстроку такой же длины
7897 // Replace inplace with a substring of the same length
7898 K* ptr = str.data();
7899 while (max_count--) {
7900 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7901 offset = src.find(pattern, offset + repl.len);
7902 if (offset == npos)
7903 break;
7904 }
7905 } else if (pattern.len > repl.len) {
7906 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7907 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7908 K* ptr = str.data();
7909 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7910 size_t posWrite = offset + repl.len;
7911 offset += pattern.len;
7912
7913 while (--max_count) {
7914 size_t idx = src.find(pattern, offset);
7915 if (idx == npos)
7916 break;
7917 size_t lenOfPiece = idx - offset;
7918 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7919 posWrite += lenOfPiece;
7920 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7921 posWrite += repl.len;
7922 offset = idx + pattern.len;
7923 }
7924 size_t tailLen = src.len - offset;
7925 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7926 str.resize(posWrite + tailLen);
7927 } else {
7928 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7929 }
7930 return str;
7931}
7932
7933template<typename K, typename A, typename T, typename M>
7934requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7935std::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) {
7936 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7937}
7938
7951template<typename K, typename A>
7952std::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) {
7953 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7954 K s = *ptr;
7955 if (isAsciiLower(s)) {
7956 *ptr = s & ~0x20;
7957 }
7958 }
7959 return str;
7960}
7961
7974template<typename K, typename A>
7975std::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) {
7976 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7977 K s = *ptr;
7978 if (isAsciiUpper(s)) {
7979 *ptr = s | 0x20;
7980 }
7981 }
7982 return str;
7983}
7984
7985} // namespace str
7986
7987} // namespace simstr
7988
7993namespace std {
8013template<simstr::StdStrSource T>
8015 return {str};
8016}
8017
8045template<typename K, typename A, simstr::StrExprForType<K> E>
8046std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
8047 return simstr::str::change(str, str.length(), 0, expr);
8048}
8049
8077template<typename K, typename A, simstr::StrExprForType<K> E>
8078std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
8079 return simstr::str::change(str, 0, 0, expr);
8080}
8081
8082} // namespace std
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3230
constexpr bool is_done() const
Find out if substrings are running out.
Definition strexpr.h:3241
constexpr str_t next()
Get the next substring.
Definition strexpr.h:3250
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:5316
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:4338
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition strexpr.h:3882
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition strexpr.h:3767
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition strexpr.h:4121
constexpr std::optional< R > strip_suffix(str_piece suffix) const
Get a string without a suffix if it ends with one.
Definition strexpr.h:5029
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition strexpr.h:4866
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition strexpr.h:3818
constexpr std::basic_string_view< D > to_sv() const noexcept
Convert to std::basic_string_view.
Definition strexpr.h:3421
constexpr T slice_to(S start, E end) const noexcept
Get the slice result and place it into an object of the specified type.
Definition strexpr.h:4452
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:4812
constexpr R trimmed_right() const
Get a string with whitespace removed on the right.
Definition strexpr.h:5112
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:5355
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:4017
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition strexpr.h:4748
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition strexpr.h:3807
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition strexpr.h:3399
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:3729
constexpr R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition strexpr.h:5234
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4512
constexpr my_type substring(S start, E end) const noexcept
Wrapper around slice_to<my_type>
Definition strexpr.h:4466
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition strexpr.h:3922
constexpr T split(str_piece delimiter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition strexpr.h:4733
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:5217
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition strexpr.h:3693
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:4440
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition strexpr.h:4943
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3469
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:4757
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:3715
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:5298
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:4975
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition strexpr.h:3778
constexpr size_t find_ia(str_piece pattern, size_t offset=0) const noexcept
Find the start of the first occurrence of a substring within this string, ignoring ASCII case.
Definition strexpr.h:4171
constexpr R trimmed_left() const
Get a string with whitespace removed on the left.
Definition strexpr.h:5100
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:4892
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:4385
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:4357
constexpr R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition strexpr.h:5262
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:5179
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:4080
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:5332
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:4803
constexpr str_piece slice(S start, E end) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3574
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:5198
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:4053
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:4398
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:4140
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:4426
std::optional< double > to_double() const noexcept
Convert string to double.
Definition strexpr.h:4557
constexpr size_t find_ia_lower(str_piece pattern_lower, size_t offset=0) const noexcept
Find the start of the first occurrence of a substring within this string, ignoring ASCII case,...
Definition strexpr.h:4232
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:3989
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition strexpr.h:4592
constexpr std::optional< R > strip_prefix(str_piece prefix) const
Get a string without a prefix if it starts with one.
Definition strexpr.h:4991
constexpr bool operator!() const noexcept
Check for emptiness.
Definition strexpr.h:3736
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition strexpr.h:5088
constexpr size_t size() const
The size of the string in characters.
Definition strexpr.h:3410
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:4319
constexpr str_piece prefix(E count) const noexcept
Wraps slice(0, count). It also allows you to set the boundary using a search method.
Definition strexpr.h:3659
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:4480
constexpr str_piece tail(E count) const noexcept
Wraps slice(count, to_end). It also allows you to set the boundary using a search method.
Definition strexpr.h:3667
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:4066
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:4370
constexpr str_piece suffix(E count) const noexcept
Wraps slice(from_end(count), to_end). It also allows you to set the boundary using a search method.
Definition strexpr.h:3675
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition strexpr.h:4899
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:3953
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:5415
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:4411
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:5142
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:4003
constexpr R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition strexpr.h:5248
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition strexpr.h:3837
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition strexpr.h:3827
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition strexpr.h:4108
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:5398
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3496
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition strexpr.h:3749
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition strexpr.h:4955
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:5280
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition strexpr.h:3847
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Convert to std::basic_string.
Definition strexpr.h:3441
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:5157
constexpr void for_all_founded(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:4296
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:4717
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:5127
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition strexpr.h:4851
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:5010
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:4825
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition strexpr.h:3973
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:5048
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:5378
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition strexpr.h:3384
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4545
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:3894
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4620
constexpr size_t find_end_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:4094
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:4770
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:312
Checks whether two types are compatible string types.
Definition strexpr.h:173
constexpr expr_if< A > e_if(bool c, const A &a)
Creating a conditional string expression expr_if.
Definition strexpr.h:1540
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Unary operator + for converting standard strings to string expressions.
Definition strexpr.h:8014
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:2695
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:1716
constexpr empty_expr< ubs > eeb
Empty string expression of type char8_t.
Definition strexpr.h:856
HexFlags
Flags for the e_hex function.
Definition strexpr.h:2523
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:2580
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:1214
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:2721
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:1462
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:6628
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:2379
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:6135
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:2799
@ Short
without leading zeroes
Definition strexpr.h:2524
Small namespace for standard string methods.
Definition strexpr.h:1596
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:7553
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:7586
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:7952
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:7480
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:7811
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:7975
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:7650
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:7621
Library namespace.
Definition sstring.cpp:12
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition strexpr.h:2866
@ Overflow
Переполнение, число не помещается в заданный тип
Definition strexpr.h:2869
@ Success
Успешно
Definition strexpr.h:2867
@ NotNumber
Вообще не число
Definition strexpr.h:2870
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition strexpr.h:2868
Some methods for working with standard strings.
Definition strexpr.h:7993
Generate a string based on the original one, replacing all ASCII uppercase letters (A-Z) with lowerca...
Definition strexpr.h:7433
Generate a string based on the original one, replacing all ASCII lowercase letters (a-z) with upperca...
Definition strexpr.h:7410
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:6967
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:7189
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:7288
An "empty" string expression.
Definition strexpr.h:835
Conditional selection string expression.
Definition strexpr.h:1341
Conditional selection string expression.
Definition strexpr.h:1407
Conditional selection string expression.
Definition strexpr.h:1250
Conditional selection string expression.
Definition strexpr.h:1279
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:6712
A string expression that generates a string replacing all occurrences of the given substring to strin...
Definition strexpr.h:6253
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Constructor.
Definition strexpr.h:6272
A string expression that generates a string replacing all occurrences of the given substring to anoth...
Definition strexpr.h:6147
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Constructor.
Definition strexpr.h:6165
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:1565
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:5586
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:5649
constexpr str_src_nt(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5620
constexpr str_src_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition strexpr.h:5611
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5638
constexpr str_src_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5626
The simplest class of an immutable non-owning string.
Definition strexpr.h:5450
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5479
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition strexpr.h:5504
constexpr size_t length() const noexcept
Get the length of the string.
Definition strexpr.h:5490
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition strexpr.h:5533
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition strexpr.h:5544
constexpr str_src(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5469
constexpr bool is_same(str_src< K > other) const noexcept
Check if two objects point to the same string.
Definition strexpr.h:5513
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition strexpr.h:5484
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5497
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition strexpr.h:5557
constexpr bool is_part_of(str_src< K > other) const noexcept
Check if a string is part of another string.
Definition strexpr.h:5522
constexpr str_src(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5463
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