simstr 2.0.0
Yet another strings library
 
Загрузка...
Поиск...
Не найдено
strexpr.h
1/*
2 * ver. 2.0.0
3 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
4 * База для строковых конкатенаций через выражения времени компиляции
5 * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6 * Base for string concatenations via compile-time expressions
7 */
8#pragma once
9#include <cstddef>
10#include <cstdint>
11#include <climits>
12#include <limits>
13#include <string>
14#include <string_view>
15#include <concepts>
16#include <vector>
17#include <optional>
18#include <charconv>
19
20#if defined __has_builtin
21# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
22# define HAS_BUILTIN_OVERFLOW
23# endif
24#endif
25
26#ifdef _MSC_VER
27#define _no_unique_address msvc::no_unique_address
28#define decl_empty_bases __declspec(empty_bases)
29#else
30#define _no_unique_address no_unique_address
31#define decl_empty_bases
32#endif
33
34#ifdef _MSC_VER
35/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
36Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
37Пишут, что баг исправлен, но видимо не до конца.
38Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
39/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
40Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
41They write that the bug has been fixed, but apparently not completely.
42Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
43#define SS_CONSTEVAL constexpr
44#else
45#define SS_CONSTEVAL consteval
46#endif
47
52namespace simstr {
53
54// Выводим типы для 16 и 32 битных символов в зависимости от размера wchar_t
55// Infer types for 16 and 32 bit characters depending on the size of wchar_t
56inline constexpr bool wchar_is_u16 = sizeof(wchar_t) == sizeof(char16_t);
57
58using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
59
60inline wchar_type* to_w(wchar_t* p) {
61 return (reinterpret_cast<wchar_type*>(p));
62}
63
64inline const wchar_type* to_w(const wchar_t* p) {
65 return (reinterpret_cast<const wchar_type*>(p));
66}
67
68inline wchar_t* from_w(wchar_type* p) {
69 return (reinterpret_cast<wchar_t*>(p));
70}
71
72inline const wchar_t* from_w(const wchar_type* p) {
73 return (reinterpret_cast<const wchar_t*>(p));
74}
75
76using u8s = char;
77using ubs = char8_t;
78using uws = wchar_t;
79using u16s = char16_t;
80using u32s = char32_t;
81
82using uu8s = std::make_unsigned<u8s>::type;
83
84template<typename T, typename K = void, typename... Types>
85struct is_one_of_type {
86 static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
87};
88template<typename T>
89struct is_one_of_type<T, void> : std::false_type {};
90
91template<typename K>
92concept is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
93
94template<typename K>
95concept is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
96
97template<is_one_of_std_char_v From>
98auto to_one_of_std_char(From* from) {
99 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
100 return from;
101 } else if constexpr (std::is_same_v<From, ubs>) {
102 return reinterpret_cast<u8s*>(from);
103 } else {
104 return from_w(from);
105 }
106}
107
108template<is_one_of_std_char_v From>
109auto to_one_of_std_char(const From* from) {
110 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
111 return from;
112 } else if constexpr (std::is_same_v<From, ubs>) {
113 return reinterpret_cast<const u8s*>(from);
114 } else {
115 return from_w(from);
116 }
117}
118
119template<typename K>
120struct to_std_char_type : std::type_identity<K>{};
121
122template<>
123struct to_std_char_type<char8_t>{
124 using type = char;
125};
126
127template<>
128struct to_std_char_type<char16_t>{
129 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
130};
131
132template<>
133struct to_std_char_type<char32_t>{
134 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
135};
136
137template<typename K>
138using to_std_char_t = typename to_std_char_type<K>::type;
139
140template<typename K>
141struct to_base_char_type : std::type_identity<K>{};
142
143template<>
144struct to_base_char_type<char8_t>{
145 using type = char;
146};
147
148template<>
149struct to_base_char_type<wchar_t>{
150 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), char16_t, char32_t>;
151};
152
153template<typename K>
154using to_base_char_t = typename to_base_char_type<K>::type;
155
171template<typename K1, typename K2>
172concept is_equal_str_type_v = sizeof(K1) == sizeof(K2) && is_one_of_char_v<K1> && is_one_of_char_v<K2>;
173
174/*
175Вспомогательные шаблоны для определения строковых литералов.
176Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
177Если пишем
178template<size_t N>
179void func(const char(&lit)[N]);
180то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
181
182// Выделили место под символы
183char buf[100];
184// Как то наполнили буфер, допустим, до половины.
185
186stringa text = buf;
187Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
188для строкового литерала, в text запишется просто указатель на buf и длина 100.
189
190Поэтому такие параметры объявляем как
191template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
192void func(T&& lit);
193
194Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
195и выражение с параметром char[100] - не скомпилируется.
196
197Helper templates for defining string literals.
198They are used to limit types in function parameters strictly as `const K(&)[N]`
199If we write
200template<size_t N>
201void func(const char(&lit)[N]);
202then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
203
204// Allocate space for symbols
205char buf[100];
206// Somehow the buffer was filled, say, to half.
207
208stringa text = buf;
209Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
210for a string literal, text will simply contain a pointer to buf and length 100.
211
212Therefore, we declare such parameters as
213template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
214void func(T&& lit);
215
216Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
217and an expression with the char[100] parameter will not compile.
218*/
219
220template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
221// Для правильных типов параметров есть определение, в виде специализации шаблона
222// There is a definition for the correct parameter types, in the form of a template specialization
223template<is_one_of_char_v T, size_t N>
224struct const_lit<const T(&)[N]> {
225 using symb_type = T;
226 constexpr static size_t Count = N;
227};
228
229template<typename T>
230concept is_const_lit_v = requires {
231 typename const_lit<T>::symb_type;
232};
233
234// Тут ещё дополнительно ограничиваем тип литерала
235// Here we further restrict the type of the literal
236template<typename K, typename T> struct const_lit_for;
237
238template<typename K, is_equal_str_type_v<K> P, size_t N>
239struct const_lit_for<K, const P(&)[N]> {
240 constexpr static size_t Count = N;
241};
242
243template<typename K, size_t N>
244class const_lit_to_array {
245
246 template<size_t Idx>
247 constexpr size_t find(K s) const {
248 if constexpr (Idx < N) {
249 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
250 }
251 return -1;
252 }
253
254 template<size_t Idx>
255 constexpr bool exist(K s) const {
256 if constexpr (Idx < N) {
257 return s == symbols_[Idx] || exist<Idx + 1>(s);
258 }
259 return false;
260 }
261public:
262 const K (&symbols_)[N + 1];
263
264 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
265 constexpr const_lit_to_array(T&& s)
266 : symbols_((const K(&)[M])s) {}
267
268 constexpr bool contain(K s) const {
269 return exist<0>(s);
270 }
271 constexpr size_t index_of(K s) const {
272 return find<0>(s);
273 }
274};
275
276template<typename A>
277concept StrTypeCommon = requires(const A& a) {
278 typename std::remove_cvref_t<A>::symb_type;
279 { a.is_empty() } -> std::same_as<bool>;
280 { a.length() } -> std::convertible_to<size_t>;
281 { a.symbols() };
282};
283
284template<typename A>
285concept HasSymbType = requires {
286 typename std::remove_cvref_t<A>::symb_type;
287};
288
310template<typename A, typename K>
311concept StrType = StrTypeCommon<A> && requires(const A& a) {
312 { a.symbols() } -> std::same_as<const K*>;
313} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
314
315template<typename T> struct is_std_string_source : std::false_type{};
316
317template<typename K, typename A>
318struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
319
320template<typename K>
321struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
322
323template<typename T>
324concept is_std_string_source_v = is_std_string_source<T>::value;
325
326template<typename T>
327concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
328
329template<typename T, typename K>
330concept StdStrSourceForType = StdStrSource<T> && is_equal_str_type_v<K, typename T::value_type>;
331
332template<typename T>
333concept StrSource = StdStrSource<T> || is_const_lit_v<T> || StrTypeCommon<T>;
334
335template<typename T>
336concept StrSourceNoLiteral = StdStrSource<T> || StrTypeCommon<T>;
337
338template<typename T> struct is_std_string : std::false_type{};
339
340template<typename K, typename A>
341struct is_std_string<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
342
343template<typename T>
344concept is_std_string_v = is_std_string<T>::value;
345template<typename T, typename K>
346concept StdStringForType = is_std_string_v<T> && is_equal_str_type_v<K, typename T::value_type>;
347
348template<typename T>
349struct symb_type_from_src {
350 using type = void;
351
352};
353
354template<typename K, size_t N>
355struct symb_type_from_src<const K(&)[N]> {
356 using type = K;
357};
358
359template<StdStrSource T>
360struct symb_type_from_src<T> {
361 using type = typename std::remove_cvref_t<T>::value_type;
362};
363
364template<HasSymbType T>
365struct symb_type_from_src<T> {
366 using type = typename std::remove_cvref_t<T>::symb_type;
367};
368
369template<typename T>
370using symb_type_from_src_t = symb_type_from_src<T>::type;
371
372
498
506template<typename A>
507concept StrExpr = requires(const A& a) {
508 typename std::remove_cvref_t<A>::symb_type;
509 { a.length() } -> std::convertible_to<size_t>;
510 { a.place(std::declval<typename std::remove_cvref_t<A>::symb_type*>()) } -> std::same_as<typename std::remove_cvref_t<A>::symb_type*>;
511};
512
524template<typename A, typename K>
526
527template<typename K, typename T>
528struct convert_to_strexpr;
529
530template<typename A, typename K>
531concept strexpr_from = requires(const A& a) {
532 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StrExprForType<K>;
533};
534
535template<typename A, typename K>
536concept strexpr_std = requires(const A& a) {
537 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StdStringForType<K>;
538};
539
540template<typename A, typename K>
542
543template<typename A, typename K>
545
546template<typename A, typename K>
547concept to_strexpr_meth = requires(const A& a) {
548 {a.template to_strexpr<K>()} -> StrExprForType<K>;
549};
550
551template<typename A, typename K>
552concept to_strexpr_std = requires(const A& a) {
553 {a.template to_strexpr<K>()} -> StdStringForType<K>;
554};
555
556template<typename A, typename K>
557concept convertible_to_strexpr = strexpr_for<A, K> || strexpr_from<A, K> || strexpr_std<A, K> || to_strexpr_type<A, K> || to_strexpr_meth<A, K> || to_strexpr_std<A, K>;
558
559template<typename K, strexpr_from<K> T>
560constexpr auto to_strexpr(T&& t) {
561 return convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t));
562}
563
564template<typename K, strexpr_for<K> T>
565constexpr typename convert_to_strexpr<K, std::remove_cvref_t<T>>::type to_strexpr(T&& t) {
566 return {std::forward<T>(t)};
567}
568
569template<typename K, to_strexpr_type<K> T>
570constexpr typename std::remove_cvref_t<T>::strexpr to_strexpr(T&& t) {
571 return {std::forward<T>(t)};
572}
573
574template<typename K, to_strexpr_meth<K> T>
575constexpr auto to_strexpr(T&& t) {
576 return t.template to_strexpr<K>();
577}
578
579template<typename K, StdStrSource T> requires is_equal_str_type_v<K, typename T::value_type>
580struct expr_stdstr_c {
581 using symb_type = K;
582 T t_;
583
584 expr_stdstr_c(T t) : t_(std::move(t)){}
585
586 constexpr size_t length() const noexcept {
587 return t_.length();
588 }
589 constexpr symb_type* place(symb_type* p) const noexcept {
590 size_t s = t_.size();
591 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
592 return p + s;
593 }
594};
595
596template<typename K, strexpr_std<K> T>
597constexpr auto to_strexpr(T&& t) {
598 using type = decltype(convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t)));
599 return expr_stdstr_c<K, type>{convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t))};
600}
601
602template<typename K, to_strexpr_std<K> T>
603constexpr auto to_strexpr(T&& t) {
604 using type = decltype(t.template to_strexpr<K>());
605 return expr_stdstr_c<K, type>{t.template to_strexpr<K>()};
606}
607
608template<typename K, typename T>
609using convert_to_strexpr_t = decltype(to_strexpr<K>(std::declval<T>()));
610
611/*
612* Шаблонные классы для создания строковых выражений из нескольких источников.
613* Благодаря компиляторно-шаблонной "магии" позволяют максимально эффективно
614* получать результирующую строку - сначала вычисляется длина результирующей строки,
615* потом один раз выделяется память для результата, после символы помещаются в
616* выделенную память.
617* Для конкатенация двух объектов строковых выражений в один
618
619* Template classes for creating string expressions from multiple sources.
620* Thanks to compiler-template "magic" they allow you to maximize efficiency
621* get the resulting string - first the length of the resulting string is calculated,
622* then memory is allocated once for the result, after which the characters are placed in
623* allocated memory.
624* For concatenating two string expression objects into one.
625*/
626
627template<typename K, typename Allocator, 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<u8s> eeb{};
862inline constexpr empty_expr<uws> eew{};
868inline constexpr empty_expr<u16s> eeu{};
874inline constexpr empty_expr<u32s> eeuu{};
875
876template<typename K>
877struct expr_char : expr_to_std_string<expr_char<K>>{
878 using symb_type = K;
879 K value;
880 constexpr expr_char(K v) : value(v){}
881 constexpr size_t length() const noexcept {
882 return 1;
883 }
884 constexpr symb_type* place(symb_type* p) const noexcept {
885 *p++ = value;
886 return p;
887 }
888};
889
902template<typename K, StrExprForType<K> A>
903constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
904 return {a, s};
905}
906
917template<typename K>
918constexpr expr_char<K> e_char(K s) {
919 return {s};
920}
921
922template<typename K, size_t N>
923struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
924 using symb_type = K;
925 const K (&str)[N + 1];
926 constexpr expr_literal(const K (&str_)[N + 1]) : str(str_){}
927
928 constexpr size_t length() const noexcept {
929 return N;
930 }
931 constexpr symb_type* place(symb_type* p) const noexcept {
932 if constexpr (N != 0)
933 std::char_traits<K>::copy(p, str, N);
934 return p + N;
935 }
936};
937
990template<typename T, size_t N = const_lit<T>::Count>
991constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
992 return {s};
993}
994
995template<bool first, typename K, size_t N, typename A>
996struct expr_literal_join : expr_to_std_string<expr_literal_join<first, K, N, A>> {
997 using symb_type = K;
998 using atype = typename A::symb_type;
999 const K (&str)[N + 1];
1000 const A& a;
1001 constexpr expr_literal_join(const K (&str_)[N + 1], const A& a_) : str(str_), a(a_){}
1002
1003 constexpr size_t length() const noexcept {
1004 return N + a.length();
1005 }
1006 constexpr symb_type* place(symb_type* p) const noexcept {
1007 if constexpr (N != 0) {
1008 if constexpr (first) {
1009 std::char_traits<K>::copy(p, str, N);
1010 return (symb_type*)a.place((atype*)(p + N));
1011 } else {
1012 p = (symb_type*)a.place((atype*)p);
1013 std::char_traits<K>::copy(p, str, N);
1014 return p + N;
1015 }
1016 } else {
1017 return a.place(p);
1018 }
1019 }
1020};
1021
1029template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
1030constexpr expr_literal_join<false, P, (N - 1), A> operator+(const A& a, T&& s) {
1031 return {s, a};
1032}
1033
1041template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
1042constexpr expr_literal_join<true, P, (N - 1), A> operator+(T&& s, const A& a) {
1043 return {s, a};
1044}
1045
1059template<typename K, size_t N, size_t S = ' '>
1060struct expr_spaces : expr_to_std_string<expr_spaces<K, N>> {
1061 using symb_type = K;
1062 constexpr size_t length() const noexcept {
1063 return N;
1064 }
1065 constexpr symb_type* place(symb_type* p) const noexcept {
1066 if constexpr (N != 0)
1067 std::char_traits<K>::assign(p, N, static_cast<K>(S));
1068 return p + N;
1069 }
1070};
1071
1085template<size_t N>
1087 return {};
1088}
1089
1103template<size_t N>
1105 return {};
1106}
1107
1119template<typename K>
1120struct expr_pad : expr_to_std_string<expr_pad<K>> {
1121 using symb_type = K;
1122 size_t len;
1123 K s;
1124 constexpr expr_pad(size_t len_, K s_) : len(len_), s(s_){}
1125 constexpr size_t length() const noexcept {
1126 return len;
1127 }
1128 constexpr symb_type* place(symb_type* p) const noexcept {
1129 if (len)
1130 std::char_traits<K>::assign(p, len, s);
1131 return p + len;
1132 }
1133};
1134
1148template<typename K>
1149constexpr expr_pad<K> e_c(size_t l, K s) {
1150 return { l, s };
1151}
1152
1153template<typename K, size_t N>
1154struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
1155 using symb_type = K;
1156 size_t repeat_;
1157 const K (&s)[N + 1];
1158 constexpr expr_repeat_lit(size_t repeat, const K (&s_)[N + 1]) : repeat_(repeat), s(s_){}
1159 constexpr size_t length() const noexcept {
1160 return N * repeat_;
1161 }
1162 constexpr symb_type* place(symb_type* p) const noexcept {
1163 if constexpr (N) {
1164 for (size_t i = 0; i < repeat_; i++) {
1165 std::char_traits<K>::copy(p, s, N);
1166 p += N;
1167 }
1168 }
1169 return p;
1170 }
1171};
1172
1173template<StrExpr A>
1174struct expr_repeat_expr : expr_to_std_string<expr_repeat_expr<A>> {
1175 using symb_type = typename A::symb_type;
1176 size_t repeat_;
1177 const A& expr_;
1178 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_.length();
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 && s.size() > 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 (I > 0 && write != ptr && (copyLen || !skip_empty)) {
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 (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 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 == -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 == -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 == -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>
3282concept first_index_in_str = std::is_same_v<T, from_end> || std::is_convertible_v<T, size_t>;
3283
3284template<typename T>
3285concept last_index_in_str = first_index_in_str<T> || std::is_same_v<T, from_begin>;
3286
3311template<typename K, typename StrRef, typename Impl, bool Mutable>
3312class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
3313 constexpr const Impl& d() const noexcept {
3314 return *static_cast<const Impl*>(this);
3315 }
3316 constexpr size_t _len() const noexcept {
3317 return d().length();
3318 }
3319 constexpr const K* _str() const noexcept {
3320 return d().symbols();
3321 }
3322 constexpr bool _is_empty() const noexcept {
3323 return d().is_empty();
3324 }
3325
3326public:
3327 using symb_type = K;
3328 using str_piece = StrRef;
3329 using traits = ch_traits<K>;
3330 using uns_type = std::make_unsigned_t<K>;
3331 using my_type = Impl;
3332 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3333 str_src_algs() = default;
3334
3347 constexpr K* place(K* ptr) const noexcept {
3348 size_t myLen = _len();
3349 traits::copy(ptr, _str(), myLen);
3350 return ptr + myLen;
3351 }
3352
3362 void copy_to(K* buffer, size_t bufSize) {
3363 size_t tlen = std::min(_len(), bufSize - 1);
3364 traits::copy(buffer, _str(), tlen);
3365 buffer[tlen] = 0;
3366 }
3367
3373 constexpr size_t size() const {
3374 return _len();
3375 }
3376
3383 template<typename D = K> requires is_equal_str_type_v<K, D>
3384 constexpr std::basic_string_view<D> to_sv() const noexcept {
3385 return {(const D*)_str(), _len()};
3386 }
3387
3393 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
3394 constexpr operator std::basic_string_view<D, Traits>() const {
3395 return {(const D*)_str(), _len()};
3396 }
3397
3403 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
3404 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
3405 return {(const D*)_str(), _len()};
3406 }
3407
3413 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
3414 constexpr operator std::basic_string<D, Traits, Allocator>() const {
3415 return {(const D*)_str(), _len()};
3416 }
3417
3423 constexpr operator str_piece() const noexcept {
3424 return str_piece{_str(), _len()};
3425 }
3426
3432 constexpr str_piece to_str() const noexcept {
3433 return {_str(), _len()};
3434 }
3435
3458 [[deprecated("Use 'slice' or [start, end] in C++23")]]
3459 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3460 size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
3461 idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
3462 if (idxEnd > myLen)
3463 idxEnd = myLen;
3464 if (idxStart > idxEnd)
3465 idxStart = idxEnd;
3466 return str_piece{_str() + idxStart, idxEnd - idxStart};
3467 }
3468
3500 template<first_index_in_str S, last_index_in_str E>
3501 constexpr str_piece slice(S start, E end) const noexcept {
3502 size_t idxStart, idxEnd, len = _len();
3503 if constexpr (std::is_same_v<S, from_end>) {
3504 idxStart = start.idx_ >= len ? 0 : len - start.idx_;
3505 } else {
3506 idxStart = std::min(size_t(start), len);
3507 }
3508
3509 if constexpr (std::is_same_v<E, from_begin>) {
3510 idxEnd = std::max(idxStart, std::min(end.idx_, len));
3511 } else if constexpr (std::is_same_v<E, from_end>) {
3512 idxEnd = std::max(idxStart, end.idx_ >= len ? 0 : len - end.idx_);
3513 } else {
3514 size_t e = end;
3515 idxEnd = len - idxStart <= e ? len : idxStart + e;
3516 }
3517
3518 return str_piece{_str() + idxStart, idxEnd - idxStart};
3519 }
3520
3521#if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202211L
3526 template<first_index_in_str S, last_index_in_str E>
3527 constexpr str_piece operator[](S start, E end) const noexcept {
3528 return slice(start, end);
3529 }
3530#endif
3535 constexpr str_piece prefix(size_t count) const noexcept {
3536 return slice(0, count);
3537 }
3538
3542 constexpr str_piece suffix(size_t count) const noexcept {
3543 return slice(from_end(count), to_end);
3544 }
3545
3555 [[deprecated("Use 'slice' or [start, end] in C++23")]]
3556 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
3557 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3558 if (idxEnd > myLen)
3559 idxEnd = myLen;
3560 if (idxStart > idxEnd)
3561 idxStart = idxEnd;
3562 return str_piece{_str() + idxStart, idxEnd - idxStart};
3563 }
3564
3578 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
3579 return str_piece{_str() + from, to - from};
3580 }
3581
3591 constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
3592 return slice(0, find_or_all(pattern, offset));
3593 }
3594
3598 constexpr bool operator!() const noexcept {
3599 return _is_empty();
3600 }
3601
3611 constexpr K at(ptrdiff_t idx) const {
3612 return _str()[idx >= 0 ? idx : _len() + idx];
3613 }
3614 // Сравнение строк
3615 // String comparison
3616 constexpr int compare(const K* text, size_t len) const {
3617 size_t myLen = _len();
3618 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3619 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3620 }
3629 constexpr int compare(str_piece o) const {
3630 return compare(o.symbols(), o.length());
3631 }
3632
3640 constexpr int strcmp(const K* text) const {
3641 size_t myLen = _len(), idx = 0;
3642 const K* ptr = _str();
3643 for (; idx < myLen; idx++) {
3644 uns_type s1 = (uns_type)text[idx];
3645 if (!s1) {
3646 return 1;
3647 }
3648 uns_type s2 = (uns_type)ptr[idx];
3649 if (s1 < s2) {
3650 return 1;
3651 } else if (s1 > s2) {
3652 return -1;
3653 }
3654 }
3655 return text[idx] == 0 ? 0 : -1;
3656 }
3657
3658 constexpr bool equal(const K* text, size_t len) const noexcept {
3659 return len == _len() && traits::compare(_str(), text, len) == 0;
3660 }
3669 constexpr bool equal(str_piece other) const noexcept {
3670 return equal(other.symbols(), other.length());
3671 }
3672
3680 constexpr bool operator==(const base& other) const noexcept {
3681 return equal(other._str(), other._len());
3682 }
3683
3689 constexpr auto operator<=>(const base& other) const noexcept {
3690 return compare(other._str(), other._len()) <=> 0;
3691 }
3692
3698 template<typename T, size_t N = const_lit_for<K, T>::Count>
3699 constexpr bool operator==(T&& other) const noexcept {
3700 return N - 1 == _len() && traits::compare(_str(), (const K*)other, N - 1) == 0;
3701 }
3702
3708 template<typename T, size_t N = const_lit_for<K, T>::Count>
3709 constexpr auto operator<=>(T&& other) const noexcept {
3710 size_t myLen = _len();
3711 int cmp = traits::compare(_str(), (const K*)other, std::min(myLen, N - 1));
3712 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3713 return res <=> 0;
3714 }
3715
3716 // Сравнение ascii строк без учёта регистра
3717 // Compare ascii strings without taking into account case
3718 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
3719 if (!len)
3720 return _is_empty() ? 0 : 1;
3721 size_t myLen = _len(), checkLen = std::min(myLen, len);
3722 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
3723 while (checkLen--) {
3724 uns_type s1 = *ptr1++, s2 = *ptr2++;
3725 if (s1 == s2)
3726 continue;
3727 s1 = makeAsciiLower(s1);
3728 s2 = makeAsciiLower(s2);
3729 if (s1 > s2)
3730 return 1;
3731 else if (s1 < s2)
3732 return -1;
3733 }
3734 return myLen == len ? 0 : myLen > len ? 1 : -1;
3735 }
3744 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
3745 return compare_ia(text.symbols(), text.length());
3746 }
3747
3756 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
3757 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
3758 }
3759
3767 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
3768 return compare_ia(text.symbols(), text.length()) < 0;
3769 }
3770
3771 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3772 size_t lenText = _len();
3773 // Образец, не вмещающийся в строку и пустой образец не находим
3774 // We don't look for an empty string or a string longer than the text.
3775 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3776 return str::npos;
3777 lenPattern--;
3778 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3779 pattern++;
3780 for (const K* fnd = text + offset;; ++fnd) {
3781 fnd = traits::find(fnd, last - fnd, first);
3782 if (!fnd)
3783 return str::npos;
3784 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3785 return static_cast<size_t>(fnd - text);
3786 }
3787 }
3798 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
3799 return find(pattern.symbols(), pattern.length(), offset);
3800 }
3801
3817 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
3818 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const {
3819 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3820 return fnd;
3821 }
3822 throw Exc(std::forward<Args>(args)...);
3823 }
3824
3834 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
3835 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3836 return fnd == str::npos ? fnd : fnd + pattern.length();
3837 }
3838
3848 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3849 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3850 return fnd == str::npos ? _len() : fnd;
3851 }
3852
3862 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3863 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3864 return fnd == str::npos ? _len() : fnd + pattern.length();
3865 }
3866
3867 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3868 if (lenPattern == 1)
3869 return find_last(pattern[0], offset);
3870 size_t lenText = std::min(_len(), offset);
3871 // Образец, не вмещающийся в строку и пустой образец не находим
3872 // We don't look for an empty string or a string longer than the text.
3873 if (!lenPattern || lenPattern > lenText)
3874 return str::npos;
3875
3876 lenPattern--;
3877 const K *text = _str() + lenPattern, last = pattern[lenPattern];
3878 lenText -= lenPattern;
3879 while(lenText) {
3880 if (text[--lenText] == last) {
3881 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
3882 return lenText;
3883 }
3884 }
3885 }
3886 return str::npos;
3887 }
3898 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
3899 return find_last(pattern.symbols(), pattern.length(), offset);
3900 }
3901
3911 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
3912 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3913 return fnd == str::npos ? fnd : fnd + pattern.length();
3914 }
3915
3925 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3926 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
3927 return fnd == str::npos ? _len() : fnd;
3928 }
3929
3939 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3940 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3941 return fnd == str::npos ? _len() : fnd + pattern.length();
3942 }
3943
3953 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
3954 return find(pattern, offset) != str::npos;
3955 }
3956
3966 constexpr size_t find(K s, size_t offset = 0) const noexcept {
3967 size_t len = _len();
3968 if (offset < len) {
3969 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3970 if (fnd)
3971 return static_cast<size_t>(fnd - str);
3972 }
3973 return str::npos;
3974 }
3975
3985 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
3986 size_t len = _len();
3987 if (offset < len) {
3988 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3989 if (fnd)
3990 return static_cast<size_t>(fnd - str);
3991 }
3992 return len;
3993 }
3994
3995 template<typename Op>
3996 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3997 if (!maxCount)
3998 maxCount--;
3999 while (maxCount-- > 0) {
4000 size_t fnd = find(pattern, patternLen, offset);
4001 if (fnd == str::npos)
4002 break;
4003 op(fnd);
4004 offset = fnd + patternLen;
4005 }
4006 }
4019 template<typename Op>
4020 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4021 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
4022 }
4023
4024 template<typename To = std::vector<size_t>>
4025 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
4026 To result;
4027 for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
4028 return result;
4029 }
4042 template<typename To = std::vector<size_t>>
4043 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4044 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
4045 }
4046 template<typename To = std::vector<size_t>>
4047 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
4048 return for_all_finded([&](size_t pos) {
4049 to.emplace_back(pos);
4050 }, pattern, len, offset, maxCount);
4051 }
4062 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
4063 size_t len = std::min(_len(), offset);
4064 const K *text = _str();
4065 while (len > 0) {
4066 if (text[--len] == s)
4067 return len;
4068 }
4069 return str::npos;
4070 }
4071
4081 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
4082 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4083 }
4084
4094 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
4095 const K* text = _str();
4096 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4097 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4098 }
4099
4109 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
4110 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4111 }
4112
4122 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4123 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4124 }
4125
4135 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
4136 const K* text = _str();
4137 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4138 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4139 }
4140
4150 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4151 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4152 }
4153
4163 [[deprecated("Use substring(start, end)")]]
4164 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
4165 return my_type{d()(from, len)};
4166 }
4167
4175 template<typename T, first_index_in_str S, last_index_in_str E> requires std::is_constructible_v<T, str_piece>
4176 constexpr T slice_to(S start, E end) const noexcept {
4177 str_piece res = slice(start, end);
4178 if constexpr (std::is_constructible_v<T, const my_type&> && !std::is_same_v<T, str_piece>) {
4179 if (res.is_same(*this)) {
4180 return T{*this};
4181 }
4182 }
4183 return T{res};
4184 }
4185
4189 template<first_index_in_str S, last_index_in_str E>
4190 constexpr my_type substring(S start, E end) const noexcept {
4191 return slice_to<my_type>(start, end);
4192 }
4193
4203 [[deprecated("Use slice_to(start, end)")]]
4204 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
4205 return my_type{d().mid(from, len)};
4206 }
4207
4235 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4236 constexpr T as_int() const noexcept {
4237 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4238 return err == IntConvertResult::Overflow ? 0 : res;
4239 }
4240
4268 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4269 constexpr convert_result<T> to_int() const noexcept {
4270 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4271 }
4272
4278 template<bool SkipWS = true, bool AllowPlus = true>
4279 requires(sizeof(K) == 1 &&
4280 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4281 std::optional<double> to_double() const noexcept {
4282 size_t len = _len();
4283 const K* ptr = _str();
4284 if constexpr (SkipWS) {
4285 while (len && uns_type(*ptr) <= ' ') {
4286 len--;
4287 ptr++;
4288 }
4289 }
4290 if constexpr (AllowPlus) {
4291 if (len && *ptr == K('+')) {
4292 ptr++;
4293 len--;
4294 }
4295 }
4296 if (!len) {
4297 return {};
4298 }
4299 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4300 double d{};
4301 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
4302 return d;
4303 }
4304 }
4305 return {};
4306 }
4307
4313 template<bool SkipWS = true>
4314 requires(sizeof(K) == 1 &&
4315 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4316 std::optional<double> to_double_hex() const noexcept {
4317 size_t len = _len();
4318 const K* ptr = _str();
4319 if constexpr (SkipWS) {
4320 while (len && uns_type(*ptr) <= ' ') {
4321 len--;
4322 ptr++;
4323 }
4324 }
4325 if (len) {
4326 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4327 double d{};
4328 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4329 return d;
4330 }
4331 }
4332 }
4333 return {};
4334 }
4335
4343 template<ToIntNumber T>
4344 constexpr void as_number(T& t) const {
4345 t = as_int<T>();
4346 }
4347
4348 template<typename T, typename Op>
4349 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
4350 size_t mylen = _len();
4351 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4352 str_piece me{_str(), mylen};
4353 for (size_t i = 0;; i++) {
4354 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4355 if (beginOfDelim == str::npos) {
4356 str_piece last{me.symbols() + offset, me.length() - offset};
4357 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4358 beforeFunc(last);
4359 }
4360 if constexpr (requires { results.emplace_back(last); }) {
4361 if (last.is_same(me)) {
4362 // Пробуем положить весь объект.
4363 // Try to put the entire object.
4364 results.emplace_back(d());
4365 } else {
4366 results.emplace_back(last);
4367 }
4368 } else if constexpr (requires { results.push_back(last); }) {
4369 if (last.is_same(me)) {
4370 // Пробуем положить весь объект.
4371 // Try to put the entire object.
4372 results.push_back(d());
4373 } else {
4374 results.push_back(last);
4375 }
4376 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
4377 if (i < std::size(results)) {
4378 if (last.is_same(me)) {
4379 // Пробуем положить весь объект.
4380 // Try to put the entire object.
4381 results[i] = d();
4382 } else
4383 results[i] = last;
4384 }
4385 }
4386 break;
4387 }
4388 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4389 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4390 beforeFunc(piece);
4391 }
4392 if constexpr (requires { results.emplace_back(piece); }) {
4393 results.emplace_back(piece);
4394 } else if constexpr (requires { results.push_back(piece); }) {
4395 results.push_back(piece);
4396 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
4397 if (i < std::size(results)) {
4398 results[i] = piece;
4399 if (i == results.size() - 1) {
4400 break;
4401 }
4402 }
4403 }
4404 offset = beginOfDelim + lendelimiter;
4405 }
4406 if constexpr (!std::is_same_v<T, void>) {
4407 return results;
4408 }
4409 }
4440 template<typename T, typename Op>
4441 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
4442 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4443 }
4444
4456 template<typename T>
4457 constexpr T split(str_piece delimiter, size_t offset = 0) const {
4458 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4459 }
4460
4461 // Начинается ли эта строка с указанной подстроки
4462 // Does this string start with the specified substring
4463 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
4464 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4465 }
4472 constexpr bool starts_with(str_piece prefix) const noexcept {
4473 return starts_with(prefix.symbols(), prefix.length());
4474 }
4475
4481 constexpr bool starts_with_and_ws(str_piece prefix) const noexcept {
4482 return _len() > prefix.length() &&
4483 starts_with(prefix) &&
4484 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4485 }
4486
4494 constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4495 return _len() > prefix.length() &&
4496 starts_with(prefix) &&
4497 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4498 }
4499 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4500 constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4501 return _len() >= N &&
4502 starts_with(prefix) &&
4503 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4504 }
4505
4506 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
4507 size_t myLen = _len();
4508 if (myLen < len) {
4509 return false;
4510 }
4511 const K* ptr1 = _str();
4512 while (len--) {
4513 K s1 = *ptr1++, s2 = *prefix++;
4514 if (s1 == s2)
4515 continue;
4516 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4517 return false;
4518 }
4519 return true;
4520 }
4527 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
4528 return starts_with_ia(prefix.symbols(), prefix.length());
4529 }
4530
4536 constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept {
4537 return _len() > prefix.length() &&
4538 starts_with_ia(prefix) &&
4539 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4540 }
4541
4549 constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4550 return _len() > prefix.length() &&
4551 starts_with_ia(prefix) &&
4552 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4553 }
4554 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4555 constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4556 return _len() >= N &&
4557 starts_with_ia(prefix) &&
4558 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4559 }
4560
4561 // Является ли эта строка началом указанной строки
4562 // Is this string the beginning of the specified string
4563 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
4564 size_t myLen = _len();
4565 if (myLen > len)
4566 return false;
4567 return !myLen || 0 == traits::compare(text, _str(), myLen);
4568 }
4575 constexpr bool prefix_in(str_piece text) const noexcept {
4576 return prefix_in(text.symbols(), text.length());
4577 }
4578 // Заканчивается ли строка указанной подстрокой
4579 // Does the string end with the specified substring
4580 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
4581 size_t myLen = _len();
4582 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4583 }
4590 constexpr bool ends_with(str_piece suffix) const noexcept {
4591 return ends_with(suffix.symbols(), suffix.length());
4592 }
4593 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
4594 // Whether the string ends with the specified substring, case insensitive ASCII
4595 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
4596 size_t myLen = _len();
4597 if (myLen < len) {
4598 return false;
4599 }
4600 const K* ptr1 = _str() + myLen - len;
4601 while (len--) {
4602 K s1 = *ptr1++, s2 = *suffix++;
4603 if (s1 == s2)
4604 continue;
4605 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4606 return false;
4607 }
4608 return true;
4609 }
4616 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
4617 return ends_with_ia(suffix.symbols(), suffix.length());
4618 }
4619
4623 constexpr bool is_ascii() const noexcept {
4624 if (_is_empty())
4625 return true;
4626 if (std::is_constant_evaluated()) {
4627 for (size_t idx = 0; idx < _len(); idx++) {
4628 if (uns_type(_str()[idx]) > 127) {
4629 return false;
4630 }
4631 }
4632 return true;
4633 }
4634 const int sl = ascii_mask<K>::WIDTH;
4635 const size_t mask = ascii_mask<K>::VALUE;
4636 size_t len = _len();
4637 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
4638 if constexpr (sl > 1) {
4639 const size_t roundMask = sizeof(size_t) - 1;
4640 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4641 if (*ptr++ > 127)
4642 return false;
4643 len--;
4644 }
4645 while (len >= sl) {
4646 if (*reinterpret_cast<const size_t*>(ptr) & mask)
4647 return false;
4648 ptr += sl;
4649 len -= sl;
4650 }
4651 }
4652 while (len--) {
4653 if (*ptr++ > 127)
4654 return false;
4655 }
4656 return true;
4657 }
4658
4666 template<typename R = my_type>
4668 return R::upperred_only_ascii_from(d());
4669 }
4670
4678 template<typename R = my_type>
4680 return R::lowered_only_ascii_from(d());
4681 }
4682
4698 template<typename R = my_type>
4699 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
4700 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4701 }
4702
4714 template<typename R = str_piece>
4715 constexpr std::optional<R> strip_prefix(str_piece prefix) const {
4716 if (starts_with(prefix)) {
4717 return R{slice(prefix.length(), to_end)};
4718 }
4719 return {};
4720 }
4721
4733 template<typename R = str_piece>
4734 constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
4735 if (starts_with_ia(prefix)) {
4736 return R{slice(prefix.length(), to_end)};
4737 }
4738 return {};
4739 }
4740
4752 template<typename R = str_piece>
4753 constexpr std::optional<R> strip_suffix(str_piece suffix) const {
4754 if (ends_with(suffix)) {
4755 return R{slice(0, from_end{suffix.length()})};
4756 }
4757 return {};
4758 }
4759
4771 template<typename R = str_piece>
4772 constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
4773 if (ends_with_ia(suffix)) {
4774 return R{slice(0, from_end{suffix.length()})};
4775 }
4776 return {};
4777 }
4778
4779 template<StrType<K> From>
4780 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
4781 str_piece sfrom = from, newPos = opTrim(sfrom);
4782 if (newPos.is_same(sfrom)) {
4783 my_type res = from;
4784 return res;
4785 }
4786 return my_type{newPos};
4787 }
4788 template<TrimSides S, StrType<K> From>
4789 constexpr static my_type trim_static(const From& from) {
4790 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
4791 }
4792
4793 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
4794 requires is_const_pattern<N>
4795 constexpr static my_type trim_static(const From& from, T&& pattern) {
4796 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
4797 }
4798
4799 template<TrimSides S, bool withSpaces, StrType<K> From>
4800 constexpr static my_type trim_static(const From& from, str_piece pattern) {
4801 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
4802 }
4811 template<typename R = str_piece>
4812 constexpr R trimmed() const {
4813 return R::template trim_static<TrimSides::TrimAll>(d());
4814 }
4815
4823 template<typename R = str_piece>
4824 constexpr R trimmed_left() const {
4825 return R::template trim_static<TrimSides::TrimLeft>(d());
4826 }
4827
4835 template<typename R = str_piece>
4836 constexpr R trimmed_right() const {
4837 return R::template trim_static<TrimSides::TrimRight>(d());
4838 }
4839
4849 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4850 requires is_const_pattern<N>
4851 constexpr R trimmed(T&& pattern) const {
4852 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4853 }
4854
4864 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4865 requires is_const_pattern<N>
4866 constexpr R trimmed_left(T&& pattern) const {
4867 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4868 }
4869
4879 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4880 requires is_const_pattern<N>
4881 constexpr R trimmed_right(T&& pattern) const {
4882 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4883 }
4884 // Триминг по символам в литерале и пробелам
4885 // Trimming by characters in literal and spaces
4886
4901 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4902 requires is_const_pattern<N>
4903 constexpr R trimmed_with_spaces(T&& pattern) const {
4904 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4905 }
4906
4920 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4921 requires is_const_pattern<N>
4922 constexpr R trimmed_left_with_spaces(T&& pattern) const {
4923 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4924 }
4925
4939 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4940 requires is_const_pattern<N>
4941 constexpr R trimmed_right_with_spaces(T&& pattern) const {
4942 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4943 }
4944 // Триминг по динамическому источнику
4945 // Trimming by dynamic source
4946
4957 template<typename R = str_piece>
4958 constexpr R trimmed(str_piece pattern) const {
4959 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4960 }
4961
4971 template<typename R = str_piece>
4972 constexpr R trimmed_left(str_piece pattern) const {
4973 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4974 }
4975
4985 template<typename R = str_piece>
4986 constexpr R trimmed_right(str_piece pattern) const {
4987 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4988 }
4989
5003 template<typename R = str_piece>
5004 constexpr R trimmed_with_spaces(str_piece pattern) const {
5005 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
5006 }
5007
5021 template<typename R = str_piece>
5022 constexpr R trimmed_left_with_spaces(str_piece pattern) const {
5023 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5024 }
5025
5039 template<typename R = str_piece>
5040 constexpr R trimmed_right_with_spaces(str_piece pattern) const {
5041 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5042 }
5043
5055 template<typename R = str_piece>
5056 constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
5057 str_piece res = *this;
5058 while(res.starts_with(prefix)) {
5059 res.remove_prefix(prefix.length());
5060 if (--max_count == 0) {
5061 break;
5062 }
5063 }
5064 return res;
5065 }
5066
5078 template<typename R = str_piece>
5079 constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
5080 str_piece res = *this;
5081 while(res.starts_with_ia(prefix)) {
5082 res.remove_prefix(prefix.length());
5083 if (--max_count == 0) {
5084 break;
5085 }
5086 }
5087 return res;
5088 }
5089
5101 template<typename R = str_piece>
5102 constexpr R trimmed_suffix(str_piece suffix) const {
5103 str_piece res = *this;
5104 while(res.ends_with(suffix)) {
5105 res.remove_suffix(suffix.length());
5106 }
5107 return res;
5108 }
5109
5121 template<typename R = str_piece>
5122 constexpr R trimmed_suffix_ia(str_piece suffix) const {
5123 str_piece res = *this;
5124 while(res.ends_with_ia(suffix)) {
5125 res.remove_suffix(suffix.length());
5126 }
5127 return res;
5128 }
5129
5139 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
5140 return SplitterBase<K, str_piece>{*this, delimiter};
5141 }
5142};
5143
5144template<size_t N> requires (N > 1)
5145struct find_all_container {
5146 static constexpr size_t max_capacity = N;
5147 size_t positions_[N];
5148 size_t added_{};
5149
5150 constexpr void emplace_back(size_t pos) {
5151 positions_[added_++] = pos;
5152 }
5153};
5154
5173template<typename K>
5174struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
5175 using symb_type = K;
5176 using my_type = str_src<K>;
5177
5178 const symb_type* str;
5179 size_t len;
5180
5181 str_src() = default;
5186 template<typename T, size_t N = const_lit_for<K, T>::Count>
5187 constexpr str_src(T&& v) noexcept : str((const K*)v), len(N - 1) {}
5188
5193 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
5194
5195 template<StrType<K> T>
5196 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
5197
5202 template<typename A>
5203 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
5208 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
5209
5214 constexpr size_t length() const noexcept {
5215 return len;
5216 }
5217
5221 constexpr const symb_type* symbols() const noexcept {
5222 return str;
5223 }
5224
5228 constexpr bool is_empty() const noexcept {
5229 return len == 0;
5230 }
5231
5237 constexpr bool is_same(str_src<K> other) const noexcept {
5238 return str == other.str && len == other.len;
5239 }
5240
5246 constexpr bool is_part_of(str_src<K> other) const noexcept {
5247 return str >= other.str && str + len <= other.str + other.len;
5248 }
5249
5257 constexpr K operator[](size_t idx) const {
5258 return str[idx];
5259 }
5260
5268 constexpr my_type& remove_prefix(size_t delta) {
5269 str += delta;
5270 len -= delta;
5271 return *this;
5272 }
5273
5281 constexpr my_type& remove_suffix(size_t delta) {
5282 len -= delta;
5283 return *this;
5284 }
5285};
5286
5309template<typename K>
5310struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
5311 using symb_type = K;
5312 using my_type = str_src_nt<K>;
5313 using base = str_src<K>;
5314
5315 constexpr static const K empty_string[1] = {0};
5316
5317 str_src_nt() = default;
5334 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
5335 constexpr explicit str_src_nt(T&& p) noexcept {
5336 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
5337 base::str = base::len ? p : empty_string;
5338 }
5339
5343 template<typename T, size_t N = const_lit_for<K, T>::Count>
5344 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
5345
5350 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
5351
5352 template<StrType<K> T>
5353 constexpr str_src_nt(T&& t) {
5354 base::str = t.symbols();
5355 base::len = t.length();
5356 }
5361 template<typename A>
5362 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5363
5364 static const my_type empty_str;
5373 constexpr my_type to_nts(size_t from) {
5374 if (from > base::len) {
5375 from = base::len;
5376 }
5377 return {base::str + from, base::len - from};
5378 }
5379};
5380
5381template<typename K>
5382inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5383template<typename K> struct simple_str_selector;
5384
5385inline namespace literals {
5445template<char...Chars>
5446SS_CONSTEVAL auto operator""_fmt() {
5447 return f::skip_0x<Chars...>();
5448}
5449
5450} // namespace literals
5451
5452#ifndef IN_FULL_SIMSTR
5453
5454template<typename K>
5455using simple_str = str_src<K>;
5456
5457template<typename K>
5458struct simple_str_selector {
5459 using type = simple_str<K>;
5460};
5461
5462template<typename K>
5463using simple_str_nt = str_src_nt<K>;
5464
5465template<typename K>
5466using Splitter = SplitterBase<K, str_src<K>>;
5467
5468using ssa = str_src<u8s>;
5469using ssb = str_src<ubs>;
5470using ssw = str_src<wchar_t>;
5471using ssu = str_src<u16s>;
5472using ssuu = str_src<u32s>;
5473using stra = str_src_nt<u8s>;
5474using strb = str_src_nt<ubs>;
5475using strw = str_src_nt<wchar_t>;
5476using stru = str_src_nt<u16s>;
5477using struu = str_src_nt<u32s>;
5478
5479template<typename K>
5480consteval 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) {
5481 if constexpr (std::is_same_v<K, u8s>)
5482 return s8;
5483 if constexpr (std::is_same_v<K, ubs>)
5484 return sb;
5485 if constexpr (std::is_same_v<K, uws>)
5486 return sw;
5487 if constexpr (std::is_same_v<K, u16s>)
5488 return s16;
5489 if constexpr (std::is_same_v<K, u32s>)
5490 return s32;
5491}
5492
5493#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5494
5495inline namespace literals {
5496
5507SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
5508 return str_src_nt<u8s>{ptr, l};
5509}
5510
5520SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
5521 return str_src_nt<ubs>{ptr, l};
5522}
5523
5533SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
5534 return str_src_nt<uws>{ptr, l};
5535}
5536
5546SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
5547 return str_src_nt<u16s>{ptr, l};
5548}
5549
5560SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
5561 return str_src_nt<u32s>{ptr, l};
5562}
5563
5564} // namespace literals
5565
5566#endif
5567
5568template<typename K, bool withSpaces>
5569struct CheckSpaceTrim {
5570 constexpr bool is_trim_spaces(K s) const {
5571 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
5572 }
5573};
5574template<typename K>
5575struct CheckSpaceTrim<K, false> {
5576 constexpr bool is_trim_spaces(K) const {
5577 return false;
5578 }
5579};
5580
5581template<typename K>
5582struct CheckSymbolsTrim {
5583 str_src<K> symbols;
5584 constexpr bool is_trim_symbols(K s) const {
5585 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
5586 }
5587};
5588
5589template<typename K, size_t N>
5590struct CheckConstSymbolsTrim {
5591 const const_lit_to_array<K, N> symbols;
5592
5593 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
5594 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5595
5596 constexpr bool is_trim_symbols(K s) const noexcept {
5597 return symbols.contain(s);
5598 }
5599};
5600
5601template<typename K>
5602struct CheckConstSymbolsTrim<K, 0> {
5603 constexpr bool is_trim_symbols(K) const {
5604 return false;
5605 }
5606};
5607
5608template<typename K, size_t N>
5609struct SymbSelector {
5610 using type = CheckConstSymbolsTrim<K, N>;
5611};
5612
5613template<typename K>
5614struct SymbSelector<K, 0> {
5615 using type = CheckSymbolsTrim<K>;
5616};
5617
5618template<typename K>
5619struct SymbSelector<K, static_cast<size_t>(-1)> {
5620 using type = CheckConstSymbolsTrim<K, 0>;
5621};
5622
5623template<TrimSides S, typename K, size_t N, bool withSpaces>
5624struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5625 constexpr bool isTrim(K s) const {
5626 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5627 }
5628 constexpr str_src<K> operator()(str_src<K> from) const {
5629 if constexpr ((S & TrimSides::TrimLeft) != 0) {
5630 while (from.len) {
5631 if (isTrim(*from.str)) {
5632 from.str++;
5633 from.len--;
5634 } else
5635 break;
5636 }
5637 }
5638 if constexpr ((S & TrimSides::TrimRight) != 0) {
5639 const K* back = from.str + from.len - 1;
5640 while (from.len) {
5641 if (isTrim(*back)) {
5642 back--;
5643 from.len--;
5644 } else
5645 break;
5646 }
5647 }
5648 return from;
5649 }
5650};
5651
5652template<TrimSides S, typename K>
5653using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
5654
5655template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
5656 requires is_const_pattern<N>
5657constexpr inline auto trimOp(T&& pattern) {
5658 return trim_operator<S, K, N - 1, withSpaces>{pattern};
5659}
5660
5661template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
5662constexpr inline auto trimOp(str_src<K> pattern) {
5663 return trim_operator<S, K, 0, withSpaces>{pattern};
5664}
5665
5666static constexpr size_t FIND_CACHE_SIZE = 16;
5667
5668template<typename K, size_t N, size_t L>
5669struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
5670 using symb_type = K;
5671 using my_type = expr_replaces<K, N, L>;
5672 str_src<K> what;
5673 const K(&pattern)[N + 1];
5674 const K(&repl)[L + 1];
5675 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5676 mutable size_t last_;
5677
5678 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
5679
5680 constexpr size_t length() const {
5681 size_t l = what.length();
5682 if constexpr (N == L) {
5683 return l;
5684 }
5685 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
5686 if (matches_.added_) {
5687 last_ = matches_.positions_[matches_.added_ - 1] + N;
5688 l += int(L - N) * matches_.added_;
5689
5690 if (matches_.added_ == FIND_CACHE_SIZE) {
5691 for (;;) {
5692 size_t next = what.find(pattern, N, last_);
5693 if (next == str::npos) {
5694 break;
5695 }
5696 last_ = next + N;
5697 l += L - N;
5698 }
5699 }
5700 }
5701 if (!l) {
5702 matches_.added_ = -1;
5703 }
5704 return l;
5705 }
5706 constexpr K* place(K* ptr) const noexcept {
5707 if constexpr (N == L) {
5708 const K* from = what.symbols();
5709 for (size_t start = 0; start < what.length();) {
5710 size_t next = what.find(pattern, N, start);
5711 if (next == str::npos) {
5712 next = what.length();
5713 }
5714 size_t delta = next - start;
5715 ch_traits<K>::copy(ptr, from + start, delta);
5716 ptr += delta;
5717 ch_traits<K>::copy(ptr, repl, L);
5718 ptr += L;
5719 start = next + N;
5720 }
5721 return ptr;
5722 }
5723 if (matches_.added_ == 0) {
5724 return what.place(ptr);
5725 } else if (matches_.added_ == size_t(-1)) {
5726 // after replaces text become empty
5727 return ptr;
5728 }
5729 const K* from = what.symbols();
5730 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5731 ch_traits<K>::copy(ptr, from + start, offset - start);
5732 ptr += offset - start;
5733 ch_traits<K>::copy(ptr, repl, L);
5734 ptr += L;
5735 start = offset + N;
5736 if (start >= last_) {
5737 size_t tail = what.length() - last_;
5738 ch_traits<K>::copy(ptr, from + last_, tail);
5739 ptr += tail;
5740 break;
5741 } else {
5742 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
5743 }
5744 }
5745 return ptr;
5746 }
5747};
5748
5762template<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>
5763 requires(N > 1)
5764constexpr auto e_repl(A&& w, T&& p, X&& r) {
5765 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
5766}
5767
5775template<typename K>
5776struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
5777 using symb_type = K;
5778 using my_type = expr_replaced<K>;
5779 str_src<K> what;
5780 const str_src<K> pattern;
5781 const str_src<K> repl;
5782 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5783 mutable size_t last_;
5794 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
5795
5796 constexpr size_t length() const {
5797 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
5798 if (!plen || plen == rlen) {
5799 return l;
5800 }
5801 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
5802 if (matches_.added_) {
5803 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5804 l += int(rlen - plen) * matches_.added_;
5805
5806 if (matches_.added_ == FIND_CACHE_SIZE) {
5807 for (;;) {
5808 size_t next = what.find(pattern.symbols(), plen, last_);
5809 if (next == str::npos) {
5810 break;
5811 }
5812 last_ = next + plen;
5813 l += rlen - plen;
5814 }
5815 }
5816 }
5817 if (!l) {
5818 matches_.added_ = -1;
5819 }
5820 return l;
5821 }
5822 constexpr K* place(K* ptr) const noexcept {
5823 size_t plen = pattern.length(), rlen = repl.length();
5824 if (plen == rlen) {
5825 const K* from = what.symbols();
5826 for (size_t start = 0; start < what.length();) {
5827 size_t next = what.find(pattern, start);
5828 if (next == str::npos) {
5829 next = what.length();
5830 }
5831 size_t delta = next - start;
5832 ch_traits<K>::copy(ptr, from + start, delta);
5833 ptr += delta;
5834 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5835 ptr += rlen;
5836 start = next + plen;
5837 }
5838 return ptr;
5839 }
5840 if (matches_.added_ == 0) {
5841 return what.place(ptr);
5842 } else if (matches_.added_ == -1) {
5843 // after replaces text become empty
5844 return ptr;
5845 }
5846 const K* from = what.symbols();
5847 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5848 ch_traits<K>::copy(ptr, from + start, offset - start);
5849 ptr += offset - start;
5850 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5851 ptr += rlen;
5852 start = offset + plen;
5853 if (start >= last_) {
5854 size_t tail = what.length() - start;
5855 ch_traits<K>::copy(ptr, from + start, tail);
5856 ptr += tail;
5857 break;
5858 } else {
5859 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
5860 }
5861 }
5862 return ptr;
5863 }
5864};
5865
5881template<typename K, StrExprForType<K> E>
5882struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
5883 using symb_type = K;
5884 using my_type = expr_replaced<K>;
5885 str_src<K> what;
5886 const str_src<K> pattern;
5887 mutable size_t replLen;
5888 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5889 mutable size_t last_;
5890 const E& expr;
5901 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
5902
5903 constexpr size_t length() const {
5904 size_t l = what.length(), plen = pattern.length();
5905 if (!plen) {
5906 return l;
5907 }
5908 matches_.positions_[0] = what.find(pattern);
5909 if (matches_.positions_[0] == -1) {
5910 // Не нашли вхождений, нечего менять
5911 return l;
5912 }
5913 matches_.added_ = 1;
5914 // Вхождение есть, надо теперь получить длину замены
5915 replLen = expr.length();
5916 if (replLen == plen) {
5917 // Замена той же длины, общая длина не изменится
5918 return l;
5919 }
5920 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
5921
5922 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5923 l += int(replLen - plen) * matches_.added_;
5924
5925 if (matches_.added_ == FIND_CACHE_SIZE) {
5926 for (;;) {
5927 size_t next = what.find(pattern.symbols(), plen, last_);
5928 if (next == str::npos) {
5929 break;
5930 }
5931 last_ = next + plen;
5932 l += replLen - plen;
5933 }
5934 }
5935 if (!l) {
5936 matches_.added_ = -1;
5937 }
5938 return l;
5939 }
5940 constexpr K* place(K* ptr) const noexcept {
5941 if (matches_.added_ == 0) {
5942 // не было найдено вхождений
5943 return what.place(ptr);
5944 } else if (matches_.added_ == -1) {
5945 // Строка стала пустой
5946 return ptr;
5947 }
5948 size_t plen = pattern.length();
5949 const K* from = what.symbols();
5950 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
5951 ptr += matches_.positions_[0];
5952 const K* repl = ptr;
5953 expr.place((typename E::symb_type*)ptr);
5954 ptr += replLen;
5955 size_t start = matches_.positions_[0] + plen;
5956
5957 if (plen == replLen) {
5958 for (;;) {
5959 size_t next = what.find(pattern, start);
5960 if (next == str::npos) {
5961 break;
5962 }
5963 size_t delta = next - start;
5964 ch_traits<K>::copy(ptr, from + start, delta);
5965 ptr += delta;
5966 ch_traits<K>::copy(ptr, repl, replLen);
5967 ptr += replLen;
5968 start = next + plen;
5969 }
5970 } else {
5971 for (size_t idx = 1;;) {
5972 if (start >= last_) {
5973 break;
5974 }
5975 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
5976 size_t delta = next - start;
5977 ch_traits<K>::copy(ptr, from + start, delta);
5978 ptr += delta;
5979 ch_traits<K>::copy(ptr, repl, replLen);
5980 ptr += replLen;
5981 start = next + plen;
5982 }
5983 }
5984 size_t tail = what.length() - start;
5985 ch_traits<K>::copy(ptr, from + start, tail);
5986 return ptr + tail;
5987 }
5988};
5989
6003template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, typename X>
6004 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>))
6005constexpr auto e_repl(A&& w, T&& p, X&& r) {
6006 str_src<K> pattern{std::forward<T>(p)};
6007 str_src<K> repl{std::forward<X>(r)};
6008 return expr_replaced<K>{std::forward<A>(w), pattern, repl};
6009}
6010
6024template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, StrExprForType<K> E>
6025 requires std::is_constructible_v<str_src<K>, T>
6026constexpr auto e_repl(A&& w, T&& p, const E& expr) {
6027 str_src<K> pattern{std::forward<T>(p)};
6028 return expr_replaced_e<K, E>{std::forward<A>(w), pattern, expr};
6029}
6030
6031template<bool UseVectorForReplace>
6032struct replace_search_result_store {
6033 size_t count_{};
6034 std::pair<size_t, size_t> replaces_[16];
6035};
6036
6037template<>
6038struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6039
6040// Строковое выражение для замены символов
6041// String expression to replace characters
6042template<typename K, size_t N, bool UseVectorForReplace>
6043struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
6044 using symb_type = K;
6045 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6046 const K pattern_[N];
6047 const str_src<K> source_;
6048 const str_src<K> replaces_[N];
6049
6050 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6051
6052 [[_no_unique_address]]
6053 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
6054
6055 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
6056 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6057
6058 size_t length() const {
6059 size_t l = source_.length();
6060 auto [fnd, num] = find_first_of(source_.str, source_.len);
6061 if (fnd == str::npos) {
6062 return l;
6063 }
6064 l += replaces_[num].len - 1;
6065 if constexpr (UseVectorForReplace) {
6066 search_results_.reserve((l >> 4) + 8);
6067 search_results_.emplace_back(fnd, num);
6068 for (size_t start = fnd + 1;;) {
6069 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6070 if (fnd == str::npos) {
6071 break;
6072 }
6073 search_results_.emplace_back(fnd, idx);
6074 start = fnd + 1;
6075 l += replaces_[idx].len - 1;
6076 }
6077 } else {
6078 const size_t max_store = std::size(search_results_.replaces_);
6079 search_results_.replaces_[0] = {fnd, num};
6080 search_results_.count_++;
6081 for (size_t start = fnd + 1;;) {
6082 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6083 if (found == str::npos) {
6084 break;
6085 }
6086 if (search_results_.count_ < max_store) {
6087 search_results_.replaces_[search_results_.count_] = {found, idx};
6088 }
6089 l += replaces_[idx].len - 1;
6090 search_results_.count_++;
6091 start = found + 1;
6092 }
6093 }
6094 return l;
6095 }
6096 K* place(K* ptr) const noexcept {
6097 size_t start = 0;
6098 const K* text = source_.str;
6099 if constexpr (UseVectorForReplace) {
6100 for (const auto& [pos, num] : search_results_) {
6101 size_t delta = pos - start;
6102 ch_traits<K>::copy(ptr, text + start, delta);
6103 ptr += delta;
6104 ptr = replaces_[num].place(ptr);
6105 start = pos + 1;
6106 }
6107 } else {
6108 const size_t max_store = std::size(search_results_.replaces_);
6109 size_t founded = search_results_.count_;
6110 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6111 const auto [pos, num] = search_results_.replaces_[idx];
6112 size_t delta = pos - start;
6113 ch_traits<K>::copy(ptr, text + start, delta);
6114 ptr += delta;
6115 ptr = replaces_[num].place(ptr);
6116 start = pos + 1;
6117 }
6118 if (founded > max_store) {
6119 founded -= max_store;
6120 while (founded--) {
6121 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6122 size_t delta = fnd - start;
6123 ch_traits<K>::copy(ptr, text + start, delta);
6124 ptr += delta;
6125 ptr = replaces_[idx].place(ptr);
6126 start = fnd + 1;
6127 }
6128 }
6129 }
6130 size_t tail = source_.len - start;
6131 ch_traits<K>::copy(ptr, text + start, tail);
6132 return ptr + tail;
6133 }
6134
6135protected:
6136 template<typename ... Repl>
6137 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
6138 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6139
6140 template<typename ... Repl> requires (sizeof...(Repl) == N)
6141 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
6142 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6143 {
6144 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6145 for (size_t idx = 0; idx < N; idx++) {
6146 uu8s s = static_cast<uu8s>(pattern_[idx]);
6147 if constexpr (sizeof(K) == 1) {
6148 bit_mask_[s >> 3] |= 1 << (s & 7);
6149 } else {
6150 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6151 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6152 } else {
6153 bit_mask_[s >> 3] |= 1 << (s & 7);
6154 }
6155 }
6156 }
6157 }
6158 }
6159
6160 template<size_t Idx>
6161 size_t index_of(K s) const {
6162 if constexpr (Idx < N) {
6163 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6164 }
6165 return -1;
6166 }
6167 bool is_in_mask(uu8s s) const {
6168 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6169 }
6170 bool is_in_mask2(uu8s s) const {
6171 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6172 }
6173 bool is_in_pattern(K s, size_t& idx) const {
6174 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6175 if constexpr (sizeof(K) == 1) {
6176 if (is_in_mask(s)) {
6177 idx = index_of<0>(s);
6178 return true;
6179 }
6180 } else {
6181 if (std::make_unsigned_t<const K>(s) > 255) {
6182 if (is_in_mask2(s)) {
6183 return (idx = index_of<0>(s)) != -1;
6184 }
6185 } else {
6186 if (is_in_mask(s)) {
6187 idx = index_of<0>(s);
6188 return true;
6189 }
6190 }
6191 }
6192 }
6193 return false;
6194 }
6195 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6196 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6197 size_t idx;
6198 while (offset < len) {
6199 if (is_in_pattern(text[offset], idx)) {
6200 return {offset, idx};
6201 }
6202 offset++;
6203 }
6204 } else {
6205 while (offset < len) {
6206 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6207 return {offset, idx};
6208 }
6209 offset++;
6210 }
6211 }
6212 return {-1, -1};
6213 }
6214};
6215
6255template<bool UseVector = false, StrSource A, typename K = symb_type_from_src_t<A>, typename ... Repl>
6256 requires (sizeof...(Repl) % 2 == 0)
6257auto e_repl_const_symbols(A&& src, Repl&& ... other) {
6258 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
6259}
6260
6300template<typename K, bool UseVectorForReplace = false>
6301struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
6302 using symb_type = K;
6303 using str_t = typename simple_str_selector<K>::type;
6304 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6305
6306 const str_src<K> source_;
6307 const std::vector<std::pair<K, str_t>>& replaces_;
6308
6309 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
6310
6311 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6312
6313 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6341 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
6342 : source_(source), replaces_(repl)
6343 {
6344 size_t pattern_len = replaces_.size();
6345 pattern_.resize(pattern_len);
6346 K* pattern = pattern_.data();
6347
6348 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6349 *pattern++ = replaces_[idx].first;
6350 }
6351
6352 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6353 for (size_t idx = 0; idx < pattern_len; idx++) {
6354 uu8s s = static_cast<uu8s>(pattern_[idx]);
6355 if constexpr (sizeof(K) == 1) {
6356 bit_mask_[s >> 3] |= (1 << (s & 7));
6357 } else {
6358 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6359 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6360 } else {
6361 bit_mask_[s >> 3] |= (1 << (s & 7));
6362 }
6363 }
6364 }
6365 }
6366 }
6367
6368 size_t length() const {
6369 size_t l = source_.length();
6370 auto [fnd, num] = find_first_of(source_.str, source_.len);
6371 if (fnd == str::npos) {
6372 return l;
6373 }
6374 l += replaces_[num].second.len - 1;
6375 if constexpr (UseVectorForReplace) {
6376 search_results_.reserve((l >> 4) + 8);
6377 search_results_.emplace_back(fnd, num);
6378 for (size_t start = fnd + 1;;) {
6379 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6380 if (fnd == str::npos) {
6381 break;
6382 }
6383 search_results_.emplace_back(fnd, idx);
6384 start = fnd + 1;
6385 l += replaces_[idx].second.len - 1;
6386 }
6387 } else {
6388 const size_t max_store = std::size(search_results_.replaces_);
6389 search_results_.replaces_[0] = {fnd, num};
6390 search_results_.count_++;
6391 for (size_t start = fnd + 1;;) {
6392 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6393 if (found == str::npos) {
6394 break;
6395 }
6396 if (search_results_.count_ < max_store) {
6397 search_results_.replaces_[search_results_.count_] = {found, idx};
6398 }
6399 l += replaces_[idx].second.len - 1;
6400 search_results_.count_++;
6401 start = found + 1;
6402 }
6403 }
6404 return l;
6405 }
6406 K* place(K* ptr) const noexcept {
6407 size_t start = 0;
6408 const K* text = source_.str;
6409 if constexpr (UseVectorForReplace) {
6410 for (const auto& [pos, num] : search_results_) {
6411 size_t delta = pos - start;
6412 ch_traits<K>::copy(ptr, text + start, delta);
6413 ptr += delta;
6414 ptr = replaces_[num].second.place(ptr);
6415 start = pos + 1;
6416 }
6417 } else {
6418 const size_t max_store = std::size(search_results_.replaces_);
6419 size_t founded = search_results_.count_;
6420 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6421 const auto [pos, num] = search_results_.replaces_[idx];
6422 size_t delta = pos - start;
6423 ch_traits<K>::copy(ptr, text + start, delta);
6424 ptr += delta;
6425 ptr = replaces_[num].second.place(ptr);
6426 start = pos + 1;
6427 }
6428 if (founded > max_store) {
6429 founded -= max_store;
6430 while (founded--) {
6431 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6432 size_t delta = fnd - start;
6433 ch_traits<K>::copy(ptr, text + start, delta);
6434 ptr += delta;
6435 ptr = replaces_[idx].second.place(ptr);
6436 start = fnd + 1;
6437 }
6438 }
6439 }
6440 size_t tail = source_.len - start;
6441 ch_traits<K>::copy(ptr, text + start, tail);
6442 return ptr + tail;
6443 }
6444
6445protected:
6446 size_t index_of(K s) const {
6447 return pattern_.find(s);
6448 }
6449
6450 bool is_in_mask(uu8s s) const {
6451 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6452 }
6453 bool is_in_mask2(uu8s s) const {
6454 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6455 }
6456
6457 bool is_in_pattern(K s, size_t& idx) const {
6458 if constexpr (sizeof(K) == 1) {
6459 if (is_in_mask(s)) {
6460 idx = index_of(s);
6461 return true;
6462 }
6463 } else {
6464 if (std::make_unsigned_t<const K>(s) > 255) {
6465 if (is_in_mask2(s)) {
6466 return (idx = index_of(s)) != -1;
6467 }
6468 } else {
6469 if (is_in_mask(s)) {
6470 idx = index_of(s);
6471 return true;
6472 }
6473 }
6474 }
6475 return false;
6476 }
6477
6478 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6479 size_t pl = pattern_.length();
6480 if (pl >= BIT_SEARCH_TRESHHOLD) {
6481 size_t idx;
6482 while (offset < len) {
6483 if (is_in_pattern(text[offset], idx)) {
6484 return {offset, idx};
6485 }
6486 offset++;
6487 }
6488 } else {
6489 while (offset < len) {
6490 if (size_t idx = index_of(text[offset]); idx != -1) {
6491 return {offset, idx};
6492 }
6493 offset++;
6494 }
6495 }
6496 return {-1, -1};
6497 }
6498};
6499
6500template<typename K, StrExprForType<K> T>
6501struct force_copy {
6502 const T& t_;
6503 force_copy(const T& t) : t_(t){}
6504};
6505
6506template<typename K, typename T>
6507struct symb_type_from_src<force_copy<K, T>> {
6508 using type = K;
6509};
6510
6511
6512template<StrExpr T>
6513force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6514
6515template<typename K, typename T>
6516constexpr auto to_subst(T&& t) {
6517 return to_strexpr<K>(std::forward<T>(t));
6518}
6519
6520template<typename K, typename T, size_t N>
6521constexpr auto to_subst(const T(&t)[N]) {
6522 return expr_literal<T, N - 1>{t};
6523}
6524
6525template<typename K, StrExprForType<K> T>
6526constexpr decltype(auto) to_subst(T&& t) {
6527 return std::forward<T>(t);
6528}
6529
6530template<typename K, typename T>
6531constexpr T to_subst(const force_copy<K, T>& t) {
6532 return t.t_;
6533}
6534
6535template<typename K, typename T>
6536constexpr T to_subst(force_copy<K, T>&& t) {
6537 return t.t_;
6538}
6539
6540template<typename K, typename T>
6541constexpr T to_subst(force_copy<K, T>& t) {
6542 return t.t_;
6543}
6544
6545template<typename K, typename T>
6546using to_str_exp_t = decltype(to_subst<K>(std::declval<T>()));
6547
6555template<typename K, typename G, typename Arg, typename...Args>
6556struct e_concat : expr_to_std_string<e_concat<K, G, Arg, Args...>> {
6557 using symb_type = K;
6558 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6559 using arg_t = to_str_exp_t<K, Arg>;
6560 to_str_exp_t<K, G> glue_;
6561 arg_t arg_;
6562 store_t args_;
6563 mutable size_t glue_len_;
6596 constexpr e_concat(G&& glue, Arg&& arg, Args&&...args)
6597 : glue_(to_subst<K>(std::forward<G>(glue)))
6598 , arg_(to_subst<K>(std::forward<Arg>(arg)))
6599 , args_(to_subst<K>(std::forward<Args>(args))...) {}
6600
6601 constexpr size_t length() const noexcept {
6602 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6603 glue_len_ = glue_.length();
6604 size_t l = arg_.length() + glue_len_ * sizeof...(Args);
6605 ((l += std::get<Indexes>(args_).length()),...);
6606 return l;
6607 }(std::make_index_sequence<sizeof...(Args)>());
6608 }
6609 constexpr K* place(K* ptr) const noexcept {
6610 return [this]<size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6611 ptr = (K*)arg_.place((typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6612 const K* glueStart = ptr;
6613 ptr = glue_.place(ptr);
6614 (
6615 (
6616 ptr = (K*)std::get<Indexes>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6617 glue_len_ > 0 && Indexes < sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) : nullptr
6618 ),
6619 ...);
6620 return ptr;
6621 }(ptr, std::make_index_sequence<sizeof...(Args)>());
6622 }
6623};
6624// CTAD deducing rule for e_concat
6625template<typename T, typename ... Args> requires (sizeof...(Args) > 1)
6626e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6627
6628struct parse_subst_string_error {
6629 parse_subst_string_error(const char*){}
6630};
6631
6632namespace details {
6633
6634template<typename K, size_t NParams>
6635constexpr size_t parse_pattern_string(str_src<K> pattern, auto&& add_part, auto&& add_param) {
6636 char used_args[NParams] = {0};
6637 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
6638 size_t all_len = 0;
6639
6640 auto find = [](const K* from, const K* last, K s) {
6641 while (from != last) {
6642 if (*from == s) {
6643 break;
6644 }
6645 from++;
6646 }
6647 return from;
6648 };
6649 size_t idx_in_params = 0;
6650
6651 while (first != last) {
6652 const K* open_pos = first;
6653 if (*first != '{') {
6654 open_pos = find(first, last, '{');
6655
6656 for (;;) {
6657 const K* close_pos = find(first, open_pos, '}');
6658 if (close_pos == open_pos) {
6659 unsigned len = open_pos - first;
6660 add_part(first - start, len);
6661 all_len += len;
6662 break;
6663 }
6664 ++close_pos;
6665 if (close_pos == open_pos || *close_pos != '}') {
6666 throw parse_subst_string_error{"unescaped }"};
6667 }
6668 unsigned len = close_pos - first;
6669 add_part(first - start, len);
6670 all_len += len;
6671 first = ++close_pos;
6672 }
6673 if (open_pos == last) {
6674 break;
6675 }
6676 }
6677 if (++open_pos == last) {
6678 throw parse_subst_string_error{"unescaped {"};
6679 }
6680 if (*open_pos == '}') {
6681 if (idx_in_params == -1) {
6682 throw parse_subst_string_error{"already used param ids"};
6683 }
6684 if (idx_in_params == NParams) {
6685 throw parse_subst_string_error{"too many params"};
6686 }
6687 used_args[idx_in_params] = 1;
6688 add_param(idx_in_params++);
6689 first = open_pos + 1;
6690 } else if (*open_pos == '{') {
6691 add_part(open_pos - start, 1);
6692 all_len++;
6693 first = open_pos + 1;
6694 } else {
6695 if (idx_in_params != 0 && idx_in_params != -1) {
6696 throw parse_subst_string_error{"already used non id params"};
6697 }
6698 idx_in_params = -1;
6699 const K* end = find(open_pos, last, '}');
6700 if (end == last) {
6701 throw parse_subst_string_error{"not found }"};
6702 }
6703 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
6704 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
6705 throw parse_subst_string_error{"bad param id"};
6706 }
6707 used_args[--p] = 1;
6708 add_param(p);
6709 first = end + 1;
6710 }
6711 }
6712 for (auto c : used_args) {
6713 if (!c) {
6714 throw parse_subst_string_error{"unused param"};
6715 }
6716 }
6717 return all_len;
6718}
6719
6720struct portion {
6721 unsigned start: 16;
6722 unsigned len: 15;
6723 unsigned is_param: 1;
6724
6725 portion() = default;
6726
6727 constexpr void set_param(unsigned param) {
6728 if (param >= (1 << 16)) {
6729 throw parse_subst_string_error{"the parameter id is too large"};
6730 }
6731 start = param;
6732 is_param = 1;
6733 }
6734 constexpr void set_part(unsigned from, unsigned l) {
6735 if (from >= (1 << 16) || len >= (1 << 15)) {
6736 throw parse_subst_string_error{"the string part is too large"};
6737 }
6738 start = from;
6739 len = l;
6740 is_param = 0;
6741 }
6742};
6743
6744template<typename K, size_t PtLen, size_t NParams>
6745struct subst_params {
6746 const K(&source_)[PtLen];
6747 unsigned all_len_{};
6748 unsigned actual_{};
6749 // The pattern string can be divided into a maximum of this number of portions.
6750 // "a{}a{}a{}a" - One portion of one symbol from the edge, and two portions for every three symbols
6751 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
6752
6753 consteval subst_params(const K(&pattern)[PtLen]) : source_(pattern) {
6754 all_len_ = parse_pattern_string<K, NParams>(pattern,
6755 [this](unsigned from, unsigned len) {
6756 portions_[actual_++].set_part(from, len);
6757 }, [this](unsigned param) {
6758 portions_[actual_++].set_param(param);
6759 });
6760 }
6761};
6762
6763} // namespace details
6764
6770template<typename K, size_t PtLen, typename ... Args>
6771struct e_subst : expr_to_std_string<e_subst<K, PtLen, Args...>> {
6772 inline static constexpr size_t NParams = sizeof...(Args);
6773 using symb_type = K;
6774 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6775
6776 const details::subst_params<K, PtLen, NParams>& subst_;
6777 store_t args_;
6818 constexpr e_subst(const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
6819 : subst_(subst)
6820 , args_(to_subst<K>(std::forward<Args>(args))...){}
6821
6822 constexpr size_t length() const noexcept {
6823 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6824 size_t idx = 0;
6825 size_t expr_length_[NParams] = {};
6826 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6827 size_t l = subst_.all_len_;
6828 for (idx = 0; idx < subst_.actual_; idx++) {
6829 if (subst_.portions_[idx].is_param) {
6830 l += expr_length_[subst_.portions_[idx].start];
6831 }
6832 }
6833 return l;
6834 }(std::make_index_sequence<sizeof...(Args)>());
6835 }
6836 template<size_t Idx>
6837 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6838 if (idx == Idx) {
6839 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6840 }
6841 if constexpr (Idx < NParams - 1) {
6842 return place_idx<Idx + 1>(ptr, idx);
6843 }
6844 return ptr;
6845 }
6846 constexpr K* place(K* ptr) const noexcept {
6847 for (size_t idx = 0; idx < subst_.actual_; idx++) {
6848 if (subst_.portions_[idx].is_param) {
6849 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
6850 } else {
6851 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
6852 ptr += subst_.portions_[idx].len;
6853 }
6854 }
6855 return ptr;
6856 }
6857};
6858
6859// CTAD deducing rule for e_subst
6860template<typename K, size_t N, typename...Args> requires (sizeof...(Args) > 0)
6861e_subst(const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
6862
6868template<typename K, typename ... Args>
6869struct e_vsubst : expr_to_std_string<e_vsubst<K, Args...>> {
6870 inline static constexpr size_t Nparams = sizeof...(Args);
6871 using symb_type = K;
6872 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6873
6874 details::portion portions_[Nparams * 3];
6875 std::vector<details::portion> more_portions_;
6876 store_t args_;
6877 str_src<K> pattern_;
6878 size_t all_len_{};
6879 unsigned actual_{};
6917 constexpr e_vsubst(str_src<K> pattern, Args&&...args)
6918 : pattern_(pattern)
6919 , args_(to_subst<K>(std::forward<Args>(args))...) {
6920
6921 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [this](unsigned from, unsigned len) {
6922 if (actual_ < std::size(portions_)) {
6923 portions_[actual_++].set_part(from, len);
6924 } else {
6925 if (actual_ == std::size(portions_)) {
6926 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6927 }
6928 more_portions_.emplace_back().set_part(from, len);
6929 }
6930 }, [this](unsigned param) {
6931 if (actual_ < std::size(portions_)) {
6932 portions_[actual_++].set_param(param);
6933 } else {
6934 if (actual_ == std::size(portions_)) {
6935 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6936 }
6937 more_portions_.emplace_back().set_param(param);
6938 }
6939 }
6940 );
6941 }
6942 constexpr size_t length() const noexcept {
6943 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6944 size_t idx = 0;
6945 size_t expr_length_[Nparams] = {};
6946 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6947 size_t l = all_len_;
6948 for (idx = 0; idx < actual_; idx++) {
6949 if (portions_[idx].is_param) {
6950 l += expr_length_[portions_[idx].start];
6951 }
6952 }
6953 for (const auto& p : more_portions_) {
6954 if (p.is_param) {
6955 l += expr_length_[p.start];
6956 }
6957 }
6958 return l;
6959 }(std::make_index_sequence<sizeof...(Args)>());
6960 }
6961 template<size_t Idx>
6962 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6963 if (idx == Idx) {
6964 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6965 }
6966 if constexpr (Idx < Nparams - 1) {
6967 return place_idx<Idx + 1>(ptr, idx);
6968 }
6969 return ptr;
6970 }
6971 constexpr K* place(K* ptr) const noexcept {
6972 for (size_t idx = 0; idx < actual_; idx++) {
6973 if (portions_[idx].is_param) {
6974 ptr = place_idx<0>(ptr, portions_[idx].start);
6975 } else {
6976 ch_traits<K>::copy(ptr, pattern_.symbols() + portions_[idx].start, portions_[idx].len);
6977 ptr += portions_[idx].len;
6978 }
6979 }
6980 for (const auto& p : more_portions_) {
6981 if (p.is_param) {
6982 ptr = place_idx<0>(ptr, p.start);
6983 } else {
6984 const K* from = pattern_.symbols() + p.start;
6985 for (size_t idx = p.len; idx--;) {
6986 *ptr++ = *from++;
6987 }
6988 //ch_traits<K>::copy(ptr, pattern_.symbols() + p.start, p.len);
6989 //ptr += p.len;
6990 }
6991 }
6992 return ptr;
6993 }
6994};
6995// CTAD deducing rule for e_vsubst
6996template<StrSourceNoLiteral T, typename...Args> requires (sizeof...(Args) > 0)
6997e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
6998
6999template<is_one_of_char_v K, bool upper>
7000struct expr_change_case_ascii : expr_to_std_string<expr_change_case_ascii<K, upper>>{
7001 using symb_type = K;
7002
7003 str_src<K> src_;
7004
7005 template<StrSource S>
7006 expr_change_case_ascii(S&& s) : src_(std::forward<S>(s)){}
7007
7008 constexpr size_t length() const noexcept {
7009 return src_.length();
7010 }
7011 constexpr K* place(K* ptr) const noexcept {
7012 const K* read = src_.str;
7013 for (size_t l = src_.len; l--;) {
7014 if constexpr (upper) {
7015 *ptr++ = makeAsciiUpper(*read++);
7016 } else {
7017 *ptr++ = makeAsciiLower(*read++);
7018 }
7019 }
7020 return ptr;
7021 }
7022};
7023
7038template<is_one_of_char_v K>
7039struct e_ascii_upper : expr_change_case_ascii<K, true> {
7040 using base = expr_change_case_ascii<K, true>;
7041 using base::base;
7042};
7043
7044template<StrSource S>
7046
7061template<is_one_of_char_v K>
7062struct e_ascii_lower : expr_change_case_ascii<K, false> {
7063 using base = expr_change_case_ascii<K, false>;
7064 using base::base;
7065};
7066
7067template<StrSource S>
7069
7074namespace str {
7075
7108template<typename K, typename A, StrExprForType<K> E>
7109std::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) {
7110 size_t expr_length = expr.length();
7111 if (!expr_length) {
7112 str.erase(from, count);
7113 return str;
7114 }
7115 size_t str_length = str.length();
7116 if (from > str_length) {
7117 from = str_length;
7118 }
7119 if (count > str_length || from + count > str_length) {
7120 count = str_length - from;
7121 }
7122 size_t new_length = str_length - count + expr_length;
7123 size_t tail_length = str_length - count - from;
7124
7125 if (new_length <= str_length) {
7126 K* data = str.data();
7127 expr.place((typename E::symb_type*)data + from);
7128 if (expr_length < count) {
7129 if (tail_length) {
7130 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7131 }
7132 str.resize(new_length);
7133 }
7134 } else {
7135 auto fill = [&](K* data, size_t) -> size_t {
7136 if (tail_length) {
7137 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7138 }
7139 expr.place((typename E::symb_type*)data + from);
7140 return new_length;
7141 };
7142 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
7143 str.resize_and_overwrite(new_length, fill);
7144 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
7145 str._Resize_and_overwrite(new_length, fill);
7146 } else {
7147 str.resize(new_length);
7148 fill(str.data(), 0);
7149 }
7150 }
7151 return str;
7152}
7153
7181template<typename K, typename A, StrExprForType<K> E>
7182std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7183 return change(str, str.length(), 0, expr);
7184}
7185
7214template<typename K, typename A, StrExprForType<K> E>
7215std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7216 return change(str, 0, 0, expr);
7217}
7218
7249template<typename K, typename A, StrExprForType<K> E>
7250std::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) {
7251 return change(str, from, 0, expr);
7252}
7253
7278template<typename K, typename A, StrExprForType<K> E>
7279std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7280 if (size_t expr_length = expr.length()) {
7281 if (expr_length <= str.length()) {
7282 K* data = str.data();
7283 expr.place((typename E::symb_type*)data);
7284 str.resize(expr_length);
7285 } else {
7286 auto fill = [&](K* data, size_t) -> size_t {
7287 expr.place((typename E::symb_type*)data);
7288 return expr_length;
7289 };
7290 if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
7291 str.resize_and_overwrite(expr_length, fill);
7292 } else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
7293 str._Resize_and_overwrite(expr_length, fill);
7294 } else {
7295 str.resize(expr_length);
7296 expr.place((typename E::symb_type*)str.data());
7297 }
7298 }
7299 } else {
7300 str.clear();
7301 }
7302 return str;
7303}
7304
7305namespace details {
7306
7307template<typename K, typename A, typename E>
7308struct replace_grow_helper {
7309 using my_type = std::basic_string<K, std::char_traits<K>, A>;
7310
7311 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)
7312 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
7313 my_type& str;
7314
7315 const str_src<K> source;
7316 const str_src<K> pattern;
7317 K* repl;
7318 const size_t replLen;
7319
7320 size_t maxCount;
7321 const size_t delta;
7322 size_t all_delta{};
7323 const E& expr;
7324
7325 K* reserve_for_copy{};
7326 size_t end_of_piece{};
7327 size_t total_length{};
7328
7329 std::optional<my_type> dst;
7330
7331 void replace(size_t offset) {
7332 size_t found[16] = {offset};
7333 maxCount--;
7334
7335 offset += pattern.len;
7336 all_delta += delta;
7337 size_t idx = 1;
7338 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
7339 found[idx] = source.find(pattern, offset);
7340 if (found[idx] == npos) {
7341 break;
7342 }
7343 offset = found[idx] + pattern.len;
7344 all_delta += delta;
7345 }
7346 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
7347 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
7348 }
7349 // Теперь делаем свои замены
7350 // Now we make our replacements
7351 if (!reserve_for_copy) {
7352 // Только начинаем
7353 // Just getting started
7354 end_of_piece = source.length();
7355 total_length = end_of_piece + all_delta;
7356 my_type* dst_str{};
7357 if (total_length <= str.capacity()) {
7358 // Строка поместится в старое место | The string will be placed in the old location.
7359 dst_str = &str;
7360 } else {
7361 // Будем создавать в другом буфере | We will create in another buffer.
7362 dst_str = &dst.emplace();
7363 }
7364 auto fill = [this](K* p, size_t) -> size_t {
7365 reserve_for_copy = p;
7366 return total_length;
7367 };
7368 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
7369 dst_str->_Resize_and_overwrite(total_length, fill);
7370 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
7371 dst_str->resize_and_overwrite(total_length, fill);
7372 } else {
7373 dst_str->resize(total_length);
7374 reserve_for_copy = dst_str->data();
7375 }
7376 }
7377 const K* src_start = str.c_str();
7378 while(idx-- > 0) {
7379 size_t pos = found[idx] + pattern.len;
7380 size_t lenOfPiece = end_of_piece - pos;
7381 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
7382 if constexpr (std::is_same_v<E, int>) {
7383 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7384 } else {
7385 if (!repl) {
7386 repl = reserve_for_copy + pos + all_delta - replLen;
7387 expr.place(repl);
7388 } else {
7389 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7390 }
7391 }
7392 all_delta -= delta;
7393 end_of_piece = found[idx];
7394 }
7395 if (!all_delta && reserve_for_copy != src_start) {
7396 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
7397 str = std::move(*dst);
7398 }
7399 }
7400};
7401
7402} // namespace details
7403
7438template<typename K, typename A, StrExprForType<K> E, typename T>
7439requires (std::is_constructible_v<str_src<K>, T>)
7440std::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) {
7441 if (!max_count) {
7442 return str;
7443 }
7444 str_src<K> src = str;
7445 str_src<K> spattern{std::forward<T>(pattern)};
7446 offset = src.find(pattern, offset);
7447 if (offset == npos) {
7448 return str;
7449 }
7450 size_t replLen = repl.length();
7451 K* replStart{};
7452 if (spattern.len == replLen) {
7453 // Заменяем inplace на подстроку такой же длины
7454 // Replace inplace with a substring of the same length
7455 K* ptr = str.data();
7456 replStart = ptr + offset;
7457 repl.place(replStart);
7458
7459 while (--max_count) {
7460 offset = src.find(spattern, offset + replLen);
7461 if (offset == npos)
7462 break;
7463 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7464 }
7465 } else if (spattern.len > replLen) {
7466 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7467 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7468 K* ptr = str.data();
7469 replStart = ptr + offset;
7470 repl.place(replStart);
7471 size_t posWrite = offset + replLen;
7472 offset += spattern.len;
7473
7474 while (--max_count) {
7475 size_t idx = src.find(spattern, offset);
7476 if (idx == npos)
7477 break;
7478 size_t lenOfPiece = idx - offset;
7479 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7480 posWrite += lenOfPiece;
7481 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7482 posWrite += replLen;
7483 offset = idx + spattern.len;
7484 }
7485 size_t tailLen = src.len - offset;
7486 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7487 str.resize(posWrite + tailLen);
7488 } else {
7489 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7490 }
7491 return str;
7492}
7493
7514template<typename K, typename A>
7515std::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) {
7516 if (!max_count) {
7517 return str;
7518 }
7519 str_src<K> src = str;
7520 offset = src.find(pattern, offset);
7521 if (offset == npos) {
7522 return str;
7523 }
7524 if (pattern.len == repl.len) {
7525 // Заменяем inplace на подстроку такой же длины
7526 // Replace inplace with a substring of the same length
7527 K* ptr = str.data();
7528 while (max_count--) {
7529 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7530 offset = src.find(pattern, offset + repl.len);
7531 if (offset == npos)
7532 break;
7533 }
7534 } else if (pattern.len > repl.len) {
7535 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7536 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7537 K* ptr = str.data();
7538 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7539 size_t posWrite = offset + repl.len;
7540 offset += pattern.len;
7541
7542 while (--max_count) {
7543 size_t idx = src.find(pattern, offset);
7544 if (idx == npos)
7545 break;
7546 size_t lenOfPiece = idx - offset;
7547 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7548 posWrite += lenOfPiece;
7549 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7550 posWrite += repl.len;
7551 offset = idx + pattern.len;
7552 }
7553 size_t tailLen = src.len - offset;
7554 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7555 str.resize(posWrite + tailLen);
7556 } else {
7557 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7558 }
7559 return str;
7560}
7561
7562template<typename K, typename A, typename T, typename M>
7563requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7564std::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) {
7565 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7566}
7567
7580template<typename K, typename A>
7581std::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) {
7582 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7583 K s = *ptr;
7584 if (isAsciiLower(s)) {
7585 *ptr = s & ~0x20;
7586 }
7587 }
7588 return str;
7589}
7590
7603template<typename K, typename A>
7604std::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) {
7605 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7606 K s = *ptr;
7607 if (isAsciiUpper(s)) {
7608 *ptr = s | 0x20;
7609 }
7610 }
7611 return str;
7612}
7613
7614} // namespace str
7615
7616} // namespace simstr
7617
7622namespace std {
7642template<simstr::StdStrSource T>
7644 return {str};
7645}
7646
7674template<typename K, typename A, simstr::StrExprForType<K> E>
7675std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7676 return simstr::str::change(str, str.length(), 0, expr);
7677}
7678
7706template<typename K, typename A, simstr::StrExprForType<K> E>
7707std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7708 return simstr::str::change(str, 0, 0, expr);
7709}
7710
7711} // namespace std
Класс для последовательного получения подстрок по заданному разделителю.
Определения strexpr.h:3230
constexpr bool is_done() const
Узнать, не закончились ли подстроки.
Определения strexpr.h:3241
constexpr str_t next()
Получить следующую подстроку.
Определения strexpr.h:3250
constexpr R trimmed_right_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:5040
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Найти последнее вхождения символа в этой строке.
Определения strexpr.h:4062
constexpr int compare_ia(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3744
constexpr int compare(str_piece o) const
Сравнение строк посимвольно.
Определения strexpr.h:3629
constexpr size_t find(K s, size_t offset=0) const noexcept
Найти символ в этой строке.
Определения strexpr.h:3966
constexpr std::optional< R > strip_suffix(str_piece suffix) const
Получить строку без суффикса, если она заканчивается им.
Определения strexpr.h:4753
constexpr bool ends_with(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой.
Определения strexpr.h:4590
constexpr bool operator==(const base &other) const noexcept
Оператор сравнение строк на равенство.
Определения strexpr.h:3680
constexpr std::basic_string_view< D > to_sv() const noexcept
Конвертировать в std::basic_string_view.
Определения strexpr.h:3384
constexpr T slice_to(S start, E end) const noexcept
Получить результат slice и поместить его в объект заданного типа.
Определения strexpr.h:4176
constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept
Проверить, начинается ли строка с указанной подстроки без учета регистра ASCII, за которой следует не...
Определения strexpr.h:4536
constexpr R trimmed_right() const
Получить строку с удалением пробельных символов справа.
Определения strexpr.h:4836
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count=0) const
Возвращает строку, в которой удалено начало строки, если она начинается с указанного префикса без учё...
Определения strexpr.h:5079
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти конец первого вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3862
constexpr bool starts_with(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки.
Определения strexpr.h:4472
constexpr bool equal(str_piece other) const noexcept
Сравнение строк на равенство.
Определения strexpr.h:3669
void copy_to(K *buffer, size_t bufSize)
Копировать строку в указанный буфер.
Определения strexpr.h:3362
constexpr str_piece until(str_piece pattern, size_t offset=0) const noexcept
Получить подстроку str_src от начала и до первого найденного вхождения указанной подстроки.
Определения strexpr.h:3591
constexpr R trimmed(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева и справа.
Определения strexpr.h:4958
constexpr T as_int() const noexcept
Преобразовать строку в число заданного типа.
Определения strexpr.h:4236
constexpr str_piece prefix(size_t count) const noexcept
Обертка вокруг slice(0, count).
Определения strexpr.h:3535
constexpr my_type substring(S start, E end) const noexcept
Обёртка вокруг slice_to<my_type>
Определения strexpr.h:4190
constexpr bool less_ia(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3767
constexpr T split(str_piece delimiter, size_t offset=0) const
Разделить строку на подстроки по заданному разделителю.
Определения strexpr.h:4457
constexpr R trimmed_right_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4941
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Получить часть строки как "кусок строки".
Определения strexpr.h:3556
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Получить подстроку. Работает аналогично operator(), только результат выдает того же типа,...
Определения strexpr.h:4164
R upperred_only_ascii() const
Получить копию строки в верхнем регистре ASCII символов.
Определения strexpr.h:4667
constexpr str_piece to_str() const noexcept
Преобразовать себя в "кусок строки", включающий всю строку.
Определения strexpr.h:3432
constexpr bool starts_with_and_ws(str_piece prefix) const noexcept
Проверить, начинается ли строка с указанной подстроки, за которой следует ней пробельный ASCII символ...
Определения strexpr.h:4481
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Получить подстроку str_src с позиции от from до позиции to (не включая её).
Определения strexpr.h:3578
constexpr R trimmed_left_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:5022
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Получить копию строки с заменёнными вхождениями подстрок.
Определения strexpr.h:4699
constexpr int strcmp(const K *text) const
Сравнение с C-строкой посимвольно.
Определения strexpr.h:3640
constexpr R trimmed_left() const
Получить строку с удалением пробельных символов слева.
Определения strexpr.h:4824
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра ASCII символов.
Определения strexpr.h:4616
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа не из заданного набора символов.
Определения strexpr.h:4109
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов.
Определения strexpr.h:4081
constexpr R trimmed_right(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, справа.
Определения strexpr.h:4986
constexpr R trimmed_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4903
constexpr str_piece suffix(size_t count) const noexcept
Обертка вокруг slice(from_end(count), to_end).
Определения strexpr.h:3542
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3925
constexpr R trimmed_prefix(str_piece prefix, size_t max_count=0) const
Возвращает строку, в которой удалено начало строки, если она начинается с указанного префикса.
Определения strexpr.h:5056
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра ASCII символов.
Определения strexpr.h:4527
constexpr str_piece slice(S start, E end) const noexcept
Получить часть строки как "str_src".
Определения strexpr.h:3501
constexpr R trimmed_left_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4922
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке.
Определения strexpr.h:3898
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов.
Определения strexpr.h:4122
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Найти символ в этой строке или конец строки.
Определения strexpr.h:3985
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа не из заданного набора символов.
Определения strexpr.h:4150
std::optional< double > to_double() const noexcept
Преобразовать строку в double.
Определения strexpr.h:4281
constexpr void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Вызвать функтор для всех найденных вхождений подстроки в этой строке.
Определения strexpr.h:4020
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Найти конец вхождения подстроки в этой строке.
Определения strexpr.h:3834
std::optional< double > to_double_hex() const noexcept
Преобразовать строку в 16ричной записи в double. Пока работает только для char.
Определения strexpr.h:4316
constexpr std::optional< R > strip_prefix(str_piece prefix) const
Получить строку без префикса, если она начинается с него.
Определения strexpr.h:4715
constexpr bool operator!() const noexcept
Проверка на пустоту.
Определения strexpr.h:3598
constexpr R trimmed() const
Получить строку с удалением пробельных символов слева и справа.
Определения strexpr.h:4812
constexpr size_t size() const
Размер строки в символах.
Определения strexpr.h:3373
constexpr To find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Найти все вхождения подстроки в этой строке.
Определения strexpr.h:4043
constexpr my_type str_mid(size_t from, size_t len=-1) const
Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
Определения strexpr.h:4204
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке.
Определения strexpr.h:3911
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов.
Определения strexpr.h:4094
constexpr bool is_ascii() const noexcept
Содержит ли строка только ASCII символы.
Определения strexpr.h:4623
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке.
Определения strexpr.h:3798
constexpr SplitterBase< K, str_piece > splitter(str_piece delimiter) const
Получить объект Splitter по заданному разделителю, который позволяет последовательно получать подстро...
Определения strexpr.h:5139
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов.
Определения strexpr.h:4135
constexpr R trimmed_left(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева.
Определения strexpr.h:4866
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3848
constexpr R trimmed_left(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева.
Определения strexpr.h:4972
constexpr bool operator==(T &&other) const noexcept
Оператор сравнения строки и строкового литерала на равенство.
Определения strexpr.h:3699
constexpr auto operator<=>(const base &other) const noexcept
Оператор сравнения строк.
Определения strexpr.h:3689
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Содержит ли строка указанную подстроку.
Определения strexpr.h:3953
constexpr R trimmed_suffix_ia(str_piece suffix) const
Возвращает строку, в которой удалён конец строки, если она заканчивается указанным суффиксом без учёт...
Определения strexpr.h:5122
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Получить часть строки как "str_src".
Определения strexpr.h:3459
constexpr K at(ptrdiff_t idx) const
Получить символ на заданной позиции .
Определения strexpr.h:3611
R lowered_only_ascii() const
Получить копию строки в нижнем регистре ASCII символов.
Определения strexpr.h:4679
constexpr R trimmed_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:5004
constexpr auto operator<=>(T &&other) const noexcept
Оператор сравнения строки и строкового литерала.
Определения strexpr.h:3709
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Конвертировать в std::basic_string.
Определения strexpr.h:3404
constexpr R trimmed_right(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, справа.
Определения strexpr.h:4881
constexpr T splitf(str_piece delimiter, const Op &beforeFunc, size_t offset=0) const
Разделить строку на части по заданному разделителю, с возможным применением функтора к каждой подстро...
Определения strexpr.h:4441
constexpr R trimmed(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева и справа.
Определения strexpr.h:4851
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3939
constexpr bool prefix_in(str_piece text) const noexcept
Является ли эта строка началом другой строки.
Определения strexpr.h:4575
constexpr std::optional< R > strip_prefix_ia(str_piece prefix) const
Получить строку без префикса, если она начинается с него без учёта регистра ASCII символов.
Определения strexpr.h:4734
constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Проверить, начинается ли строка с указанной подстроки без учета регистра ASCII, за которой следует од...
Определения strexpr.h:4549
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const
Найти начало первого вхождения подстроки в этой строке или выкинуть исключение.
Определения strexpr.h:3818
constexpr std::optional< R > strip_suffix_ia(str_piece suffix) const
Получить строку без суффикса, если она заканчивается им без учёта регистра ASCII символов.
Определения strexpr.h:4772
constexpr R trimmed_suffix(str_piece suffix) const
Возвращает строку, в которой удалён конец строки, если она заканчивается указанным суффиксом.
Определения strexpr.h:5102
constexpr K * place(K *ptr) const noexcept
Копировать строку в указанный буфер.
Определения strexpr.h:3347
constexpr convert_result< T > to_int() const noexcept
Преобразовать строку в число заданного типа.
Определения strexpr.h:4269
constexpr bool equal_ia(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3756
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения strexpr.h:4344
constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Проверить, начинается ли строка с указанной подстроки, за которой следует один из указанных символов.
Определения strexpr.h:4494
Концепт строкового выражения, совместимого с заданным типом символов.
Определения strexpr.h:525
Концепт "Строковых выражений".
Определения strexpr.h:507
Базовая концепция строкового объекта.
Определения strexpr.h:311
Проверка, являются ли два типа совместимыми строковыми типами.
Определения strexpr.h:172
constexpr expr_if< A > e_if(bool c, const A &a)
Создание условного строкового выражения expr_if.
Определения strexpr.h:1540
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Унарный оператор+ для преобразования стандартных строк в строковые выражения.
Определения strexpr.h:7643
constexpr expr_spaces< uws, N > e_spcw()
Генерирует строку из N wchar_t пробелов.
Определения strexpr.h:1104
expr_fill< K, A, true > e_fill_left(const A &a, size_t width, K symbol=K(' '))
Создает выражение, которое дополняет указанное строковое выражение до заданной длины заданным символо...
Определения strexpr.h:2695
constexpr empty_expr< uws > eew
Пустое строковое выражение типа wchar_t.
Определения strexpr.h:862
constexpr expr_spaces< u8s, N > e_spca()
Генерирует строку из N char пробелов.
Определения strexpr.h:1086
constexpr empty_expr< u32s > eeuu
Пустое строковое выражение типа char32_t.
Определения strexpr.h:874
constexpr expr_num< K, T > e_num(T t)
Преобразование целого числа в строковое выражение.
Определения strexpr.h:1716
HexFlags
Флаги для функции e_hex.
Определения strexpr.h:2523
constexpr auto e_hex(T v)
Создает объект, который может преобразовываться в строковое выражение, генерирующее 16ричное представ...
Определения strexpr.h:2580
constexpr strexprjoin< A, B > operator+(const A &a, const B &b)
Оператор сложения двух произвольных строковых выражения для одинакового типа символов.
Определения strexpr.h:719
constexpr expr_repeat_lit< K, M - 1 > e_repeat(T &&s, size_t l)
Генерирует строку из l строковых констант s типа K.
Определения strexpr.h:1214
constexpr expr_literal< typename const_lit< T >::symb_type, static_cast< size_t >(N - 1)> e_t(T &&s)
Преобразует строковый литерал в строковое выражение.
Определения strexpr.h:991
expr_fill< K, A, false > e_fill_right(const A &a, size_t width, K symbol=K(' '))
Создает выражение, которое дополняет указанное строковое выражение до заданной длины заданным символо...
Определения strexpr.h:2721
constexpr expr_choice< A, B > e_choice(bool c, const A &a, const B &b)
Создание условного строкового выражения expr_choice.
Определения strexpr.h:1462
constexpr expr_char< K > e_char(K s)
Генерирует строку из 1 заданного символа.
Определения strexpr.h:918
constexpr empty_expr< u8s > eea
Пустое строковое выражение типа char.
Определения strexpr.h:850
constexpr expr_pad< K > e_c(size_t l, K s)
Генерирует строку из l символов s типа K.
Определения strexpr.h:1149
constexpr empty_expr< u16s > eeu
Пустое строковое выражение типа char16_t.
Определения strexpr.h:868
auto e_repl_const_symbols(A &&src, Repl &&... other)
Возвращает строковое выражение, генерирующее строку, в которой заданные символы заменены на заданные ...
Определения strexpr.h:6257
constexpr auto e_int(T v)
Создает объект, который преобразовывается в строковое выражение, генерирующее строковое представление...
Определения strexpr.h:2379
constexpr empty_expr< u8s > eeb
Пустое строковое выражение типа char8_t.
Определения strexpr.h:856
constexpr auto e_repl(A &&w, T &&p, X &&r)
Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
Определения strexpr.h:5764
constexpr auto e_join(const T &s, L &&d)
Получить строковое выражение, конкатенирующее строки в контейнере в одну строку с заданным разделител...
Определения strexpr.h:2799
@ Short
without leading zeroes
Определения strexpr.h:2524
Небольшое пространство для методов работы со стандартными строками.
Определения 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)
Добавить к стандартной строке строковое выражение.
Определения strexpr.h:7182
std::basic_string< K, std::char_traits< K >, A > & prepend(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Вставить строковое выражение в начало стандартной строки.
Определения strexpr.h:7215
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)
Изменить строчные ASCII символы строки (a-z) на прописные.
Определения strexpr.h:7581
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)
Изменить часть стандартной строки на заданное строковое выражение.
Определения strexpr.h:7109
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)
Функция поиска подстрок в стандартной строке и замены найденных вхождений на значение строкового выра...
Определения strexpr.h:7440
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)
Изменить прописные ASCII символы строки (A-Z) на строчные.
Определения strexpr.h:7604
std::basic_string< K, std::char_traits< K >, A > & overwrite(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Переписать всю строку заданным строковым выражением, при необходимости изменив размер строки.
Определения strexpr.h:7279
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)
Вставить строковое выражение в указанную позицию стандартной строки.
Определения strexpr.h:7250
Пространство имён для объектов библиотеки
Определения sstring.cpp:12
IntConvertResult
Перечисление с возможными результатами преобразования строки в целое число
Определения strexpr.h:2866
@ Overflow
Переполнение, число не помещается в заданный тип
Определения strexpr.h:2869
@ Success
Успешно
Определения strexpr.h:2867
@ NotNumber
Вообще не число
Определения strexpr.h:2870
@ BadSymbolAtTail
Число закончилось не числовым символом
Определения strexpr.h:2868
Некоторые методы для работы с стандартными строками.
Определения strexpr.h:7622
Генерирует строку на основе исходной, заменяя все ASCII прописные буквы (A-Z) на строчные.
Определения strexpr.h:7062
Генерирует строку на основе исходной, заменяя все ASCII строчные буквы (a-z) на прописные.
Определения strexpr.h:7039
constexpr e_concat(G &&glue, Arg &&arg, Args &&...args)
Создание строкового выражения, объединяющего указанные строковые выражения, с использованием заданног...
Определения strexpr.h:6596
constexpr e_subst(const details::subst_params< K, PtLen, NParams > &subst, Args &&...args)
Создает строковое выражение, которое подставляет в заданные места в строковом литерале - образце знач...
Определения strexpr.h:6818
constexpr e_vsubst(str_src< K > pattern, Args &&...args)
Создает строковое выражение, которое подставляет в заданные места в строке-образце,...
Определения strexpr.h:6917
"Пустое" строковое выражение.
Определения strexpr.h:835
Строковое выражение условного выбора.
Определения strexpr.h:1341
Строковое выражение условного выбора.
Определения strexpr.h:1407
Строковое выражение условного выбора.
Определения strexpr.h:1250
Строковое выражение условного выбора.
Определения strexpr.h:1279
Тип строкового выражения, возвращающего N заданных символов.
Определения strexpr.h:1120
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Конструктор выражения.
Определения strexpr.h:6341
Строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки на строковое выр...
Определения strexpr.h:5882
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Конструктор.
Определения strexpr.h:5901
Строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки на другую строку...
Определения strexpr.h:5776
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Конструктор.
Определения strexpr.h:5794
Тип строкового выражения, возвращающего N заданных символов.
Определения strexpr.h:1060
Тип для использования std::basic_string и std::basic_string_view как источников в строковых выражения...
Определения strexpr.h:1565
Базовый класс для преобразования строковых выражений в стандартные строки
Определения strexpr.h:659
Класс, заявляющий, что ссылается на нуль-терминированную строку.
Определения strexpr.h:5310
constexpr my_type to_nts(size_t from)
Получить нуль-терминированную строку, сдвинув начало на заданное количество символов.
Определения strexpr.h:5373
constexpr str_src_nt(T &&v) noexcept
Конструктор из строкового литерала.
Определения strexpr.h:5344
constexpr str_src_nt(T &&p) noexcept
Явный конструктор из С-строки.
Определения strexpr.h:5335
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения strexpr.h:5362
constexpr str_src_nt(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения strexpr.h:5350
Простейший класс иммутабельной не владеющей строки.
Определения strexpr.h:5174
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения strexpr.h:5203
constexpr bool is_empty() const noexcept
Проверить, не пуста ли строка.
Определения strexpr.h:5228
constexpr size_t length() const noexcept
Получить длину строки.
Определения strexpr.h:5214
constexpr K operator[](size_t idx) const
Получить символ из указанной позиции. Проверка границ не выполняется.
Определения strexpr.h:5257
constexpr my_type & remove_prefix(size_t delta)
Сдвигает начало строки на заданное количество символов.
Определения strexpr.h:5268
constexpr str_src(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения strexpr.h:5193
constexpr bool is_same(str_src< K > other) const noexcept
Проверить, не указывают ли два объекта на одну строку.
Определения strexpr.h:5237
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Конструктор из std::basic_string_view.
Определения strexpr.h:5208
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки.
Определения strexpr.h:5221
constexpr my_type & remove_suffix(size_t delta)
Укорачивает строку на заданное количество символов.
Определения strexpr.h:5281
constexpr bool is_part_of(str_src< K > other) const noexcept
Проверить, не является ли строка частью другой строки.
Определения strexpr.h:5246
constexpr str_src(T &&v) noexcept
Конструктор из строкового литерала.
Определения strexpr.h:5187
Конкатенация ссылки на строковое выражение и значения строкового выражения.
Определения strexpr.h:744
Шаблонный класс для конкатенации двух строковых выражений в одно с помощью operator +
Определения strexpr.h:682