simstr 1.9.1
Yet another strings library
 
Loading...
Searching...
No Matches
strexpr.h
1/*
2 * ver. 1.9.1
3 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
4 * База для строковых конкатенаций через выражения времени компиляции
5 * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6 * Base for string concatenations via compile-time expressions
7 */
8#pragma once
9#include <cstddef>
10#include <cstdint>
11#include <climits>
12#include <limits>
13#include <string>
14#include <string_view>
15#include <concepts>
16#include <vector>
17#include <optional>
18#include <charconv>
19
20#if defined __has_builtin
21# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
22# define HAS_BUILTIN_OVERFLOW
23# endif
24#endif
25
26#ifdef _MSC_VER
27#define _no_unique_address msvc::no_unique_address
28#define decl_empty_bases __declspec(empty_bases)
29#else
30#define _no_unique_address no_unique_address
31#define decl_empty_bases
32#endif
33
34#ifdef _MSC_VER
35/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
36Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
37Пишут, что баг исправлен, но видимо не до конца.
38Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
39/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
40Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
41They write that the bug has been fixed, but apparently not completely.
42Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
43#define SS_CONSTEVAL constexpr
44#else
45#define SS_CONSTEVAL consteval
46#endif
47
52namespace simstr {
53
54// Выводим типы для 16 и 32 битных символов в зависимости от размера wchar_t
55// Infer types for 16 and 32 bit characters depending on the size of wchar_t
56inline constexpr bool wchar_is_u16 = sizeof(wchar_t) == sizeof(char16_t);
57
58using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
59
60inline wchar_type* to_w(wchar_t* p) {
61 return (reinterpret_cast<wchar_type*>(p));
62}
63
64inline const wchar_type* to_w(const wchar_t* p) {
65 return (reinterpret_cast<const wchar_type*>(p));
66}
67
68inline wchar_t* from_w(wchar_type* p) {
69 return (reinterpret_cast<wchar_t*>(p));
70}
71
72inline const wchar_t* from_w(const wchar_type* p) {
73 return (reinterpret_cast<const wchar_t*>(p));
74}
75
76using u8s = char;
77using ubs = char8_t;
78using uws = wchar_t;
79using u16s = char16_t;
80using u32s = char32_t;
81
82using uu8s = std::make_unsigned<u8s>::type;
83
84template<typename T, typename K = void, typename... Types>
85struct is_one_of_type {
86 static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
87};
88template<typename T>
89struct is_one_of_type<T, void> : std::false_type {};
90
91template<typename K>
92concept is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
93
94template<typename K>
95concept is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
96
97template<is_one_of_std_char_v From>
98auto to_one_of_std_char(From* from) {
99 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
100 return from;
101 } else if constexpr (std::is_same_v<From, ubs>) {
102 return reinterpret_cast<u8s*>(from);
103 } else {
104 return from_w(from);
105 }
106}
107
108template<is_one_of_std_char_v From>
109auto to_one_of_std_char(const From* from) {
110 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
111 return from;
112 } else if constexpr (std::is_same_v<From, ubs>) {
113 return reinterpret_cast<const u8s*>(from);
114 } else {
115 return from_w(from);
116 }
117}
118
119template<typename K>
120struct to_std_char_type : std::type_identity<K>{};
121
122template<>
123struct to_std_char_type<char8_t>{
124 using type = char;
125};
126
127template<>
128struct to_std_char_type<char16_t>{
129 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
130};
131
132template<>
133struct to_std_char_type<char32_t>{
134 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
135};
136
137template<typename K>
138using to_std_char_t = typename to_std_char_type<K>::type;
139
140template<typename K>
141struct to_base_char_type : std::type_identity<K>{};
142
143template<>
144struct to_base_char_type<char8_t>{
145 using type = char;
146};
147
148template<>
149struct to_base_char_type<wchar_t>{
150 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), char16_t, char32_t>;
151};
152
153template<typename K>
154using to_base_char_t = typename to_base_char_type<K>::type;
155
171template<typename K1, typename K2>
172concept is_equal_str_type_v = sizeof(K1) == sizeof(K2) && is_one_of_char_v<K1> && is_one_of_char_v<K2>;
173
174/*
175Вспомогательные шаблоны для определения строковых литералов.
176Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
177Если пишем
178template<size_t N>
179void func(const char(&lit)[N]);
180то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
181
182// Выделили место под символы
183char buf[100];
184// Как то наполнили буфер, допустим, до половины.
185
186stringa text = buf;
187Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
188для строкового литерала, в text запишется просто указатель на buf и длина 100.
189
190Поэтому такие параметры объявляем как
191template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
192void func(T&& lit);
193
194Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
195и выражение с параметром char[100] - не скомпилируется.
196
197Helper templates for defining string literals.
198They are used to limit types in function parameters strictly as `const K(&)[N]`
199If we write
200template<size_t N>
201void func(const char(&lit)[N]);
202then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
203
204// Allocate space for symbols
205char buf[100];
206// Somehow the buffer was filled, say, to half.
207
208stringa text = buf;
209Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
210for a string literal, text will simply contain a pointer to buf and length 100.
211
212Therefore, we declare such parameters as
213template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
214void func(T&& lit);
215
216Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
217and an expression with the char[100] parameter will not compile.
218*/
219
220template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
221// Для правильных типов параметров есть определение, в виде специализации шаблона
222// There is a definition for the correct parameter types, in the form of a template specialization
223template<is_one_of_char_v T, size_t N>
224struct const_lit<const T(&)[N]> {
225 using symb_type = T;
226 constexpr static size_t Count = N;
227};
228
229template<typename T>
230concept is_const_lit_v = requires {
231 typename const_lit<T>::symb_type;
232};
233
234// Тут ещё дополнительно ограничиваем тип литерала
235// Here we further restrict the type of the literal
236template<typename K, typename T> struct const_lit_for;
237
238template<typename K, is_equal_str_type_v<K> P, size_t N>
239struct const_lit_for<K, const P(&)[N]> {
240 constexpr static size_t Count = N;
241};
242
243template<typename K, size_t N>
244class const_lit_to_array {
245
246 template<size_t Idx>
247 constexpr size_t find(K s) const {
248 if constexpr (Idx < N) {
249 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
250 }
251 return -1;
252 }
253
254 template<size_t Idx>
255 constexpr bool exist(K s) const {
256 if constexpr (Idx < N) {
257 return s == symbols_[Idx] || exist<Idx + 1>(s);
258 }
259 return false;
260 }
261public:
262 const K (&symbols_)[N + 1];
263
264 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
265 constexpr const_lit_to_array(T&& s)
266 : symbols_((const K(&)[M])s) {}
267
268 constexpr bool contain(K s) const {
269 return exist<0>(s);
270 }
271 constexpr size_t index_of(K s) const {
272 return find<0>(s);
273 }
274};
275
276template<typename A>
277concept StrTypeCommon = requires(const A& a) {
278 typename std::remove_cvref_t<A>::symb_type;
279 { a.is_empty() } -> std::same_as<bool>;
280 { a.length() } -> std::convertible_to<size_t>;
281 { a.symbols() };
282};
283
284template<typename A>
285concept HasSymbType = requires {
286 typename std::remove_cvref_t<A>::symb_type;
287};
288
310template<typename A, typename K>
311concept StrType = StrTypeCommon<A> && requires(const A& a) {
312 { a.symbols() } -> std::same_as<const K*>;
313} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
314
315template<typename T> struct is_std_string_source : std::false_type{};
316
317template<typename K, typename A>
318struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
319
320template<typename K>
321struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
322
323template<typename T>
324concept is_std_string_source_v = is_std_string_source<T>::value;
325
326template<typename T>
327concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
328
329template<typename T, typename K>
330concept StdStrSourceForType = StdStrSource<T> && is_equal_str_type_v<K, typename T::value_type>;
331
332template<typename T>
333concept StrSource = StdStrSource<T> || is_const_lit_v<T> || StrTypeCommon<T>;
334
335template<typename T>
336concept StrSourceNoLiteral = StdStrSource<T> || StrTypeCommon<T>;
337
338template<typename T> struct is_std_string : std::false_type{};
339
340template<typename K, typename A>
341struct is_std_string<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
342
343template<typename T>
344concept is_std_string_v = is_std_string<T>::value;
345template<typename T, typename K>
346concept StdStringForType = is_std_string_v<T> && is_equal_str_type_v<K, typename T::value_type>;
347
348template<typename T>
349struct symb_type_from_src {
350 using type = void;
351
352};
353
354template<typename K, size_t N>
355struct symb_type_from_src<const K(&)[N]> {
356 using type = K;
357};
358
359template<StdStrSource T>
360struct symb_type_from_src<T> {
361 using type = typename std::remove_cvref_t<T>::value_type;
362};
363
364template<HasSymbType T>
365struct symb_type_from_src<T> {
366 using type = typename std::remove_cvref_t<T>::symb_type;
367};
368
369template<typename T>
370using symb_type_from_src_t = symb_type_from_src<T>::type;
371
372
498
506template<typename A>
507concept StrExpr = requires(const A& a) {
508 typename std::remove_cvref_t<A>::symb_type;
509 { a.length() } -> std::convertible_to<size_t>;
510 { a.place(std::declval<typename std::remove_cvref_t<A>::symb_type*>()) } -> std::same_as<typename std::remove_cvref_t<A>::symb_type*>;
511};
512
524template<typename A, typename K>
526
527template<typename K, typename T>
528struct convert_to_strexpr;
529
530template<typename A, typename K>
531concept strexpr_from = requires(const A& a) {
532 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StrExprForType<K>;
533};
534
535template<typename A, typename K>
536concept strexpr_std = requires(const A& a) {
537 {convert_to_strexpr<K, std::remove_cvref_t<A>>::convert(a)} -> StdStringForType<K>;
538};
539
540template<typename A, typename K>
542
543template<typename A, typename K>
545
546template<typename A, typename K>
547concept to_strexpr_meth = requires(const A& a) {
548 {a.template to_strexpr<K>()} -> StrExprForType<K>;
549};
550
551template<typename A, typename K>
552concept to_strexpr_std = requires(const A& a) {
553 {a.template to_strexpr<K>()} -> StdStringForType<K>;
554};
555
556template<typename A, typename K>
557concept convertible_to_strexpr = strexpr_for<A, K> || strexpr_from<A, K> || strexpr_std<A, K> || to_strexpr_type<A, K> || to_strexpr_meth<A, K> || to_strexpr_std<A, K>;
558
559template<typename K, strexpr_from<K> T>
560constexpr auto to_strexpr(T&& t) {
561 return convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t));
562}
563
564template<typename K, strexpr_for<K> T>
565constexpr typename convert_to_strexpr<K, std::remove_cvref_t<T>>::type to_strexpr(T&& t) {
566 return {std::forward<T>(t)};
567}
568
569template<typename K, to_strexpr_type<K> T>
570constexpr typename std::remove_cvref_t<T>::strexpr to_strexpr(T&& t) {
571 return {std::forward<T>(t)};
572}
573
574template<typename K, to_strexpr_meth<K> T>
575constexpr auto to_strexpr(T&& t) {
576 return t.template to_strexpr<K>();
577}
578
579template<typename K, StdStrSource T> requires is_equal_str_type_v<K, typename T::value_type>
580struct expr_stdstr_c {
581 using symb_type = K;
582 T t_;
583
584 expr_stdstr_c(T t) : t_(std::move(t)){}
585
586 constexpr size_t length() const noexcept {
587 return t_.length();
588 }
589 constexpr symb_type* place(symb_type* p) const noexcept {
590 size_t s = t_.size();
591 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
592 return p + s;
593 }
594};
595
596template<typename K, strexpr_std<K> T>
597constexpr auto to_strexpr(T&& t) {
598 using type = decltype(convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t)));
599 return expr_stdstr_c<K, type>{convert_to_strexpr<K, std::remove_cvref_t<T>>::convert(std::forward<T>(t))};
600}
601
602template<typename K, to_strexpr_std<K> T>
603constexpr auto to_strexpr(T&& t) {
604 using type = decltype(t.template to_strexpr<K>());
605 return expr_stdstr_c<K, type>{t.template to_strexpr<K>()};
606}
607
608template<typename K, typename T>
609using convert_to_strexpr_t = decltype(to_strexpr<K>(std::declval<T>()));
610
611/*
612* Шаблонные классы для создания строковых выражений из нескольких источников.
613* Благодаря компиляторно-шаблонной "магии" позволяют максимально эффективно
614* получать результирующую строку - сначала вычисляется длина результирующей строки,
615* потом один раз выделяется память для результата, после символы помещаются в
616* выделенную память.
617* Для конкатенация двух объектов строковых выражений в один
618
619* Template classes for creating string expressions from multiple sources.
620* Thanks to compiler-template "magic" they allow you to maximize efficiency
621* get the resulting string - first the length of the resulting string is calculated,
622* then memory is allocated once for the result, after which the characters are placed in
623* allocated memory.
624* For concatenating two string expression objects into one.
625*/
626
627template<typename K, typename Allocator, StrExpr A>
628constexpr std::basic_string<K, std::char_traits<K>, Allocator> to_std_string(const A& expr) {
629 std::basic_string<K, std::char_traits<K>, Allocator> res;
630 if (size_t l = expr.length()) {
631 auto fill = [&](K* ptr, size_t size) -> size_t {
632 expr.place((typename A::symb_type*)ptr);
633 return l;
634 };
635 if constexpr (requires { res.resize_and_overwrite(l, fill); }) {
636 res.resize_and_overwrite(l, fill);
637 } else if constexpr (requires{ res._Resize_and_overwrite(l, fill); }) {
638 // Work in MSVC std lib before C++23
639 res._Resize_and_overwrite(l, fill);
640 } else {
641 res.resize(l); // bad, fill by 0 first.
642 expr.place((typename A::symb_type*)res.data());
643 }
644 }
645 return res;
646}
647
658template<typename Impl>
660 template<is_equal_str_type_v<typename Impl::symb_type> P, typename Allocator>
661 constexpr operator std::basic_string<P, std::char_traits<P>, Allocator>() const {
662 return to_std_string<P, Allocator>(*static_cast<const Impl*>(this));
663 }
664};
665
681template<StrExpr A, StrExprForType<typename A::symb_type> B>
682struct strexprjoin : expr_to_std_string<strexprjoin<A, B>>{
683 using symb_type = typename A::symb_type;
684 const A& a;
685 const B& b;
686 constexpr strexprjoin(const A& a_, const B& b_) : a(a_), b(b_){}
687 constexpr size_t length() const noexcept {
688 return a.length() + b.length();
689 }
690 constexpr symb_type* place(symb_type* p) const noexcept {
691 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
692 }
693};
694
718template<StrExpr A, StrExprForType<typename A::symb_type> B>
719constexpr strexprjoin<A, B> operator+(const A& a, const B& b) {
720 return {a, b};
721}
722
743template<StrExpr A, StrExprForType<typename A::symb_type> B, bool last = true>
744struct strexprjoin_c : expr_to_std_string<strexprjoin_c<A, B, last>>{
745 using symb_type = typename A::symb_type;
746 const A& a;
747 B b;
748 template<typename... Args>
749 constexpr strexprjoin_c(const A& a_, Args&&... args_) : a(a_), b(std::forward<Args>(args_)...) {}
750 constexpr size_t length() const noexcept {
751 return a.length() + b.length();
752 }
753 constexpr symb_type* place(symb_type* p) const noexcept {
754 if constexpr (last) {
755 return (symb_type*)b.place((typename B::symb_type*)a.place(p));
756 } else {
757 return a.place((symb_type*)b.place((typename B::symb_type*)p));
758 }
759 }
760};
761
776template<StrExpr A, convertible_to_strexpr<typename A::symb_type> B>
778 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
779}
780
795template<StrExpr A, convertible_to_strexpr<typename A::symb_type> B>
797 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
798}
799
834template<typename K>
835struct empty_expr : expr_to_std_string<empty_expr<K>>{
836 using symb_type = K;
837 constexpr size_t length() const noexcept {
838 return 0;
839 }
840 constexpr symb_type* place(symb_type* p) const noexcept {
841 return p;
842 }
843};
844
850inline constexpr empty_expr<u8s> eea{};
856inline constexpr empty_expr<u8s> eeb{};
862inline constexpr empty_expr<uws> eew{};
868inline constexpr empty_expr<u16s> eeu{};
874inline constexpr empty_expr<u32s> eeuu{};
875
876template<typename K>
877struct expr_char : expr_to_std_string<expr_char<K>>{
878 using symb_type = K;
879 K value;
880 constexpr expr_char(K v) : value(v){}
881 constexpr size_t length() const noexcept {
882 return 1;
883 }
884 constexpr symb_type* place(symb_type* p) const noexcept {
885 *p++ = value;
886 return p;
887 }
888};
889
902template<typename K, StrExprForType<K> A>
903constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
904 return {a, s};
905}
906
917template<typename K>
918constexpr expr_char<K> e_char(K s) {
919 return {s};
920}
921
922template<typename K, size_t N>
923struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
924 using symb_type = K;
925 const K (&str)[N + 1];
926 constexpr expr_literal(const K (&str_)[N + 1]) : str(str_){}
927
928 constexpr size_t length() const noexcept {
929 return N;
930 }
931 constexpr symb_type* place(symb_type* p) const noexcept {
932 if constexpr (N != 0)
933 std::char_traits<K>::copy(p, str, N);
934 return p + N;
935 }
936};
937
990template<typename T, size_t N = const_lit<T>::Count>
991constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
992 return {s};
993}
994
995template<bool first, typename K, size_t N, typename A>
996struct expr_literal_join : expr_to_std_string<expr_literal_join<first, K, N, A>> {
997 using symb_type = K;
998 using atype = typename A::symb_type;
999 const K (&str)[N + 1];
1000 const A& a;
1001 constexpr expr_literal_join(const K (&str_)[N + 1], const A& a_) : str(str_), a(a_){}
1002
1003 constexpr size_t length() const noexcept {
1004 return N + a.length();
1005 }
1006 constexpr symb_type* place(symb_type* p) const noexcept {
1007 if constexpr (N != 0) {
1008 if constexpr (first) {
1009 std::char_traits<K>::copy(p, str, N);
1010 return (symb_type*)a.place((atype*)(p + N));
1011 } else {
1012 p = (symb_type*)a.place((atype*)p);
1013 std::char_traits<K>::copy(p, str, N);
1014 return p + N;
1015 }
1016 } else {
1017 return a.place(p);
1018 }
1019 }
1020};
1021
1029template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
1030constexpr expr_literal_join<false, P, (N - 1), A> operator+(const A& a, T&& s) {
1031 return {s, a};
1032}
1033
1041template<StrExpr A, typename T, typename P = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count> requires is_equal_str_type_v<typename A::symb_type, P>
1042constexpr expr_literal_join<true, P, (N - 1), A> operator+(T&& s, const A& a) {
1043 return {s, a};
1044}
1045
1059template<typename K, size_t N, size_t S = ' '>
1060struct expr_spaces : expr_to_std_string<expr_spaces<K, N>> {
1061 using symb_type = K;
1062 constexpr size_t length() const noexcept {
1063 return N;
1064 }
1065 constexpr symb_type* place(symb_type* p) const noexcept {
1066 if constexpr (N != 0)
1067 std::char_traits<K>::assign(p, N, static_cast<K>(S));
1068 return p + N;
1069 }
1070};
1071
1085template<size_t N>
1087 return {};
1088}
1089
1103template<size_t N>
1105 return {};
1106}
1107
1119template<typename K>
1120struct expr_pad : expr_to_std_string<expr_pad<K>> {
1121 using symb_type = K;
1122 size_t len;
1123 K s;
1124 constexpr expr_pad(size_t len_, K s_) : len(len_), s(s_){}
1125 constexpr size_t length() const noexcept {
1126 return len;
1127 }
1128 constexpr symb_type* place(symb_type* p) const noexcept {
1129 if (len)
1130 std::char_traits<K>::assign(p, len, s);
1131 return p + len;
1132 }
1133};
1134
1148template<typename K>
1149constexpr expr_pad<K> e_c(size_t l, K s) {
1150 return { l, s };
1151}
1152
1153template<typename K, size_t N>
1154struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
1155 using symb_type = K;
1156 size_t repeat_;
1157 const K (&s)[N + 1];
1158 constexpr expr_repeat_lit(size_t repeat, const K (&s_)[N + 1]) : repeat_(repeat), s(s_){}
1159 constexpr size_t length() const noexcept {
1160 return N * repeat_;
1161 }
1162 constexpr symb_type* place(symb_type* p) const noexcept {
1163 if constexpr (N) {
1164 for (size_t i = 0; i < repeat_; i++) {
1165 std::char_traits<K>::copy(p, s, N);
1166 p += N;
1167 }
1168 }
1169 return p;
1170 }
1171};
1172
1173template<StrExpr A>
1174struct expr_repeat_expr : expr_to_std_string<expr_repeat_expr<A>> {
1175 using symb_type = typename A::symb_type;
1176 size_t repeat_;
1177 const A& expr_;
1178 constexpr expr_repeat_expr(size_t repeat, const A& expr) : repeat_(repeat), expr_(expr){}
1179 constexpr size_t length() const noexcept {
1180 if (repeat_) {
1181 return repeat_ * expr_.length();
1182 }
1183 return 0;
1184 }
1185 constexpr symb_type* place(symb_type* p) const noexcept {
1186 if (repeat_) {
1187 if (repeat_ == 1) {
1188 return expr_.place(p);
1189 }
1190 symb_type* start = p;
1191 p = expr_.place(p);
1192 size_t len = size_t(p - start);
1193 if (len) {
1194 for (size_t i = 1; i < repeat_; i++) {
1195 std::char_traits<symb_type>::copy(p, start, len);
1196 p += len;
1197 }
1198 }
1199 }
1200 return p;
1201 }
1202};
1203
1217template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
1218constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
1219 return { l, s };
1220}
1221
1235template<StrExpr A>
1236constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
1237 return { l, s };
1238}
1239
1253template<StrExpr A, StrExprForType<typename A::symb_type> B>
1254struct expr_choice : expr_to_std_string<expr_choice<A, B>> {
1255 using symb_type = typename A::symb_type;
1256 using my_type = expr_choice<A, B>;
1257 const A& a;
1258 const B& b;
1259 bool choice;
1260
1261 constexpr expr_choice(const A& _a, const B& _b, bool _choice) : a(_a), b(_b), choice(_choice){}
1262
1263 constexpr size_t length() const noexcept {
1264 return choice ? a.length() : b.length();
1265 }
1266 constexpr symb_type* place(symb_type* ptr) const noexcept {
1267 return choice ? a.place(ptr) : (symb_type*)b.place((typename B::symb_type*)ptr);
1268 }
1269};
1270
1282template<StrExpr A>
1283struct expr_if : expr_to_std_string<expr_if<A>> {
1284 using symb_type = typename A::symb_type;
1285 using my_type = expr_if<A>;
1286 const A& a;
1287 bool choice;
1288 constexpr expr_if(const A& _a, bool _choice) : a(_a), choice(_choice){}
1289
1290 constexpr size_t length() const noexcept {
1291 return choice ? a.length() : 0;
1292 }
1293 constexpr symb_type* place(symb_type* ptr) const noexcept {
1294 return choice ? a.place(ptr) : ptr;
1295 }
1296};
1297
1344template<typename L, StrExprForType<L> A, size_t N, bool Compare>
1345struct expr_choice_one_lit : expr_to_std_string<expr_choice_one_lit<L, A, N, Compare>> {
1346 using symb_type = L;
1347 const symb_type (&str)[N + 1];
1348 const A& a;
1349 bool choice;
1350 constexpr expr_choice_one_lit(const symb_type (&_str)[N + 1], const A& _a, bool _choice) : str(_str), a(_a), choice(_choice){}
1351
1352 constexpr size_t length() const noexcept {
1353 return choice == Compare ? a.length() : N;
1354 }
1355 constexpr symb_type* place(symb_type* ptr) const noexcept {
1356 if (choice == Compare) {
1357 return (L*)a.place((typename A::symb_type*)ptr);
1358 }
1359 if constexpr (N != 0) {
1360 std::char_traits<symb_type>::copy(ptr, str, N);
1361 }
1362 return ptr + N;
1363 }
1364};
1365
1410template<typename K, size_t N, typename P, size_t M>
1411struct expr_choice_two_lit : expr_to_std_string<expr_choice_two_lit<K, N, P, M>> {
1412 using symb_type = K;
1413 const K (&str_a)[N + 1];
1414 const P (&str_b)[M + 1];
1415 bool choice;
1416 constexpr expr_choice_two_lit(const K(&_str_a)[N + 1], const P(&_str_b)[M + 1], bool _choice)
1417 : str_a(_str_a), str_b(_str_b), choice(_choice){}
1418
1419 constexpr size_t length() const noexcept {
1420 return choice ? N : M;
1421 }
1422 constexpr symb_type* place(symb_type* ptr) const noexcept {
1423 if (choice) {
1424 if constexpr (N != 0) {
1425 std::char_traits<symb_type>::copy(ptr, str_a, N);
1426 }
1427 return ptr + N;
1428 }
1429 if constexpr (M != 0) {
1430 std::char_traits<symb_type>::copy(ptr, (const K(&)[M + 1])str_b, M);
1431 }
1432 return ptr + M;
1433 }
1434};
1435
1465template<StrExpr A, StrExprForType<typename A::symb_type> B>
1466constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
1467 return {a, b, c};
1468}
1469
1475template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1476constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
1477 return {str, a, c};
1478}
1479
1485template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1486constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
1487 return {str, a, c};
1488}
1489
1494template<typename T, typename L, typename K = typename const_lit<T>::symb_type, typename P = typename const_lit<L>::symb_type,
1495 size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
1496 requires is_equal_str_type_v<K, P>
1497constexpr expr_choice_two_lit<K, N -1, P, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
1498 return {str_a, str_b, c};
1499}
1500
1543template<StrExpr A>
1544constexpr expr_if<A> e_if(bool c, const A& a) {
1545 return {a, c};
1546}
1547
1552template<typename T, size_t N = const_lit<T>::Count>
1553constexpr auto e_if(bool c, T&& str) {
1554 using K = typename const_lit<T>::symb_type;
1555 const K empty[1] = {0};
1556 return expr_choice_two_lit<K, N - 1, K, 0>{str, empty, c};
1557}
1558
1568template<typename K, StdStrSourceForType<K> T>
1569struct expr_stdstr {
1570 using symb_type = K;
1571 const T& t_;
1572
1573 expr_stdstr(const T& t) : t_(t){}
1574
1575 constexpr size_t length() const noexcept {
1576 return t_.length();
1577 }
1578 constexpr symb_type* place(symb_type* p) const noexcept {
1579 size_t s = t_.size();
1580 std::char_traits<K>::copy(p, (const K*)t_.data(), s);
1581 return p + s;
1582 }
1583};
1584
1595template<typename K, StdStrSource T>
1596struct convert_to_strexpr<K, T> {
1597 using type = expr_stdstr<K, T>;
1598};
1599
1600namespace str {
1601constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
1602} // namespace str
1603
1604template<typename K>
1605std::char_traits<K> char_traits_selector(...);
1606
1607template<typename K>
1608using ch_traits = decltype(char_traits_selector<K>(int(0)));
1609
1610template<typename T>
1611concept FromIntNumber =
1612 is_one_of_type<std::remove_cv_t<T>, unsigned char, int, short, long, long long, unsigned, unsigned short, unsigned long, unsigned long long>::value;
1613
1614template<typename T>
1615concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1616
1617template<typename K, bool I, typename T>
1618struct need_sign { // NOLINT
1619 bool negate;
1620 std::make_unsigned_t<T> val;
1621 constexpr need_sign(T t) : negate(t < 0), val(t < 0 ? std::make_unsigned_t<T>{} - t : t) {}
1622 constexpr void after(K*& ptr) {
1623 if (negate)
1624 *--ptr = '-';
1625 }
1626};
1627
1628template<typename K, typename T>
1629struct need_sign<K, false, T> {
1630 T val;
1631 constexpr need_sign(T t) : val(t){}
1632 constexpr void after(K*&) {}
1633};
1634
1635template<typename K, typename T>
1636constexpr size_t fromInt(K* bufEnd, T val) {
1637 const char* twoDigit =
1638 "0001020304050607080910111213141516171819"
1639 "2021222324252627282930313233343536373839"
1640 "4041424344454647484950515253545556575859"
1641 "6061626364656667686970717273747576777879"
1642 "8081828384858687888990919293949596979899";
1643 if (val) {
1644 need_sign<K, std::is_signed_v<T>, T> store(val);
1645 K* itr = bufEnd;
1646 while (store.val >= 100) {
1647 const char* ptr = twoDigit + (store.val % 100) * 2;
1648 *--itr = static_cast<K>(ptr[1]);
1649 *--itr = static_cast<K>(ptr[0]);
1650 store.val /= 100;
1651 }
1652 if (store.val < 10) {
1653 *--itr = static_cast<K>('0' + store.val);
1654 } else {
1655 const char* ptr = twoDigit + store.val * 2;
1656 *--itr = static_cast<K>(ptr[1]);
1657 *--itr = static_cast<K>(ptr[0]);
1658 }
1659 store.after(itr);
1660 return size_t(bufEnd - itr);
1661 }
1662 bufEnd[-1] = '0';
1663 return 1;
1664}
1665
1666template<typename K, typename T>
1667struct expr_num : expr_to_std_string<expr_num<K, T>> {
1668 using symb_type = K;
1669 using my_type = expr_num<K, T>;
1670
1671 enum { bufSize = 24 };
1672 mutable K buf[bufSize];
1673 mutable T value;
1674
1675 constexpr expr_num(T t) : value(t) {}
1676 constexpr expr_num(expr_num&& t) noexcept : value(t.value) {}
1677
1678 constexpr size_t length() const noexcept {
1679 value = static_cast<T>(fromInt(buf + bufSize, value));
1680 return static_cast<size_t>(value);
1681 }
1682 constexpr K* place(K* ptr) const noexcept {
1683 size_t len = static_cast<size_t>(value);
1684 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1685 return ptr + len;
1686 }
1687};
1688
1699template<typename K, FromIntNumber T>
1700struct convert_to_strexpr<K, T> {
1701 using type = expr_num<K, T>;
1702};
1703
1719template<typename K, FromIntNumber T>
1720constexpr expr_num<K, T> e_num(T t) {
1721 return {t};
1722}
1723
1724namespace f{
1725
1726enum class int_align { none, left, right, center };
1727enum class int_plus_sign {none, plus, space};
1728enum class int_prefix {none, lcase, ucase};
1729
1730enum fmt_kinds : unsigned {
1731 fmt_none = 0,
1732 fmt_align = 1,
1733 fmt_width = 2,
1734 fmt_fill = 4,
1735 fmt_sign = 8,
1736 fmt_upper = 16,
1737 fmt_prefix = 32,
1738 fmt_zero = 64,
1739};
1740
1741template<typename A, typename B, unsigned Kind>
1742struct fmt_info {
1743 using a_t = A;
1744 using b_t = B;
1745 inline static constexpr unsigned kind = Kind;
1746};
1747
1748struct fmt_default {
1749 inline static constexpr unsigned kind = fmt_none;
1750};
1751
1752template<int_align A>
1753struct align_info{
1754 inline static constexpr unsigned kind = fmt_align;
1755};
1756
1757template<size_t Width = 0>
1758struct width_info {
1759 inline static constexpr unsigned kind = fmt_width;
1760};
1761
1762template<unsigned Fill = ' '>
1763struct fill_info {
1764 inline static constexpr unsigned kind = fmt_fill;
1765};
1766
1767template<int_plus_sign S>
1768struct sign_info {
1769 inline static constexpr unsigned kind = fmt_sign;
1770};
1771
1772struct upper_info {
1773 inline static constexpr unsigned kind = fmt_upper;
1774};
1775
1776template<int_prefix>
1777struct prefix_info {
1778 inline static constexpr unsigned kind = fmt_prefix;
1779};
1780
1781struct zero_info {
1782 inline static constexpr unsigned kind = fmt_zero;
1783};
1784
1785template<typename T>
1786concept FmtParam = requires {
1787 {std::remove_cvref_t<T>::kind} -> std::same_as<const unsigned&>;
1788};
1789
1790template<FmtParam A, FmtParam B>
1791requires((A::kind & B::kind) == 0)
1792constexpr auto operator | (const A&, const B&) {
1793 return fmt_info<A, B, A::kind | B::kind>{};
1794}
1795
1796inline constexpr align_info<int_align::left> l;
1797inline constexpr align_info<int_align::right> r;
1798inline constexpr align_info<int_align::center> c;
1799
1800template<size_t W>
1801inline constexpr width_info<W> w;
1802
1803template<unsigned F>
1804inline constexpr fill_info<F> f;
1805
1806inline constexpr sign_info<int_plus_sign::plus> sp;
1807inline constexpr sign_info<int_plus_sign::space> ss;
1808inline constexpr upper_info u;
1809inline constexpr prefix_info<int_prefix::lcase> p;
1810inline constexpr prefix_info<int_prefix::ucase> P;
1811inline constexpr zero_info z;
1812
1813inline constexpr width_info<unsigned(-1)> wp;
1814inline constexpr fmt_default df;
1815
1816template<typename T>
1817struct extract_align : std::false_type {
1818 inline static constexpr int_align align = int_align::none;
1819};
1820
1821template<int_align A>
1822struct extract_align<align_info<A>> : std::true_type {
1823 inline static constexpr int_align align = A;
1824};
1825
1826template<typename A, typename B, unsigned U>
1827struct extract_align<fmt_info<A, B, U>> {
1828 inline static constexpr bool in_a = extract_align<A>::value, in_b = extract_align<B>::value, value = in_a | in_b;
1829 inline static constexpr int_align align = extract_align<std::conditional_t<in_a, A, std::conditional_t<in_b, B, align_info<int_align::none>>>>::align;
1830};
1831
1832template<typename T>
1833struct extract_width : std::false_type {
1834 inline static constexpr size_t width = 0;
1835};
1836
1837template<size_t W>
1838struct extract_width<width_info<W>> : std::true_type {
1839 inline static constexpr size_t width = W;
1840};
1841
1842template<typename A, typename B, unsigned U>
1843struct extract_width<fmt_info<A, B, U>> {
1844 inline static constexpr bool in_a = extract_width<A>::value, in_b = extract_width<B>::value, value = in_a | in_b;
1845 inline static constexpr size_t width = extract_width<std::conditional_t<in_a, A, std::conditional_t<in_b, B, width_info<0>>>>::width;
1846};
1847
1848template<typename T>
1849struct extract_fill : std::false_type {
1850 inline static constexpr unsigned fill = ' ';
1851};
1852
1853template<unsigned F>
1854struct extract_fill<fill_info<F>> : std::true_type {
1855 inline static constexpr unsigned fill = F;
1856};
1857
1858template<typename A, typename B, unsigned U>
1859struct extract_fill<fmt_info<A, B, U>> {
1860 inline static constexpr bool in_a = extract_fill<A>::value, in_b = extract_fill<B>::value, value = in_a | in_b;
1861 inline static constexpr unsigned fill = extract_fill<std::conditional_t<in_a, A, std::conditional_t<in_b, B, fill_info<' '>>>>::fill;
1862};
1863
1864template<typename T>
1865struct extract_sign : std::false_type {
1866 inline static constexpr int_plus_sign sign = int_plus_sign::none;
1867};
1868
1869template<int_plus_sign S>
1870struct extract_sign<sign_info<S>> : std::true_type {
1871 inline static constexpr int_plus_sign sign = S;
1872};
1873
1874template<typename A, typename B, unsigned U>
1875struct extract_sign<fmt_info<A, B, U>> {
1876 inline static constexpr bool in_a = extract_sign<A>::value, in_b = extract_sign<B>::value, value = in_a | in_b;
1877 inline static constexpr int_plus_sign sign = extract_sign<std::conditional_t<in_a, A, std::conditional_t<in_b, B, sign_info<int_plus_sign::none>>>>::sign;
1878};
1879
1880template<typename T>
1881struct extract_upper : std::false_type {};
1882
1883template<>
1884struct extract_upper<upper_info> : std::true_type {};
1885
1886template<typename A, typename B, unsigned U>
1887struct extract_upper<fmt_info<A, B, U>> {
1888 inline static constexpr bool in_a = extract_upper<A>::value, in_b = extract_upper<B>::value,
1889 value = extract_upper<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1890};
1891
1892template<typename T>
1893struct extract_prefix : std::false_type{
1894 inline static constexpr int_prefix prefix = int_prefix::none;
1895};
1896
1897template<int_prefix P>
1898struct extract_prefix<prefix_info<P>> : std::true_type {
1899 inline static constexpr int_prefix prefix = P;
1900};
1901
1902template<typename A, typename B, unsigned U>
1903struct extract_prefix<fmt_info<A, B, U>> {
1904 inline static constexpr bool in_a = extract_prefix<A>::value, in_b = extract_prefix<B>::value, value = in_a | in_b;
1905 inline static constexpr int_prefix prefix = extract_prefix<std::conditional_t<in_a, A, std::conditional_t<in_b, B, prefix_info<int_prefix::none>>>>::prefix;
1906};
1907
1908template<typename T>
1909struct extract_zero : std::false_type{};
1910
1911template<>
1912struct extract_zero<zero_info> : std::true_type {};
1913
1914template<typename A, typename B, unsigned U>
1915struct extract_zero<fmt_info<A, B, U>> {
1916 inline static constexpr bool in_a = extract_zero<A>::value, in_b = extract_zero<B>::value,
1917 value = extract_zero<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1918};
1919
1920template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1921struct fmt_params {
1922 inline static constexpr int_align align = Align;
1923 inline static constexpr unsigned width = Width;
1924 inline static constexpr unsigned fill = Fill;
1925 inline static constexpr int_plus_sign sign = Sign;
1926 inline static constexpr bool upper = Upper;
1927 inline static constexpr int_prefix prefix = Prefix;
1928 inline static constexpr bool zero = Zero;
1929};
1930
1931template<FmtParam T>
1932using p_to_fmt_t = fmt_params<
1933 extract_align<T>::align,
1934 extract_width<T>::width,
1935 extract_fill<T>::fill,
1936 extract_sign<T>::sign,
1937 extract_upper<T>::value,
1938 extract_prefix<T>::prefix,
1939 extract_zero<T>::value>;
1940
1941template<FmtParam T>
1942using to_fmt_t = p_to_fmt_t<std::remove_cvref_t<T>>;
1943
1944template<typename T>
1945struct is_fmt_params : std::false_type{};
1946
1947template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1948struct is_fmt_params<fmt_params<Align, Width, Fill, Sign, Upper, Prefix, Zero>> : std::true_type{};
1949
1950template<typename T>
1951concept FmtParamSet = is_fmt_params<T>::value;
1952
1953struct fmt_end{};
1954
1955template<f::FmtParam F>
1956constexpr auto operator|(const F& f, fmt_end) {
1957 return f;
1958}
1959
1960template<char C = 0, char...Chars>
1961constexpr auto parse_fmt_symbol();
1962
1963template<unsigned N, char C = 'Z', char...Chars>
1964constexpr auto parse_width() {
1965 if constexpr (C >= '0' && C <= '9') {
1966 return parse_width<N * 10 + C - '0', Chars...>();
1967 } else {
1968 return w<N> | parse_fmt_symbol<C, Chars...>();
1969 }
1970}
1971
1972template<unsigned N, char C = 'Z', char...Chars>
1973constexpr auto parse_fill() {
1974 if constexpr (C >= '0' && C <= '9') {
1975 return parse_fill<N * 16 + C - '0', Chars...>();
1976 } else if constexpr (C >= 'a' && C <= 'f') {
1977 return parse_fill<N * 16 + C - 'a' + 10, Chars...>();
1978 } else if constexpr (C >= 'A' && C <= 'F') {
1979 return parse_fill<N * 16 + C - 'A' + 10, Chars...>();
1980 } else {
1981 return f<N> | parse_fmt_symbol<C, Chars...>();
1982 }
1983}
1984
1985template<char C, char...Chars>
1986constexpr auto parse_fmt_symbol() {
1987 if constexpr (C == '0') {
1988 return z | parse_fmt_symbol<Chars...>();
1989 } else if constexpr (C == 'a') {
1990 return p | parse_fmt_symbol<Chars...>();
1991 } else if constexpr (C == 'A') {
1992 return P | parse_fmt_symbol<Chars...>();
1993 } else if constexpr (C == 'b') {
1994 return l | parse_fmt_symbol<Chars...>();
1995 } else if constexpr (C == 'c') {
1996 return c | parse_fmt_symbol<Chars...>();
1997 } else if constexpr (C == 'd') {
1998 return r | parse_fmt_symbol<Chars...>();
1999 } else if constexpr (C == 'e') {
2000 return sp | parse_fmt_symbol<Chars...>();
2001 } else if constexpr (C == 'f') {
2002 return ss | parse_fmt_symbol<Chars...>();
2003 } else if constexpr (C == 'E') {
2004 return u | parse_fmt_symbol<Chars...>();
2005 } else if constexpr (C == '\'') {
2006 return parse_fmt_symbol<Chars...>();
2007 } else if constexpr (C == 'F') {
2008 return parse_fill<0, Chars...>();
2009 } else if constexpr (C >= '1' && C <= '9') {
2010 return parse_width<C - '0', Chars...>();
2011 } else {
2012 return fmt_end{};
2013 }
2014}
2015
2016template<unsigned R, typename T>
2017struct fmt_radix_info {};
2018
2019template<unsigned N, char C = 0, char...Chars>
2020constexpr auto parse_radix() {
2021 if constexpr (C >='0' && C <= '9') {
2022 return parse_radix<N * 10 + C - '0', Chars...>();
2023 } else if constexpr (C == 0) {
2024 return fmt_radix_info<N, to_fmt_t<fmt_default>>{};
2025 } else {
2026 return fmt_radix_info<N, decltype(parse_fmt_symbol<C, Chars...>())>{};
2027 }
2028};
2029
2030template<char C1, char C2, char C3, char...Chars>
2031constexpr auto skip_0x() {
2032 static_assert(C1 == '0' && C2 == 'x' && "Fmt symbols must begin with 0x");
2033 static_assert(C3 >= '1' && C3 <= '9' && "Radix must begin with 1-9");
2034 return parse_radix<C3 - '0', Chars...>();
2035}
2036
2037} // namespace f
2038
2039template<typename K, bool Ucase>
2040inline constexpr K digits_symbols[36] = {K('0'), K('1'), K('2'), K('3'), K('4'), K('5'), K('6'), K('7'), K('8'), K('9'),
2041 K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'), K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'),
2042 K(Ucase ? 'F' : 'f'), K(Ucase ? 'G' : 'g'), K(Ucase ? 'H' : 'h'), K(Ucase ? 'I' : 'i'), K(Ucase ? 'J' : 'j'),
2043 K(Ucase ? 'K' : 'k'), K(Ucase ? 'L' : 'l'), K(Ucase ? 'M' : 'm'), K(Ucase ? 'N' : 'n'), K(Ucase ? 'O' : 'o'),
2044 K(Ucase ? 'P' : 'p'), K(Ucase ? 'Q' : 'q'), K(Ucase ? 'R' : 'r'), K(Ucase ? 'S' : 's'), K(Ucase ? 'T' : 't'),
2045 K(Ucase ? 'U' : 'u'), K(Ucase ? 'V' : 'v'), K(Ucase ? 'W' : 'w'), K(Ucase ? 'X' : 'x'), K(Ucase ? 'Y' : 'y'),
2046 K(Ucase ? 'Z' : 'z'),
2047};
2048
2049template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2050struct expr_integer_src {
2051 T value_;
2052 unsigned width_{};
2053 constexpr expr_integer_src(T v) : value_(v){}
2054 constexpr expr_integer_src(T v, unsigned w) : value_(v), width_(w){}
2055
2056 template<is_std_string_v S>
2057 operator S() const;
2058
2059 template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2060 operator S() const;
2061};
2062
2063template<is_one_of_char_v K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2064requires (Radix > 1 && Radix <= 36)
2065struct expr_integer : expr_to_std_string<expr_integer<K, T, Radix, FP>> {
2066 using symb_type = K;
2067
2068 enum { bufSize = 64 };
2069 mutable K buf[bufSize];
2070 mutable T value_;
2071 unsigned width_{};
2072
2073 mutable bool negate_{};
2074
2075 constexpr expr_integer(T t) : value_(t){}
2076 constexpr expr_integer(T t, unsigned w) : value_(t), width_(w){}
2077 constexpr expr_integer(const expr_integer_src<T, Radix, FP>& v) : value_(v.value_), width_(v.width_){}
2078
2079 constexpr expr_integer(expr_integer&& t) noexcept : value_(t.value_), width_(t.width_){}
2080
2081 constexpr size_t length() const noexcept {
2082 K* bufEnd = std::end(buf), *itr = bufEnd;
2083
2084 if (value_) {
2085 need_sign<K, std::is_signed_v<T>, T> store(value_);
2086 while (store.val) {
2087 *--itr = digits_symbols<K, FP::upper>[store.val % Radix];
2088 store.val /= Radix;
2089 }
2090 if constexpr (std::is_signed_v<T>) {
2091 negate_ = store.negate;
2092 }
2093 } else {
2094 *--itr = digits_symbols<K, FP::upper>[0];
2095 }
2096 size_t len = bufEnd - itr;
2097 value_ = T(len);
2098 if constexpr (std::is_signed_v<T>) {
2099 if constexpr (FP::sign != f::int_plus_sign::none) {
2100 len++;
2101 } else {
2102 if (negate_) {
2103 len++;
2104 }
2105 }
2106 }
2107 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2108 len++; // add 0
2109 if constexpr (Radix != 8) { // for octo just add 0,
2110 len++; // for other b or x
2111 }
2112 }
2113 if constexpr (FP::width == unsigned(-1)) {
2114 return std::max<size_t>(width_, len);
2115 } else {
2116 return std::max<size_t>(FP::width, len);
2117 }
2118 }
2119 constexpr K* place(K* ptr) const noexcept {
2120 size_t len = static_cast<size_t>(value_), all_len = len;
2121 if constexpr (std::is_signed_v<T>) {
2122 if constexpr (FP::sign != f::int_plus_sign::none) {
2123 all_len++;
2124 } else {
2125 if (negate_) {
2126 all_len++;
2127 }
2128 }
2129 }
2130 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2131 all_len++; // add 0
2132 if constexpr (Radix != 8) { // for octo just add 0,
2133 all_len++; // for other b or x
2134 }
2135 }
2136 if constexpr (FP::zero) {
2137 if constexpr (std::is_signed_v<T>) {
2138 if (negate_) {
2139 *ptr++ = K('-');
2140 } else {
2141 if constexpr (FP::sign == f::int_plus_sign::plus) {
2142 *ptr++ = K('+');
2143 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2144 *ptr++ = K(' ');
2145 }
2146 }
2147 }
2148 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2149 *ptr++ = K('0');
2150 if constexpr (Radix == 2) {
2151 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2152 } else if constexpr (Radix == 16) {
2153 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2154 }
2155 }
2156 size_t before = 0;
2157 if constexpr (FP::width == unsigned(-1)) {
2158 if (width_ > all_len) {
2159 before = width_ - all_len;
2160 }
2161 } else {
2162 if (FP::width > all_len) {
2163 before = FP::width - all_len;
2164 }
2165 }
2166 if (before) {
2167 ch_traits<K>::assign(ptr, before, K('0'));
2168 ptr += before;
2169 }
2170 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2171 ptr += len;
2172 } else {
2173 size_t before = 0, after = 0;
2174 if constexpr (FP::width == unsigned(-1)) {
2175 if (width_ > all_len) {
2176 if constexpr (FP::align == f::int_align::left) {
2177 after = width_ - all_len;
2178 } else if constexpr (FP::align == f::int_align::center) {
2179 before = (width_ - all_len) / 2;
2180 after = width_ - all_len - before;
2181 } else {
2182 before = width_ - all_len;
2183 }
2184 }
2185 } else {
2186 if (FP::width > all_len) {
2187 if constexpr (FP::align == f::int_align::left) {
2188 after = FP::width - all_len;
2189 } else if constexpr (FP::align == f::int_align::center) {
2190 before = (FP::width - all_len) / 2;
2191 after = FP::width - all_len - before;
2192 } else {
2193 before = FP::width - all_len;
2194 }
2195 }
2196 }
2197 if (before) {
2198 ch_traits<K>::assign(ptr, before, K(FP::fill));
2199 ptr += before;
2200 }
2201 if constexpr (std::is_signed_v<T>) {
2202 if (negate_) {
2203 *ptr++ = K('-');
2204 } else {
2205 if constexpr (FP::sign == f::int_plus_sign::plus) {
2206 *ptr++ = K('+');
2207 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2208 *ptr++ = K(' ');
2209 }
2210 }
2211 }
2212 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2213 *ptr++ = K('0');
2214 if constexpr (Radix == 2) {
2215 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2216 } else if constexpr (Radix == 16) {
2217 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2218 }
2219 }
2220 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2221 ptr += len;
2222
2223 if (after) {
2224 ch_traits<K>::assign(ptr, after, K(FP::fill));
2225 ptr += after;
2226 }
2227 }
2228 return ptr;
2229 }
2230};
2231
2232template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2233template<is_std_string_v S>
2234expr_integer_src<T, Radix, FP>::operator S() const {
2235 using st = typename S::value_type;
2236 using Al = typename S::allocator_type;
2237 return to_std_string<st, Al>(expr_integer<st, T, Radix, FP>{value_, width_});
2238}
2239
2240template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2241template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2242expr_integer_src<T, Radix, FP>::operator S() const {
2243 using st = typename S::symb_type;
2244 return S{expr_integer<st, T, Radix, FP>{value_, width_}};
2245}
2246
2247template<typename K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2248struct convert_to_strexpr<K, expr_integer_src<T, Radix, FP>> {
2249 using type = expr_integer<K, T, Radix, FP>;
2250};
2251
2252template<unsigned R, typename T, typename F>
2253struct flags_checker {
2254 using val_t = T;
2255 using flags_t = f::to_fmt_t<F>;
2256 inline static constexpr unsigned Radix = R;
2257};
2258
2259template<typename T, bool ArgWidth>
2260concept good_int_flags =
2261 T::Radix > 1 && T::Radix <= 36
2262 // Должно быть задано только или выравнивание, или заполнение нулём.
2263 // Only either alignment or zero-padding must be specified.
2264 && (T::flags_t::align == f::int_align::none || !T::flags_t::zero)
2265 // Флаг вывода знака должен задаваться только для знаковых чисел
2266 // The sign output flag should only be set for signed numbers.
2267 && (std::is_signed_v<typename T::val_t> || T::flags_t::sign == f::int_plus_sign::none)
2268 // Неверное поле ширины
2269 // Invalid width field
2270 && (T::flags_t::width == unsigned(-1)) == ArgWidth
2271 // Префикс может быть только при основании 2, 8, 16
2272 // Prefix may by only for radix 2, 8, 16
2273 && (T::flags_t::prefix == f::int_prefix::none || T::Radix == 2 || T::Radix == 8 || T::Radix == 16);
2274
2382template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, false>
2383constexpr auto e_int(T v) {
2384 using fmt_param = f::to_fmt_t<decltype(fp)>;
2385 return expr_integer_src<T, R, fmt_param>{v};
2386}
2387
2391template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, true>
2392constexpr auto e_int(T v, unsigned w) {
2393 using fmt_param = f::to_fmt_t<decltype(fp)>;
2394 return expr_integer_src<T, R, fmt_param>{v, w};
2395}
2396
2397template<typename T, unsigned R, typename F> requires good_int_flags<flags_checker<R, T, F>, false>
2398auto operator / (T v, const f::fmt_radix_info<R, F>&) {
2399 return expr_integer_src<T, R, f::to_fmt_t<F>>{v};
2400}
2401
2402template<typename K>
2403struct expr_real : expr_to_std_string<expr_real<K>> {
2404 using symb_type = K;
2405 mutable u8s buf[40];
2406 mutable size_t l;
2407 double v;
2408 constexpr expr_real(double d) : v(d) {}
2409 constexpr expr_real(float d) : v(d) {}
2410
2411 size_t length() const noexcept {
2412 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
2413 l = ec != std::errc{} ? 0 : ptr - buf;
2414 return l;
2415 }
2416 K* place(K* ptr) const noexcept {
2417 if constexpr (sizeof(K) == sizeof(buf[0])) {
2418 ch_traits<K>::copy(ptr, (K*)buf, l);
2419 } else {
2420 for (size_t i = 0; i < l; i++) {
2421 ptr[i] = buf[i];
2422 }
2423 }
2424 return ptr + l;
2425 }
2426};
2427
2438template<typename K, std::floating_point T>
2439struct convert_to_strexpr<K, T> {
2440 using type = expr_real<K>;
2441};
2442
2454template<typename K> requires is_one_of_char_v<K>
2455inline constexpr expr_real<K> e_num(double t) {
2456 return {t};
2457}
2458
2459template<FromIntNumber Val, bool All, bool Ucase, bool Ox>
2460struct expr_hex_src {
2461 explicit constexpr expr_hex_src(Val v) : v_(v){}
2462 Val v_;
2463};
2464
2465template<typename K, FromIntNumber Val, bool All, bool Ucase, bool Ox>
2466struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
2467 using symb_type = K;
2468 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
2469 mutable K buf_[sizeof(Val) * 2]{};
2470
2471 explicit constexpr expr_hex(Val v) : v_(v){}
2472 constexpr expr_hex(const expr_hex_src<Val, All, Ucase, Ox>& v) : v_(v.v_){}
2473
2474 constexpr size_t length() const noexcept {
2475 K *ptr = buf_ + std::size(buf_);
2476 size_t l = 0;
2477 for (;;) {
2478 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2479 v_.val >>= 4;
2480 l++;
2481 if (v_.val) {
2482 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2483 v_.val >>= 4;
2484 l++;
2485 }
2486 if (!v_.val) {
2487 if constexpr (All) {
2488 if (size_t need = sizeof(Val) * 2 - l) {
2489 ch_traits<K>::assign(buf_, need, K('0'));
2490 }
2491 l = sizeof(Val) * 2;
2492 }
2493 break;
2494 }
2495 }
2496 v_.val = l;
2497 if constexpr (std::is_signed_v<Val>) {
2498 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
2499 }
2500 return l + (Ox ? 2 : 0);
2501 }
2502 constexpr K* place(K* ptr) const noexcept {
2503 if constexpr (std::is_signed_v<Val>) {
2504 if (v_.negate) {
2505 *ptr++ = K('-');
2506 }
2507 }
2508 if constexpr (Ox) {
2509 *ptr++ = K('0');
2510 *ptr++ = K('x');
2511 }
2512 if constexpr (All) {
2513 ch_traits<K>::copy(ptr, buf_, sizeof(Val) * 2);
2514 return ptr + sizeof(Val) * 2;
2515 } else {
2516 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
2517 return ptr + v_.val;
2518 }
2519 }
2520};
2521
2527enum HexFlags : unsigned {
2528 Short = 1,
2529 No0x = 2, //< without 0x prefix
2530 Lcase = 4, //< Use lower case
2531};
2532
2583template<unsigned Flags = 0, FromIntNumber T>
2584constexpr auto e_hex(T v) {
2585 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2586}
2587
2588template<char c = 0, char...Chars>
2589constexpr unsigned parse_f16_flags() {
2590 if constexpr (c == '1') {
2591 return HexFlags::Short | parse_f16_flags<Chars...>();
2592 } else if constexpr (c == '2') {
2593 return HexFlags::Lcase | parse_f16_flags<Chars...>();
2594 } else if constexpr (c == '3') {
2595 return HexFlags::No0x | parse_f16_flags<Chars...>();
2596 } else {
2597 return 0;
2598 }
2599}
2600
2601template<unsigned N>
2602struct f16flags{};
2603
2604template<FromIntNumber T, unsigned Flags>
2605constexpr auto operator/(T v, const f16flags<Flags>&) {
2606 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2607}
2608
2609inline namespace literals {
2610template<char...Chars>
2611constexpr auto operator""_f16() {
2612 return f16flags<parse_f16_flags<Chars...>()>{};
2613}
2614} // namespace literals
2615
2624template<typename K, typename Val, bool All, bool Ucase, bool Ox>
2625struct convert_to_strexpr<K, expr_hex_src<Val, All, Ucase, Ox>> {
2626 using type = expr_hex<K, Val, All, Ucase, Ox>;
2627};
2628
2629template<typename K>
2630struct expr_pointer : expr_hex<K, uintptr_t, true, true, true> {
2631 constexpr expr_pointer(const void* ptr) : expr_hex<K, uintptr_t, true, true, true>((uintptr_t)ptr){}
2632};
2633
2642template<typename K, typename T>
2643struct convert_to_strexpr<K, const T*> {
2644 using type = expr_pointer<K>;
2645};
2646
2647template<typename K, StrExprForType<K> A, bool Left>
2648struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
2649 using symb_type = K;
2650 K symbol_;
2651 size_t width_;
2652 const A& a_;
2653 mutable size_t alen_{};
2654 constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
2655
2656 constexpr size_t length() const noexcept {
2657 alen_ = a_.length();
2658 return std::max(alen_, width_);
2659 }
2660 constexpr K* place(K* ptr) const noexcept {
2661 if (alen_ >= width_) {
2662 return (K*)a_.place((typename A::symb_type*)ptr);
2663 }
2664 size_t w = width_ - alen_;
2665 if constexpr (Left) {
2666 ch_traits<K>::assign(ptr, w, symbol_);
2667 ptr += w;
2668 return (K*)a_.place((typename A::symb_type*)ptr);
2669 } else {
2670 ptr = (K*)a_.place((typename A::symb_type*)ptr);
2671 ch_traits<K>::assign(ptr, w, symbol_);
2672 return ptr + w;
2673 }
2674 }
2675};
2676
2698template<StrExpr A, typename K = typename A::symb_type>
2699expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
2700 return {symbol, width, a};
2701}
2702
2724template<StrExpr A, typename K = typename A::symb_type>
2725expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
2726 return {symbol, width, a};
2727}
2728
2729/*
2730* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
2731* K - тип символов строки
2732* T - тип контейнера строк (vector, list)
2733* I - длина разделителя в символах
2734* tail - добавлять разделитель после последнего элемента контейнера.
2735* Если контейнер пустой, разделитель в любом случае не добавляется
2736* skip_empty - пропускать пустые строки без добавления разделителя
2737* To create string concatenations with vectors and lists joined by a constant delimiter
2738* K is the symbols
2739* T - type of string container (vector, list)
2740* I - length of separator in characters
2741* tail - add a separator after the last element of the container.
2742* If the container is empty, the separator is not added anyway
2743* skip_empty - skip empty lines without adding a separator
2744*/
2745template<typename K, typename T, size_t I, bool tail, bool skip_empty>
2746struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
2747 using symb_type = K;
2748 using my_type = expr_join<K, T, I, tail, skip_empty>;
2749
2750 const T& s;
2751 const K* delim;
2752 constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
2753
2754 constexpr size_t length() const noexcept {
2755 size_t l = 0;
2756 for (const auto& t: s) {
2757 size_t len = t.length();
2758 if (len > 0 || !skip_empty) {
2759 if (I > 0 && l > 0) {
2760 l += I;
2761 }
2762 l += len;
2763 }
2764 }
2765 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
2766 }
2767 constexpr K* place(K* ptr) const noexcept {
2768 if (s.empty()) {
2769 return ptr;
2770 }
2771 K* write = ptr;
2772 for (const auto& t: s) {
2773 size_t copyLen = t.length();
2774 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
2775 ch_traits<K>::copy(write, delim, I);
2776 write += I;
2777 }
2778 ch_traits<K>::copy(write, t.data(), copyLen);
2779 write += copyLen;
2780 }
2781 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
2782 ch_traits<K>::copy(write, delim, I);
2783 write += I;
2784 }
2785 return write;
2786 }
2787};
2788
2802template<bool tail = false, bool skip_empty = false, typename L, typename K = typename const_lit<L>::symb_type, size_t I = const_lit<L>::Count, typename T>
2803inline constexpr auto e_join(const T& s, L&& d) {
2804 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
2805}
2806
2807template<size_t N>
2808concept is_const_pattern = N > 1 && N <= 17;
2809
2810template<typename K, size_t I>
2811struct _ascii_mask { // NOLINT
2812 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
2813};
2814
2815template<typename K>
2816struct _ascii_mask<K, 0> {
2817 constexpr static const size_t value = 0;
2818};
2819
2820template<typename K>
2821struct ascii_mask { // NOLINT
2822 using uns = std::make_unsigned_t<K>;
2823 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
2824 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
2825};
2826
2827template<typename K>
2828constexpr inline bool isAsciiUpper(K k) {
2829 return k >= 'A' && k <= 'Z';
2830}
2831
2832template<typename K>
2833constexpr inline bool isAsciiLower(K k) {
2834 return k >= 'a' && k <= 'z';
2835}
2836
2837template<typename K>
2838constexpr inline K makeAsciiLower(K k) {
2839 return isAsciiUpper(k) ? k | 0x20 : k;
2840}
2841
2842template<typename K>
2843constexpr inline K makeAsciiUpper(K k) {
2844 return isAsciiLower(k) ? k & ~0x20 : k;
2845}
2846
2847enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
2848template<TrimSides S, typename K, size_t N, bool withSpaces = false>
2849struct trim_operator;
2850
2851template<size_t I>
2852struct digits_selector {
2853 using wider_type = uint16_t;
2854};
2855
2856template<>
2857struct digits_selector<2> {
2858 using wider_type = uint32_t;
2859};
2860
2861template<>
2862struct digits_selector<4> {
2863 using wider_type = uint64_t;
2864};
2865
2876
2877template<bool CanNegate, bool CheckOverflow, typename T>
2878struct result_type_selector { // NOLINT
2879 using type = T;
2880};
2881
2882template<typename T>
2883struct result_type_selector<true, false, T> {
2884 using type = std::make_unsigned_t<T>;
2885};
2886
2887template<unsigned Base>
2888constexpr unsigned digit_width() {
2889 if (Base <=2) {
2890 return 1;
2891 }
2892 if (Base <= 4) {
2893 return 2;
2894 }
2895 if (Base <= 8) {
2896 return 3;
2897 }
2898 if (Base <= 16) {
2899 return 4;
2900 }
2901 if (Base <= 32) {
2902 return 5;
2903 }
2904 return 6;
2905}
2906
2907template<typename T, unsigned Base>
2908constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
2909
2910template<typename T>
2911struct convert_result {
2912 T value;
2914 size_t read;
2915};
2916
2917struct int_convert { // NOLINT
2918 inline static constexpr uint8_t NUMBERS[] = {
2919 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2920 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
2921 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
2922 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
2923 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
2924 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2925 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2926 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2927 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2928 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
2929
2930 template<typename K, unsigned Base>
2931 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2932 auto us = static_cast<std::make_unsigned_t<K>>(s);
2933 if constexpr (Base <= 10) {
2934 return us - '0';
2935 } else {
2936 if constexpr (sizeof(K) == 1) {
2937 return NUMBERS[us];
2938 } else {
2939 return us < 256 ? NUMBERS[us] : us;
2940 }
2941 }
2942 }
2943
2944 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
2945 requires(Base != 0)
2946 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
2947 using u_type = std::make_unsigned_t<T>;
2948 #ifndef HAS_BUILTIN_OVERFLOW
2949 u_type maxMult = 0, maxAdd = 0;
2950 if constexpr (CheckOverflow) {
2951 maxMult = std::numeric_limits<u_type>::max() / Base;
2952 maxAdd = std::numeric_limits<u_type>::max() % Base;
2953 }
2954 #endif
2955 u_type number = 0;
2956 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2958 const K* from = current;
2959
2960 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2961
2962 if (no_need_check_o_f) {
2963 for (;;) {
2964 const u_type digit = toDigit<K, Base>(*current);
2965 if (digit >= Base) {
2966 break;
2967 }
2968 number = number * Base + digit;
2969 if (++current == end) {
2971 break;
2972 }
2973 }
2974 } else {
2975 for (;maxDigits; maxDigits--) {
2976 const u_type digit = toDigit<K, Base>(*current);
2977 if (digit >= Base) {
2978 break;
2979 }
2980 number = number * Base + digit;
2981 ++current;
2982 }
2983 if (!maxDigits) {
2984 // Прошли все цифры, дальше надо с проверкой на overflow
2985 // All numbers have passed, then we need to check for overflow
2986 for (;;) {
2987 const u_type digit = toDigit<K, Base>(*current);
2988 if (digit >= Base) {
2989 break;
2990 }
2991 #ifdef HAS_BUILTIN_OVERFLOW
2992 if (__builtin_mul_overflow(number, Base, &number) ||
2993 __builtin_add_overflow(number, digit, &number)) {
2994 #else
2995 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2996 number = number * Base + digit;
2997 } else {
2998 #endif
3000 while(++current < end) {
3001 if (toDigit<K, Base>(*current) >= Base) {
3002 break;
3003 }
3004 }
3005 break;
3006 }
3007 if (++current == end) {
3009 break;
3010 }
3011 }
3012 }
3013 }
3014 T result;
3015 if constexpr (std::is_signed_v<T>) {
3016 result = negate ? 0 - number : number;
3017 if constexpr (CheckOverflow) {
3018 if (error != IntConvertResult::Overflow) {
3019 if (number > (u_type)std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
3021 }
3022 }
3023 }
3024 } else {
3025 result = number;
3026 }
3027 if (error == IntConvertResult::NotNumber && current > from) {
3029 }
3030 return {result, error, size_t(current - start)};
3031 }
3032public:
3033 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
3034 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
3035 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
3036 // If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
3037 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
3038 requires(Base == -1 || (Base < 37 && Base != 1))
3039 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
3040 const K *ptr = start, *end = ptr + len;
3041 bool negate = false;
3042 if constexpr (SkipWs) {
3043 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
3044 ptr++;
3045 }
3046 if (ptr != end) {
3047 if constexpr (std::is_signed_v<T>) {
3048 if constexpr (AllowSign) {
3049 // Может быть число, +число или -число
3050 // Can be a number, +number or -number
3051 if (*ptr == '+') {
3052 ptr++;
3053 } else if (*ptr == '-') {
3054 negate = true;
3055 ptr++;
3056 }
3057 } else {
3058 // Может быть число или -число
3059 // Can be a number or -number
3060 if (*ptr == '-') {
3061 negate = true;
3062 ptr++;
3063 }
3064 }
3065 } else if constexpr (AllowSign) {
3066 // Может быть число или +число
3067 // Can be a number or +number
3068 if (*ptr == '+') {
3069 ptr++;
3070 }
3071 }
3072 }
3073 if (ptr != end) {
3074 if constexpr (Base == 0 || Base == -1) {
3075 if (*ptr == '0') {
3076 ptr++;
3077 if (ptr != end) {
3078 if (*ptr == 'x' || *ptr == 'X') {
3079 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
3080 }
3081 if constexpr (Base == -1) {
3082 if (*ptr == 'b' || *ptr == 'B') {
3083 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
3084 }
3085 if (*ptr == 'o' || *ptr == 'O') {
3086 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
3087 }
3088 }
3089 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
3090 }
3091 return {0, IntConvertResult::Success, size_t(ptr - start)};
3092 }
3093 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
3094 } else
3095 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
3096 }
3097 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
3098 }
3099};
3100
3101template<typename K, typename Impl>
3102class null_terminated {
3103public:
3110 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
3111};
3112
3113template<typename K, typename Impl, bool Mutable> class buffer_pointers;
3114
3123template<typename K, typename Impl>
3124class buffer_pointers<K, Impl, false> {
3125 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
3126public:
3133 constexpr const K* data() const { return d().symbols(); }
3140 constexpr const K* begin() const { return d().symbols(); }
3147 constexpr const K* end() const { return d().symbols() + d().length(); }
3154 constexpr const K* cbegin() const { return d().symbols(); }
3161 constexpr const K* cend() const { return d().symbols() + d().length(); }
3162};
3163
3164template<typename K, typename Impl>
3165class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
3166 constexpr Impl& d() { return *static_cast<Impl*>(this); }
3167 using base = buffer_pointers<K, Impl, false>;
3168public:
3175 constexpr const K* data() const { return base::data(); }
3182 constexpr const K* begin() const { return base::begin(); }
3189 constexpr const K* end() const { return base::end(); }
3196 constexpr const K* cbegin() const { return base::cbegin(); }
3203 constexpr const K* cend() const { return base::cend(); }
3210 constexpr K* data() { return d().str(); }
3217 constexpr K* begin() { return d().str(); }
3224 constexpr K* end() { return d().str() + d().length(); }
3225};
3226
3233template<typename K, typename StrSrc>
3234class SplitterBase {
3235 using str_t = StrSrc;
3236 str_t text_;
3237 str_t delim_;
3238
3239public:
3240 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
3245 constexpr bool is_done() const {
3246 return text_.length() == str::npos;
3247 }
3248
3254 constexpr str_t next() {
3255 if (!text_.length()) {
3256 auto ret = text_;
3257 text_.str++;
3258 text_.len--;
3259 return ret;
3260 } else if (text_.length() == str::npos) {
3261 return {nullptr, 0};
3262 }
3263 size_t pos = text_.find(delim_), next = 0;
3264 if (pos == str::npos) {
3265 pos = text_.length();
3266 next = pos + 1;
3267 } else {
3268 next = pos + delim_.length();
3269 }
3270 str_t result{text_.str, pos};
3271 text_.str += next;
3272 text_.len -= next;
3273 return result;
3274 }
3275};
3276
3277struct from_begin {
3278 size_t idx_;
3279};
3280struct from_end {
3281 size_t idx_;
3282};
3283inline constexpr from_end to_end{0};
3284
3285template<typename T>
3286concept first_index_in_str = std::is_same_v<T, from_end> || std::is_convertible_v<T, size_t>;
3287
3288template<typename T>
3289concept last_index_in_str = first_index_in_str<T> || std::is_same_v<T, from_begin>;
3290
3315template<typename K, typename StrRef, typename Impl, bool Mutable>
3316class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
3317 constexpr const Impl& d() const noexcept {
3318 return *static_cast<const Impl*>(this);
3319 }
3320 constexpr size_t _len() const noexcept {
3321 return d().length();
3322 }
3323 constexpr const K* _str() const noexcept {
3324 return d().symbols();
3325 }
3326 constexpr bool _is_empty() const noexcept {
3327 return d().is_empty();
3328 }
3329
3330public:
3331 using symb_type = K;
3332 using str_piece = StrRef;
3333 using traits = ch_traits<K>;
3334 using uns_type = std::make_unsigned_t<K>;
3335 using my_type = Impl;
3336 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3337 str_src_algs() = default;
3338
3351 constexpr K* place(K* ptr) const noexcept {
3352 size_t myLen = _len();
3353 traits::copy(ptr, _str(), myLen);
3354 return ptr + myLen;
3355 }
3356
3366 void copy_to(K* buffer, size_t bufSize) {
3367 size_t tlen = std::min(_len(), bufSize - 1);
3368 traits::copy(buffer, _str(), tlen);
3369 buffer[tlen] = 0;
3370 }
3371
3377 constexpr size_t size() const {
3378 return _len();
3379 }
3380
3387 template<typename D = K> requires is_equal_str_type_v<K, D>
3388 constexpr std::basic_string_view<D> to_sv() const noexcept {
3389 return {(const D*)_str(), _len()};
3390 }
3391
3397 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
3398 constexpr operator std::basic_string_view<D, Traits>() const {
3399 return {(const D*)_str(), _len()};
3400 }
3401
3407 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
3408 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
3409 return {(const D*)_str(), _len()};
3410 }
3411
3417 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
3418 constexpr operator std::basic_string<D, Traits, Allocator>() const {
3419 return {(const D*)_str(), _len()};
3420 }
3421
3427 constexpr operator str_piece() const noexcept {
3428 return str_piece{_str(), _len()};
3429 }
3430
3436 constexpr str_piece to_str() const noexcept {
3437 return {_str(), _len()};
3438 }
3439
3462 [[deprecated("Use 'get' or [start, end] in C++23")]]
3463 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3464 size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
3465 idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
3466 if (idxEnd > myLen)
3467 idxEnd = myLen;
3468 if (idxStart > idxEnd)
3469 idxStart = idxEnd;
3470 return str_piece{_str() + idxStart, idxEnd - idxStart};
3471 }
3472
3504 template<first_index_in_str S, last_index_in_str E>
3505 constexpr str_piece get(S start, E end) const noexcept {
3506 size_t idxStart, idxEnd, len = _len();
3507 if constexpr (std::is_same_v<S, from_end>) {
3508 idxStart = start.idx_ > len ? 0 : len - start.idx_;
3509 } else {
3510 idxStart = std::min(size_t(start), len);
3511 }
3512
3513 if constexpr (std::is_same_v<E, from_begin>) {
3514 idxEnd = std::max(idxStart, std::min(end.idx_, len));
3515 } else if constexpr (std::is_same_v<E, from_end>) {
3516 idxEnd = std::max(idxStart, end.idx_ > len ? 0 : len - end.idx_);
3517 } else {
3518 size_t e = end;
3519 idxEnd = len - idxStart < e ? len : idxStart + e;
3520 }
3521
3522 return str_piece{_str() + idxStart, idxEnd - idxStart};
3523 }
3524
3525#if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202211L
3530 template<first_index_in_str S, last_index_in_str E>
3531 constexpr str_piece operator[](S start, E end) const noexcept {
3532 return get(start, end);
3533 }
3534#endif
3539 constexpr str_piece prefix(size_t count) const noexcept {
3540 return get(0, count);
3541 }
3542
3546 constexpr str_piece suffix(size_t count) const noexcept {
3547 return get(from_end(count), to_end);
3548 }
3549
3559 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
3560 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3561 if (idxEnd > myLen)
3562 idxEnd = myLen;
3563 if (idxStart > idxEnd)
3564 idxStart = idxEnd;
3565 return str_piece{_str() + idxStart, idxEnd - idxStart};
3566 }
3567
3581 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
3582 return str_piece{_str() + from, to - from};
3583 }
3584
3594 constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
3595 return get(0, find_or_all(pattern, offset));
3596 }
3597
3601 constexpr bool operator!() const noexcept {
3602 return _is_empty();
3603 }
3604
3614 constexpr K at(ptrdiff_t idx) const {
3615 return _str()[idx >= 0 ? idx : _len() + idx];
3616 }
3617 // Сравнение строк
3618 // String comparison
3619 constexpr int compare(const K* text, size_t len) const {
3620 size_t myLen = _len();
3621 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3622 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3623 }
3632 constexpr int compare(str_piece o) const {
3633 return compare(o.symbols(), o.length());
3634 }
3635
3643 constexpr int strcmp(const K* text) const {
3644 size_t myLen = _len(), idx = 0;
3645 const K* ptr = _str();
3646 for (; idx < myLen; idx++) {
3647 uns_type s1 = (uns_type)text[idx];
3648 if (!s1) {
3649 return 1;
3650 }
3651 uns_type s2 = (uns_type)ptr[idx];
3652 if (s1 < s2) {
3653 return 1;
3654 } else if (s1 > s2) {
3655 return -1;
3656 }
3657 }
3658 return text[idx] == 0 ? 0 : -1;
3659 }
3660
3661 constexpr bool equal(const K* text, size_t len) const noexcept {
3662 return len == _len() && traits::compare(_str(), text, len) == 0;
3663 }
3672 constexpr bool equal(str_piece other) const noexcept {
3673 return equal(other.symbols(), other.length());
3674 }
3675
3683 constexpr bool operator==(const base& other) const noexcept {
3684 return equal(other._str(), other._len());
3685 }
3686
3692 constexpr auto operator<=>(const base& other) const noexcept {
3693 return compare(other._str(), other._len()) <=> 0;
3694 }
3695
3701 template<typename T, size_t N = const_lit_for<K, T>::Count>
3702 constexpr bool operator==(T&& other) const noexcept {
3703 return N - 1 == _len() && traits::compare(_str(), (const K*)other, N - 1) == 0;
3704 }
3705
3711 template<typename T, size_t N = const_lit_for<K, T>::Count>
3712 constexpr auto operator<=>(T&& other) const noexcept {
3713 size_t myLen = _len();
3714 int cmp = traits::compare(_str(), (const K*)other, std::min(myLen, N - 1));
3715 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3716 return res <=> 0;
3717 }
3718
3719 // Сравнение ascii строк без учёта регистра
3720 // Compare ascii strings without taking into account case
3721 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
3722 if (!len)
3723 return _is_empty() ? 0 : 1;
3724 size_t myLen = _len(), checkLen = std::min(myLen, len);
3725 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
3726 while (checkLen--) {
3727 uns_type s1 = *ptr1++, s2 = *ptr2++;
3728 if (s1 == s2)
3729 continue;
3730 s1 = makeAsciiLower(s1);
3731 s2 = makeAsciiLower(s2);
3732 if (s1 > s2)
3733 return 1;
3734 else if (s1 < s2)
3735 return -1;
3736 }
3737 return myLen == len ? 0 : myLen > len ? 1 : -1;
3738 }
3747 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
3748 return compare_ia(text.symbols(), text.length());
3749 }
3750
3759 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
3760 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
3761 }
3762
3770 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
3771 return compare_ia(text.symbols(), text.length()) < 0;
3772 }
3773
3774 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3775 size_t lenText = _len();
3776 // Образец, не вмещающийся в строку и пустой образец не находим
3777 // We don't look for an empty string or a string longer than the text.
3778 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3779 return str::npos;
3780 lenPattern--;
3781 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3782 pattern++;
3783 for (const K* fnd = text + offset;; ++fnd) {
3784 fnd = traits::find(fnd, last - fnd, first);
3785 if (!fnd)
3786 return str::npos;
3787 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3788 return static_cast<size_t>(fnd - text);
3789 }
3790 }
3801 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
3802 return find(pattern.symbols(), pattern.length(), offset);
3803 }
3804
3820 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
3821 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
3822 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3823 return fnd;
3824 }
3825 throw Exc(std::forward<Args>(args)...);
3826 }
3827
3837 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
3838 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3839 return fnd == str::npos ? fnd : fnd + pattern.length();
3840 }
3841
3851 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3852 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3853 return fnd == str::npos ? _len() : fnd;
3854 }
3855
3865 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3866 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3867 return fnd == str::npos ? _len() : fnd + pattern.length();
3868 }
3869
3870 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3871 if (lenPattern == 1)
3872 return find_last(pattern[0], offset);
3873 size_t lenText = std::min(_len(), offset);
3874 // Образец, не вмещающийся в строку и пустой образец не находим
3875 // We don't look for an empty string or a string longer than the text.
3876 if (!lenPattern || lenPattern > lenText)
3877 return str::npos;
3878
3879 lenPattern--;
3880 const K *text = _str() + lenPattern, last = pattern[lenPattern];
3881 lenText -= lenPattern;
3882 while(lenText) {
3883 if (text[--lenText] == last) {
3884 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
3885 return lenText;
3886 }
3887 }
3888 }
3889 return str::npos;
3890 }
3901 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
3902 return find_last(pattern.symbols(), pattern.length(), offset);
3903 }
3904
3914 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
3915 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3916 return fnd == str::npos ? fnd : fnd + pattern.length();
3917 }
3918
3928 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3929 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
3930 return fnd == str::npos ? _len() : fnd;
3931 }
3932
3942 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3943 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3944 return fnd == str::npos ? _len() : fnd + pattern.length();
3945 }
3946
3956 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
3957 return find(pattern, offset) != str::npos;
3958 }
3959
3969 constexpr size_t find(K s, size_t offset = 0) const noexcept {
3970 size_t len = _len();
3971 if (offset < len) {
3972 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3973 if (fnd)
3974 return static_cast<size_t>(fnd - str);
3975 }
3976 return str::npos;
3977 }
3978
3988 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
3989 size_t len = _len();
3990 if (offset < len) {
3991 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3992 if (fnd)
3993 return static_cast<size_t>(fnd - str);
3994 }
3995 return len;
3996 }
3997
3998 template<typename Op>
3999 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
4000 if (!maxCount)
4001 maxCount--;
4002 while (maxCount-- > 0) {
4003 size_t fnd = find(pattern, patternLen, offset);
4004 if (fnd == str::npos)
4005 break;
4006 op(fnd);
4007 offset = fnd + patternLen;
4008 }
4009 }
4022 template<typename Op>
4023 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4024 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
4025 }
4026
4027 template<typename To = std::vector<size_t>>
4028 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
4029 To result;
4030 for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
4031 return result;
4032 }
4045 template<typename To = std::vector<size_t>>
4046 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
4047 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
4048 }
4049 template<typename To = std::vector<size_t>>
4050 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
4051 return for_all_finded([&](size_t pos) {
4052 to.emplace_back(pos);
4053 }, pattern, len, offset, maxCount);
4054 }
4065 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
4066 size_t len = std::min(_len(), offset);
4067 const K *text = _str();
4068 while (len > 0) {
4069 if (text[--len] == s)
4070 return len;
4071 }
4072 return str::npos;
4073 }
4074
4084 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
4085 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4086 }
4087
4097 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
4098 const K* text = _str();
4099 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4100 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4101 }
4102
4112 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
4113 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4114 }
4115
4125 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4126 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4127 }
4128
4138 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
4139 const K* text = _str();
4140 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4141 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4142 }
4143
4153 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4154 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4155 }
4156
4166 [[deprecated("Use sub(start, end)")]]
4167 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
4168 return my_type{d()(from, len)};
4169 }
4170 template<first_index_in_str S, last_index_in_str E>
4171 constexpr my_type sub(S start, E end) const noexcept {
4172 return my_type{get(start, end)};
4173 }
4184 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
4185 return my_type{d().mid(from, len)};
4186 }
4187
4215 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4216 constexpr T as_int() const noexcept {
4217 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4218 return err == IntConvertResult::Overflow ? 0 : res;
4219 }
4220
4248 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4249 constexpr convert_result<T> to_int() const noexcept {
4250 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4251 }
4252
4258 template<bool SkipWS = true, bool AllowPlus = true>
4259 requires(sizeof(K) == 1 &&
4260 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4261 std::optional<double> to_double() const noexcept {
4262 size_t len = _len();
4263 const K* ptr = _str();
4264 if constexpr (SkipWS) {
4265 while (len && uns_type(*ptr) <= ' ') {
4266 len--;
4267 ptr++;
4268 }
4269 }
4270 if constexpr (AllowPlus) {
4271 if (len && *ptr == K('+')) {
4272 ptr++;
4273 len--;
4274 }
4275 }
4276 if (!len) {
4277 return {};
4278 }
4279 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4280 double d{};
4281 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
4282 return d;
4283 }
4284 }
4285 return {};
4286 }
4287
4293 template<bool SkipWS = true>
4294 requires(sizeof(K) == 1 &&
4295 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4296 std::optional<double> to_double_hex() const noexcept {
4297 size_t len = _len();
4298 const K* ptr = _str();
4299 if constexpr (SkipWS) {
4300 while (len && uns_type(*ptr) <= ' ') {
4301 len--;
4302 ptr++;
4303 }
4304 }
4305 if (len) {
4306 if constexpr (requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4307 double d{};
4308 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4309 return d;
4310 }
4311 }
4312 }
4313 return {};
4314 }
4315
4323 template<ToIntNumber T>
4324 constexpr void as_number(T& t) const {
4325 t = as_int<T>();
4326 }
4327
4328 template<typename T, typename Op>
4329 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
4330 size_t mylen = _len();
4331 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4332 str_piece me{_str(), mylen};
4333 for (size_t i = 0;; i++) {
4334 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4335 if (beginOfDelim == str::npos) {
4336 str_piece last{me.symbols() + offset, me.length() - offset};
4337 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4338 beforeFunc(last);
4339 }
4340 if constexpr (requires { results.emplace_back(last); }) {
4341 if (last.is_same(me)) {
4342 // Пробуем положить весь объект.
4343 // Try to put the entire object.
4344 results.emplace_back(d());
4345 } else {
4346 results.emplace_back(last);
4347 }
4348 } else if constexpr (requires { results.push_back(last); }) {
4349 if (last.is_same(me)) {
4350 // Пробуем положить весь объект.
4351 // Try to put the entire object.
4352 results.push_back(d());
4353 } else {
4354 results.push_back(last);
4355 }
4356 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
4357 if (i < std::size(results)) {
4358 if (last.is_same(me)) {
4359 // Пробуем положить весь объект.
4360 // Try to put the entire object.
4361 results[i] = d();
4362 } else
4363 results[i] = last;
4364 }
4365 }
4366 break;
4367 }
4368 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4369 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4370 beforeFunc(piece);
4371 }
4372 if constexpr (requires { results.emplace_back(piece); }) {
4373 results.emplace_back(piece);
4374 } else if constexpr (requires { results.push_back(piece); }) {
4375 results.push_back(piece);
4376 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
4377 if (i < std::size(results)) {
4378 results[i] = piece;
4379 if (i == results.size() - 1) {
4380 break;
4381 }
4382 }
4383 }
4384 offset = beginOfDelim + lendelimiter;
4385 }
4386 if constexpr (!std::is_same_v<T, void>) {
4387 return results;
4388 }
4389 }
4420 template<typename T, typename Op>
4421 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
4422 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4423 }
4424
4436 template<typename T>
4437 constexpr T split(str_piece delimiter, size_t offset = 0) const {
4438 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4439 }
4440
4441 // Начинается ли эта строка с указанной подстроки
4442 // Does this string start with the specified substring
4443 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
4444 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4445 }
4452 constexpr bool starts_with(str_piece prefix) const noexcept {
4453 return starts_with(prefix.symbols(), prefix.length());
4454 }
4455
4461 constexpr bool starts_with_and_ws(str_piece prefix) const noexcept {
4462 return _len() > prefix.length() &&
4463 starts_with(prefix) &&
4464 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4465 }
4466
4474 constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4475 return _len() > prefix.length() &&
4476 starts_with(prefix) &&
4477 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4478 }
4479 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4480 constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4481 return _len() >= N &&
4482 starts_with(prefix) &&
4483 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4484 }
4485
4486 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
4487 size_t myLen = _len();
4488 if (myLen < len) {
4489 return false;
4490 }
4491 const K* ptr1 = _str();
4492 while (len--) {
4493 K s1 = *ptr1++, s2 = *prefix++;
4494 if (s1 == s2)
4495 continue;
4496 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4497 return false;
4498 }
4499 return true;
4500 }
4507 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
4508 return starts_with_ia(prefix.symbols(), prefix.length());
4509 }
4510
4516 constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept {
4517 return _len() > prefix.length() &&
4518 starts_with_ia(prefix) &&
4519 trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
4520 }
4521
4529 constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
4530 return _len() > prefix.length() &&
4531 starts_with_ia(prefix) &&
4532 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4533 }
4534 template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
4535 constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
4536 return _len() >= N &&
4537 starts_with_ia(prefix) &&
4538 trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
4539 }
4540
4541 // Является ли эта строка началом указанной строки
4542 // Is this string the beginning of the specified string
4543 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
4544 size_t myLen = _len();
4545 if (myLen > len)
4546 return false;
4547 return !myLen || 0 == traits::compare(text, _str(), myLen);
4548 }
4555 constexpr bool prefix_in(str_piece text) const noexcept {
4556 return prefix_in(text.symbols(), text.length());
4557 }
4558 // Заканчивается ли строка указанной подстрокой
4559 // Does the string end with the specified substring
4560 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
4561 size_t myLen = _len();
4562 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4563 }
4570 constexpr bool ends_with(str_piece suffix) const noexcept {
4571 return ends_with(suffix.symbols(), suffix.length());
4572 }
4573 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
4574 // Whether the string ends with the specified substring, case insensitive ASCII
4575 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
4576 size_t myLen = _len();
4577 if (myLen < len) {
4578 return false;
4579 }
4580 const K* ptr1 = _str() + myLen - len;
4581 while (len--) {
4582 K s1 = *ptr1++, s2 = *suffix++;
4583 if (s1 == s2)
4584 continue;
4585 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4586 return false;
4587 }
4588 return true;
4589 }
4596 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
4597 return ends_with_ia(suffix.symbols(), suffix.length());
4598 }
4599
4603 constexpr bool is_ascii() const noexcept {
4604 if (_is_empty())
4605 return true;
4606 if (std::is_constant_evaluated()) {
4607 for (size_t idx = 0; idx < _len(); idx++) {
4608 if (uns_type(_str()[idx]) > 127) {
4609 return false;
4610 }
4611 }
4612 return true;
4613 }
4614 const int sl = ascii_mask<K>::WIDTH;
4615 const size_t mask = ascii_mask<K>::VALUE;
4616 size_t len = _len();
4617 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
4618 if constexpr (sl > 1) {
4619 const size_t roundMask = sizeof(size_t) - 1;
4620 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4621 if (*ptr++ > 127)
4622 return false;
4623 len--;
4624 }
4625 while (len >= sl) {
4626 if (*reinterpret_cast<const size_t*>(ptr) & mask)
4627 return false;
4628 ptr += sl;
4629 len -= sl;
4630 }
4631 }
4632 while (len--) {
4633 if (*ptr++ > 127)
4634 return false;
4635 }
4636 return true;
4637 }
4638
4646 template<typename R = my_type>
4648 return R::upperred_only_ascii_from(d());
4649 }
4650
4658 template<typename R = my_type>
4660 return R::lowered_only_ascii_from(d());
4661 }
4662
4678 template<typename R = my_type>
4679 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
4680 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4681 }
4682
4694 template<typename R = str_piece>
4695 constexpr std::optional<R> strip_prefix(str_piece prefix) const {
4696 if (starts_with(prefix)) {
4697 return R{get(prefix.length(), to_end)};
4698 }
4699 return {};
4700 }
4701
4713 template<typename R = str_piece>
4714 constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
4715 if (starts_with_ia(prefix)) {
4716 return R{get(prefix.length(), to_end)};
4717 }
4718 return {};
4719 }
4720
4732 template<typename R = str_piece>
4733 constexpr std::optional<R> strip_suffix(str_piece suffix) const {
4734 if (ends_with(suffix)) {
4735 return R{get(0, from_end{suffix.length()})};
4736 }
4737 return {};
4738 }
4739
4751 template<typename R = str_piece>
4752 constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
4753 if (ends_with_ia(suffix)) {
4754 return R{get(0, from_end{suffix.length()})};
4755 }
4756 return {};
4757 }
4758
4759 template<StrType<K> From>
4760 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
4761 str_piece sfrom = from, newPos = opTrim(sfrom);
4762 if (newPos.is_same(sfrom)) {
4763 my_type res = from;
4764 return res;
4765 }
4766 return my_type{newPos};
4767 }
4768 template<TrimSides S, StrType<K> From>
4769 constexpr static my_type trim_static(const From& from) {
4770 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
4771 }
4772
4773 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
4774 requires is_const_pattern<N>
4775 constexpr static my_type trim_static(const From& from, T&& pattern) {
4776 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
4777 }
4778
4779 template<TrimSides S, bool withSpaces, StrType<K> From>
4780 constexpr static my_type trim_static(const From& from, str_piece pattern) {
4781 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
4782 }
4791 template<typename R = str_piece>
4792 constexpr R trimmed() const {
4793 return R::template trim_static<TrimSides::TrimAll>(d());
4794 }
4795
4803 template<typename R = str_piece>
4804 constexpr R trimmed_left() const {
4805 return R::template trim_static<TrimSides::TrimLeft>(d());
4806 }
4807
4815 template<typename R = str_piece>
4816 constexpr R trimmed_right() const {
4817 return R::template trim_static<TrimSides::TrimRight>(d());
4818 }
4819
4829 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4830 requires is_const_pattern<N>
4831 constexpr R trimmed(T&& pattern) const {
4832 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4833 }
4834
4844 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4845 requires is_const_pattern<N>
4846 constexpr R trimmed_left(T&& pattern) const {
4847 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4848 }
4849
4859 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4860 requires is_const_pattern<N>
4861 constexpr R trimmed_right(T&& pattern) const {
4862 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4863 }
4864 // Триминг по символам в литерале и пробелам
4865 // Trimming by characters in literal and spaces
4866
4881 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4882 requires is_const_pattern<N>
4883 constexpr R trimmed_with_spaces(T&& pattern) const {
4884 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4885 }
4886
4900 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4901 requires is_const_pattern<N>
4902 constexpr R trimmed_left_with_spaces(T&& pattern) const {
4903 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4904 }
4905
4919 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4920 requires is_const_pattern<N>
4921 constexpr R trimmed_right_with_spaces(T&& pattern) const {
4922 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4923 }
4924 // Триминг по динамическому источнику
4925 // Trimming by dynamic source
4926
4937 template<typename R = str_piece>
4938 constexpr R trimmed(str_piece pattern) const {
4939 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4940 }
4941
4951 template<typename R = str_piece>
4952 constexpr R trimmed_left(str_piece pattern) const {
4953 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4954 }
4955
4965 template<typename R = str_piece>
4966 constexpr R trimmed_right(str_piece pattern) const {
4967 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4968 }
4969
4983 template<typename R = str_piece>
4984 constexpr R trimmed_with_spaces(str_piece pattern) const {
4985 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4986 }
4987
5001 template<typename R = str_piece>
5002 constexpr R trimmed_left_with_spaces(str_piece pattern) const {
5003 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5004 }
5005
5019 template<typename R = str_piece>
5020 constexpr R trimmed_right_with_spaces(str_piece pattern) const {
5021 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5022 }
5023
5035 template<typename R = str_piece>
5036 constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
5037 str_piece res = *this;
5038 while(res.starts_with(prefix)) {
5039 res.remove_prefix(prefix.length());
5040 if (--max_count == 0) {
5041 break;
5042 }
5043 }
5044 return res;
5045 }
5046
5058 template<typename R = str_piece>
5059 constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
5060 str_piece res = *this;
5061 while(res.starts_with_ia(prefix)) {
5062 res.remove_prefix(prefix.length());
5063 if (--max_count == 0) {
5064 break;
5065 }
5066 }
5067 return res;
5068 }
5069
5081 template<typename R = str_piece>
5082 constexpr R trimmed_suffix(str_piece suffix) const {
5083 str_piece res = *this;
5084 while(res.ends_with(suffix)) {
5085 res.remove_suffix(suffix.length());
5086 }
5087 return res;
5088 }
5089
5101 template<typename R = str_piece>
5102 constexpr R trimmed_suffix_ia(str_piece suffix) const {
5103 str_piece res = *this;
5104 while(res.ends_with_ia(suffix)) {
5105 res.remove_suffix(suffix.length());
5106 }
5107 return res;
5108 }
5109
5119 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
5120 return SplitterBase<K, str_piece>{*this, delimiter};
5121 }
5122};
5123
5124template<size_t N> requires (N > 1)
5125struct find_all_container {
5126 static constexpr size_t max_capacity = N;
5127 size_t positions_[N];
5128 size_t added_{};
5129
5130 constexpr void emplace_back(size_t pos) {
5131 positions_[added_++] = pos;
5132 }
5133};
5134
5153template<typename K>
5154struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
5155 using symb_type = K;
5156 using my_type = str_src<K>;
5157
5158 const symb_type* str;
5159 size_t len;
5160
5161 str_src() = default;
5166 template<typename T, size_t N = const_lit_for<K, T>::Count>
5167 constexpr str_src(T&& v) noexcept : str((const K*)v), len(N - 1) {}
5168
5173 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
5174
5175 template<StrType<K> T>
5176 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
5177
5182 template<typename A>
5183 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
5188 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
5189
5194 constexpr size_t length() const noexcept {
5195 return len;
5196 }
5197
5201 constexpr const symb_type* symbols() const noexcept {
5202 return str;
5203 }
5204
5208 constexpr bool is_empty() const noexcept {
5209 return len == 0;
5210 }
5211
5217 constexpr bool is_same(str_src<K> other) const noexcept {
5218 return str == other.str && len == other.len;
5219 }
5220
5226 constexpr bool is_part_of(str_src<K> other) const noexcept {
5227 return str >= other.str && str + len <= other.str + other.len;
5228 }
5229
5237 constexpr K operator[](size_t idx) const {
5238 return str[idx];
5239 }
5240
5248 constexpr my_type& remove_prefix(size_t delta) {
5249 str += delta;
5250 len -= delta;
5251 return *this;
5252 }
5253
5261 constexpr my_type& remove_suffix(size_t delta) {
5262 len -= delta;
5263 return *this;
5264 }
5265};
5266
5289template<typename K>
5290struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
5291 using symb_type = K;
5292 using my_type = str_src_nt<K>;
5293 using base = str_src<K>;
5294
5295 constexpr static const K empty_string[1] = {0};
5296
5297 str_src_nt() = default;
5314 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
5315 constexpr explicit str_src_nt(T&& p) noexcept {
5316 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
5317 base::str = base::len ? p : empty_string;
5318 }
5319
5323 template<typename T, size_t N = const_lit_for<K, T>::Count>
5324 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
5325
5330 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
5331
5332 template<StrType<K> T>
5333 constexpr str_src_nt(T&& t) {
5334 base::str = t.symbols();
5335 base::len = t.length();
5336 }
5341 template<typename A>
5342 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5343
5344 static const my_type empty_str;
5353 constexpr my_type to_nts(size_t from) {
5354 if (from > base::len) {
5355 from = base::len;
5356 }
5357 return {base::str + from, base::len - from};
5358 }
5359};
5360
5361template<typename K>
5362inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5363template<typename K> struct simple_str_selector;
5364
5365inline namespace literals {
5425template<char...Chars>
5426SS_CONSTEVAL auto operator""_fmt() {
5427 return f::skip_0x<Chars...>();
5428}
5429
5430} // namespace literals
5431
5432#ifndef IN_FULL_SIMSTR
5433
5434template<typename K>
5435using simple_str = str_src<K>;
5436
5437template<typename K>
5438struct simple_str_selector {
5439 using type = simple_str<K>;
5440};
5441
5442template<typename K>
5443using simple_str_nt = str_src_nt<K>;
5444
5445template<typename K>
5446using Splitter = SplitterBase<K, str_src<K>>;
5447
5448using ssa = str_src<u8s>;
5449using ssb = str_src<ubs>;
5450using ssw = str_src<wchar_t>;
5451using ssu = str_src<u16s>;
5452using ssuu = str_src<u32s>;
5453using stra = str_src_nt<u8s>;
5454using strb = str_src_nt<ubs>;
5455using strw = str_src_nt<wchar_t>;
5456using stru = str_src_nt<u16s>;
5457using struu = str_src_nt<u32s>;
5458
5459template<typename K>
5460consteval 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) {
5461 if constexpr (std::is_same_v<K, u8s>)
5462 return s8;
5463 if constexpr (std::is_same_v<K, ubs>)
5464 return sb;
5465 if constexpr (std::is_same_v<K, uws>)
5466 return sw;
5467 if constexpr (std::is_same_v<K, u16s>)
5468 return s16;
5469 if constexpr (std::is_same_v<K, u32s>)
5470 return s32;
5471}
5472
5473#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5474
5475inline namespace literals {
5476
5487SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
5488 return str_src_nt<u8s>{ptr, l};
5489}
5490
5500SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
5501 return str_src_nt<ubs>{ptr, l};
5502}
5503
5513SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
5514 return str_src_nt<uws>{ptr, l};
5515}
5516
5526SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
5527 return str_src_nt<u16s>{ptr, l};
5528}
5529
5540SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
5541 return str_src_nt<u32s>{ptr, l};
5542}
5543
5544} // namespace literals
5545
5546#endif
5547
5548template<typename K, bool withSpaces>
5549struct CheckSpaceTrim {
5550 constexpr bool is_trim_spaces(K s) const {
5551 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
5552 }
5553};
5554template<typename K>
5555struct CheckSpaceTrim<K, false> {
5556 constexpr bool is_trim_spaces(K) const {
5557 return false;
5558 }
5559};
5560
5561template<typename K>
5562struct CheckSymbolsTrim {
5563 str_src<K> symbols;
5564 constexpr bool is_trim_symbols(K s) const {
5565 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
5566 }
5567};
5568
5569template<typename K, size_t N>
5570struct CheckConstSymbolsTrim {
5571 const const_lit_to_array<K, N> symbols;
5572
5573 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
5574 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5575
5576 constexpr bool is_trim_symbols(K s) const noexcept {
5577 return symbols.contain(s);
5578 }
5579};
5580
5581template<typename K>
5582struct CheckConstSymbolsTrim<K, 0> {
5583 constexpr bool is_trim_symbols(K) const {
5584 return false;
5585 }
5586};
5587
5588template<typename K, size_t N>
5589struct SymbSelector {
5590 using type = CheckConstSymbolsTrim<K, N>;
5591};
5592
5593template<typename K>
5594struct SymbSelector<K, 0> {
5595 using type = CheckSymbolsTrim<K>;
5596};
5597
5598template<typename K>
5599struct SymbSelector<K, static_cast<size_t>(-1)> {
5600 using type = CheckConstSymbolsTrim<K, 0>;
5601};
5602
5603template<TrimSides S, typename K, size_t N, bool withSpaces>
5604struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5605 constexpr bool isTrim(K s) const {
5606 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5607 }
5608 constexpr str_src<K> operator()(str_src<K> from) const {
5609 if constexpr ((S & TrimSides::TrimLeft) != 0) {
5610 while (from.len) {
5611 if (isTrim(*from.str)) {
5612 from.str++;
5613 from.len--;
5614 } else
5615 break;
5616 }
5617 }
5618 if constexpr ((S & TrimSides::TrimRight) != 0) {
5619 const K* back = from.str + from.len - 1;
5620 while (from.len) {
5621 if (isTrim(*back)) {
5622 back--;
5623 from.len--;
5624 } else
5625 break;
5626 }
5627 }
5628 return from;
5629 }
5630};
5631
5632template<TrimSides S, typename K>
5633using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
5634
5635template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
5636 requires is_const_pattern<N>
5637constexpr inline auto trimOp(T&& pattern) {
5638 return trim_operator<S, K, N - 1, withSpaces>{pattern};
5639}
5640
5641template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
5642constexpr inline auto trimOp(str_src<K> pattern) {
5643 return trim_operator<S, K, 0, withSpaces>{pattern};
5644}
5645
5646static constexpr size_t FIND_CACHE_SIZE = 16;
5647
5648template<typename K, size_t N, size_t L>
5649struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
5650 using symb_type = K;
5651 using my_type = expr_replaces<K, N, L>;
5652 str_src<K> what;
5653 const K(&pattern)[N + 1];
5654 const K(&repl)[L + 1];
5655 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5656 mutable size_t last_;
5657
5658 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
5659
5660 constexpr size_t length() const {
5661 size_t l = what.length();
5662 if constexpr (N == L) {
5663 return l;
5664 }
5665 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
5666 if (matches_.added_) {
5667 last_ = matches_.positions_[matches_.added_ - 1] + N;
5668 l += int(L - N) * matches_.added_;
5669
5670 if (matches_.added_ == FIND_CACHE_SIZE) {
5671 for (;;) {
5672 size_t next = what.find(pattern, N, last_);
5673 if (next == str::npos) {
5674 break;
5675 }
5676 last_ = next + N;
5677 l += L - N;
5678 }
5679 }
5680 }
5681 if (!l) {
5682 matches_.added_ = -1;
5683 }
5684 return l;
5685 }
5686 constexpr K* place(K* ptr) const noexcept {
5687 if constexpr (N == L) {
5688 const K* from = what.symbols();
5689 for (size_t start = 0; start < what.length();) {
5690 size_t next = what.find(pattern, N, start);
5691 if (next == str::npos) {
5692 next = what.length();
5693 }
5694 size_t delta = next - start;
5695 ch_traits<K>::copy(ptr, from + start, delta);
5696 ptr += delta;
5697 ch_traits<K>::copy(ptr, repl, L);
5698 ptr += L;
5699 start = next + N;
5700 }
5701 return ptr;
5702 }
5703 if (matches_.added_ == 0) {
5704 return what.place(ptr);
5705 } else if (matches_.added_ == size_t(-1)) {
5706 // after replaces text become empty
5707 return ptr;
5708 }
5709 const K* from = what.symbols();
5710 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5711 ch_traits<K>::copy(ptr, from + start, offset - start);
5712 ptr += offset - start;
5713 ch_traits<K>::copy(ptr, repl, L);
5714 ptr += L;
5715 start = offset + N;
5716 if (start >= last_) {
5717 size_t tail = what.length() - last_;
5718 ch_traits<K>::copy(ptr, from + last_, tail);
5719 ptr += tail;
5720 break;
5721 } else {
5722 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
5723 }
5724 }
5725 return ptr;
5726 }
5727};
5728
5742template<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>
5743 requires(N > 1)
5744constexpr auto e_repl(A&& w, T&& p, X&& r) {
5745 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
5746}
5747
5755template<typename K>
5756struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
5757 using symb_type = K;
5758 using my_type = expr_replaced<K>;
5759 str_src<K> what;
5760 const str_src<K> pattern;
5761 const str_src<K> repl;
5762 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5763 mutable size_t last_;
5774 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
5775
5776 constexpr size_t length() const {
5777 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
5778 if (!plen || plen == rlen) {
5779 return l;
5780 }
5781 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
5782 if (matches_.added_) {
5783 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5784 l += int(rlen - plen) * matches_.added_;
5785
5786 if (matches_.added_ == FIND_CACHE_SIZE) {
5787 for (;;) {
5788 size_t next = what.find(pattern.symbols(), plen, last_);
5789 if (next == str::npos) {
5790 break;
5791 }
5792 last_ = next + plen;
5793 l += rlen - plen;
5794 }
5795 }
5796 }
5797 if (!l) {
5798 matches_.added_ = -1;
5799 }
5800 return l;
5801 }
5802 constexpr K* place(K* ptr) const noexcept {
5803 size_t plen = pattern.length(), rlen = repl.length();
5804 if (plen == rlen) {
5805 const K* from = what.symbols();
5806 for (size_t start = 0; start < what.length();) {
5807 size_t next = what.find(pattern, start);
5808 if (next == str::npos) {
5809 next = what.length();
5810 }
5811 size_t delta = next - start;
5812 ch_traits<K>::copy(ptr, from + start, delta);
5813 ptr += delta;
5814 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5815 ptr += rlen;
5816 start = next + plen;
5817 }
5818 return ptr;
5819 }
5820 if (matches_.added_ == 0) {
5821 return what.place(ptr);
5822 } else if (matches_.added_ == -1) {
5823 // after replaces text become empty
5824 return ptr;
5825 }
5826 const K* from = what.symbols();
5827 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5828 ch_traits<K>::copy(ptr, from + start, offset - start);
5829 ptr += offset - start;
5830 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5831 ptr += rlen;
5832 start = offset + plen;
5833 if (start >= last_) {
5834 size_t tail = what.length() - start;
5835 ch_traits<K>::copy(ptr, from + start, tail);
5836 ptr += tail;
5837 break;
5838 } else {
5839 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
5840 }
5841 }
5842 return ptr;
5843 }
5844};
5845
5861template<typename K, StrExprForType<K> E>
5862struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
5863 using symb_type = K;
5864 using my_type = expr_replaced<K>;
5865 str_src<K> what;
5866 const str_src<K> pattern;
5867 mutable size_t replLen;
5868 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5869 mutable size_t last_;
5870 const E& expr;
5881 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
5882
5883 constexpr size_t length() const {
5884 size_t l = what.length(), plen = pattern.length();
5885 if (!plen) {
5886 return l;
5887 }
5888 matches_.positions_[0] = what.find(pattern);
5889 if (matches_.positions_[0] == -1) {
5890 // Не нашли вхождений, нечего менять
5891 return l;
5892 }
5893 matches_.added_ = 1;
5894 // Вхождение есть, надо теперь получить длину замены
5895 replLen = expr.length();
5896 if (replLen == plen) {
5897 // Замена той же длины, общая длина не изменится
5898 return l;
5899 }
5900 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
5901
5902 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5903 l += int(replLen - plen) * matches_.added_;
5904
5905 if (matches_.added_ == FIND_CACHE_SIZE) {
5906 for (;;) {
5907 size_t next = what.find(pattern.symbols(), plen, last_);
5908 if (next == str::npos) {
5909 break;
5910 }
5911 last_ = next + plen;
5912 l += replLen - plen;
5913 }
5914 }
5915 if (!l) {
5916 matches_.added_ = -1;
5917 }
5918 return l;
5919 }
5920 constexpr K* place(K* ptr) const noexcept {
5921 if (matches_.added_ == 0) {
5922 // не было найдено вхождений
5923 return what.place(ptr);
5924 } else if (matches_.added_ == -1) {
5925 // Строка стала пустой
5926 return ptr;
5927 }
5928 size_t plen = pattern.length();
5929 const K* from = what.symbols();
5930 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
5931 ptr += matches_.positions_[0];
5932 const K* repl = ptr;
5933 expr.place((typename E::symb_type*)ptr);
5934 ptr += replLen;
5935 size_t start = matches_.positions_[0] + plen;
5936
5937 if (plen == replLen) {
5938 for (;;) {
5939 size_t next = what.find(pattern, start);
5940 if (next == str::npos) {
5941 break;
5942 }
5943 size_t delta = next - start;
5944 ch_traits<K>::copy(ptr, from + start, delta);
5945 ptr += delta;
5946 ch_traits<K>::copy(ptr, repl, replLen);
5947 ptr += replLen;
5948 start = next + plen;
5949 }
5950 } else {
5951 for (size_t idx = 1;;) {
5952 if (start >= last_) {
5953 break;
5954 }
5955 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
5956 size_t delta = next - start;
5957 ch_traits<K>::copy(ptr, from + start, delta);
5958 ptr += delta;
5959 ch_traits<K>::copy(ptr, repl, replLen);
5960 ptr += replLen;
5961 start = next + plen;
5962 }
5963 }
5964 size_t tail = what.length() - start;
5965 ch_traits<K>::copy(ptr, from + start, tail);
5966 return ptr + tail;
5967 }
5968};
5969
5983template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, typename X>
5984 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>))
5985constexpr auto e_repl(A&& w, T&& p, X&& r) {
5986 str_src<K> pattern{std::forward<T>(p)};
5987 str_src<K> repl{std::forward<X>(r)};
5988 return expr_replaced<K>{std::forward<A>(w), pattern, repl};
5989}
5990
6004template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, StrExprForType<K> E>
6005 requires std::is_constructible_v<str_src<K>, T>
6006constexpr auto e_repl(A&& w, T&& p, const E& expr) {
6007 str_src<K> pattern{std::forward<T>(p)};
6008 return expr_replaced_e<K, E>{std::forward<A>(w), pattern, expr};
6009}
6010
6011template<bool UseVectorForReplace>
6012struct replace_search_result_store {
6013 size_t count_{};
6014 std::pair<size_t, size_t> replaces_[16];
6015};
6016
6017template<>
6018struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6019
6020// Строковое выражение для замены символов
6021// String expression to replace characters
6022template<typename K, size_t N, bool UseVectorForReplace>
6023struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
6024 using symb_type = K;
6025 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6026 const K pattern_[N];
6027 const str_src<K> source_;
6028 const str_src<K> replaces_[N];
6029
6030 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6031
6032 [[_no_unique_address]]
6033 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
6034
6035 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
6036 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6037
6038 size_t length() const {
6039 size_t l = source_.length();
6040 auto [fnd, num] = find_first_of(source_.str, source_.len);
6041 if (fnd == str::npos) {
6042 return l;
6043 }
6044 l += replaces_[num].len - 1;
6045 if constexpr (UseVectorForReplace) {
6046 search_results_.reserve((l >> 4) + 8);
6047 search_results_.emplace_back(fnd, num);
6048 for (size_t start = fnd + 1;;) {
6049 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6050 if (fnd == str::npos) {
6051 break;
6052 }
6053 search_results_.emplace_back(fnd, idx);
6054 start = fnd + 1;
6055 l += replaces_[idx].len - 1;
6056 }
6057 } else {
6058 const size_t max_store = std::size(search_results_.replaces_);
6059 search_results_.replaces_[0] = {fnd, num};
6060 search_results_.count_++;
6061 for (size_t start = fnd + 1;;) {
6062 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6063 if (found == str::npos) {
6064 break;
6065 }
6066 if (search_results_.count_ < max_store) {
6067 search_results_.replaces_[search_results_.count_] = {found, idx};
6068 }
6069 l += replaces_[idx].len - 1;
6070 search_results_.count_++;
6071 start = found + 1;
6072 }
6073 }
6074 return l;
6075 }
6076 K* place(K* ptr) const noexcept {
6077 size_t start = 0;
6078 const K* text = source_.str;
6079 if constexpr (UseVectorForReplace) {
6080 for (const auto& [pos, num] : search_results_) {
6081 size_t delta = pos - start;
6082 ch_traits<K>::copy(ptr, text + start, delta);
6083 ptr += delta;
6084 ptr = replaces_[num].place(ptr);
6085 start = pos + 1;
6086 }
6087 } else {
6088 const size_t max_store = std::size(search_results_.replaces_);
6089 size_t founded = search_results_.count_;
6090 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6091 const auto [pos, num] = search_results_.replaces_[idx];
6092 size_t delta = pos - start;
6093 ch_traits<K>::copy(ptr, text + start, delta);
6094 ptr += delta;
6095 ptr = replaces_[num].place(ptr);
6096 start = pos + 1;
6097 }
6098 if (founded > max_store) {
6099 founded -= max_store;
6100 while (founded--) {
6101 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6102 size_t delta = fnd - start;
6103 ch_traits<K>::copy(ptr, text + start, delta);
6104 ptr += delta;
6105 ptr = replaces_[idx].place(ptr);
6106 start = fnd + 1;
6107 }
6108 }
6109 }
6110 size_t tail = source_.len - start;
6111 ch_traits<K>::copy(ptr, text + start, tail);
6112 return ptr + tail;
6113 }
6114
6115protected:
6116 template<typename ... Repl>
6117 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
6118 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6119
6120 template<typename ... Repl> requires (sizeof...(Repl) == N)
6121 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
6122 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6123 {
6124 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6125 for (size_t idx = 0; idx < N; idx++) {
6126 uu8s s = static_cast<uu8s>(pattern_[idx]);
6127 if constexpr (sizeof(K) == 1) {
6128 bit_mask_[s >> 3] |= 1 << (s & 7);
6129 } else {
6130 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6131 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6132 } else {
6133 bit_mask_[s >> 3] |= 1 << (s & 7);
6134 }
6135 }
6136 }
6137 }
6138 }
6139
6140 template<size_t Idx>
6141 size_t index_of(K s) const {
6142 if constexpr (Idx < N) {
6143 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6144 }
6145 return -1;
6146 }
6147 bool is_in_mask(uu8s s) const {
6148 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6149 }
6150 bool is_in_mask2(uu8s s) const {
6151 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6152 }
6153 bool is_in_pattern(K s, size_t& idx) const {
6154 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6155 if constexpr (sizeof(K) == 1) {
6156 if (is_in_mask(s)) {
6157 idx = index_of<0>(s);
6158 return true;
6159 }
6160 } else {
6161 if (std::make_unsigned_t<const K>(s) > 255) {
6162 if (is_in_mask2(s)) {
6163 return (idx = index_of<0>(s)) != -1;
6164 }
6165 } else {
6166 if (is_in_mask(s)) {
6167 idx = index_of<0>(s);
6168 return true;
6169 }
6170 }
6171 }
6172 }
6173 return false;
6174 }
6175 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6176 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6177 size_t idx;
6178 while (offset < len) {
6179 if (is_in_pattern(text[offset], idx)) {
6180 return {offset, idx};
6181 }
6182 offset++;
6183 }
6184 } else {
6185 while (offset < len) {
6186 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6187 return {offset, idx};
6188 }
6189 offset++;
6190 }
6191 }
6192 return {-1, -1};
6193 }
6194};
6195
6235template<bool UseVector = false, StrSource A, typename K = symb_type_from_src_t<A>, typename ... Repl>
6236 requires (sizeof...(Repl) % 2 == 0)
6237auto e_repl_const_symbols(A&& src, Repl&& ... other) {
6238 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
6239}
6240
6280template<typename K, bool UseVectorForReplace = false>
6281struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
6282 using symb_type = K;
6283 using str_t = typename simple_str_selector<K>::type;
6284 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6285
6286 const str_src<K> source_;
6287 const std::vector<std::pair<K, str_t>>& replaces_;
6288
6289 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
6290
6291 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6292
6293 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6321 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
6322 : source_(source), replaces_(repl)
6323 {
6324 size_t pattern_len = replaces_.size();
6325 pattern_.resize(pattern_len);
6326 K* pattern = pattern_.data();
6327
6328 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6329 *pattern++ = replaces_[idx].first;
6330 }
6331
6332 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6333 for (size_t idx = 0; idx < pattern_len; idx++) {
6334 uu8s s = static_cast<uu8s>(pattern_[idx]);
6335 if constexpr (sizeof(K) == 1) {
6336 bit_mask_[s >> 3] |= (1 << (s & 7));
6337 } else {
6338 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6339 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6340 } else {
6341 bit_mask_[s >> 3] |= (1 << (s & 7));
6342 }
6343 }
6344 }
6345 }
6346 }
6347
6348 size_t length() const {
6349 size_t l = source_.length();
6350 auto [fnd, num] = find_first_of(source_.str, source_.len);
6351 if (fnd == str::npos) {
6352 return l;
6353 }
6354 l += replaces_[num].second.len - 1;
6355 if constexpr (UseVectorForReplace) {
6356 search_results_.reserve((l >> 4) + 8);
6357 search_results_.emplace_back(fnd, num);
6358 for (size_t start = fnd + 1;;) {
6359 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6360 if (fnd == str::npos) {
6361 break;
6362 }
6363 search_results_.emplace_back(fnd, idx);
6364 start = fnd + 1;
6365 l += replaces_[idx].second.len - 1;
6366 }
6367 } else {
6368 const size_t max_store = std::size(search_results_.replaces_);
6369 search_results_.replaces_[0] = {fnd, num};
6370 search_results_.count_++;
6371 for (size_t start = fnd + 1;;) {
6372 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6373 if (found == str::npos) {
6374 break;
6375 }
6376 if (search_results_.count_ < max_store) {
6377 search_results_.replaces_[search_results_.count_] = {found, idx};
6378 }
6379 l += replaces_[idx].second.len - 1;
6380 search_results_.count_++;
6381 start = found + 1;
6382 }
6383 }
6384 return l;
6385 }
6386 K* place(K* ptr) const noexcept {
6387 size_t start = 0;
6388 const K* text = source_.str;
6389 if constexpr (UseVectorForReplace) {
6390 for (const auto& [pos, num] : search_results_) {
6391 size_t delta = pos - start;
6392 ch_traits<K>::copy(ptr, text + start, delta);
6393 ptr += delta;
6394 ptr = replaces_[num].second.place(ptr);
6395 start = pos + 1;
6396 }
6397 } else {
6398 const size_t max_store = std::size(search_results_.replaces_);
6399 size_t founded = search_results_.count_;
6400 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6401 const auto [pos, num] = search_results_.replaces_[idx];
6402 size_t delta = pos - start;
6403 ch_traits<K>::copy(ptr, text + start, delta);
6404 ptr += delta;
6405 ptr = replaces_[num].second.place(ptr);
6406 start = pos + 1;
6407 }
6408 if (founded > max_store) {
6409 founded -= max_store;
6410 while (founded--) {
6411 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6412 size_t delta = fnd - start;
6413 ch_traits<K>::copy(ptr, text + start, delta);
6414 ptr += delta;
6415 ptr = replaces_[idx].second.place(ptr);
6416 start = fnd + 1;
6417 }
6418 }
6419 }
6420 size_t tail = source_.len - start;
6421 ch_traits<K>::copy(ptr, text + start, tail);
6422 return ptr + tail;
6423 }
6424
6425protected:
6426 size_t index_of(K s) const {
6427 return pattern_.find(s);
6428 }
6429
6430 bool is_in_mask(uu8s s) const {
6431 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6432 }
6433 bool is_in_mask2(uu8s s) const {
6434 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6435 }
6436
6437 bool is_in_pattern(K s, size_t& idx) const {
6438 if constexpr (sizeof(K) == 1) {
6439 if (is_in_mask(s)) {
6440 idx = index_of(s);
6441 return true;
6442 }
6443 } else {
6444 if (std::make_unsigned_t<const K>(s) > 255) {
6445 if (is_in_mask2(s)) {
6446 return (idx = index_of(s)) != -1;
6447 }
6448 } else {
6449 if (is_in_mask(s)) {
6450 idx = index_of(s);
6451 return true;
6452 }
6453 }
6454 }
6455 return false;
6456 }
6457
6458 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6459 size_t pl = pattern_.length();
6460 if (pl >= BIT_SEARCH_TRESHHOLD) {
6461 size_t idx;
6462 while (offset < len) {
6463 if (is_in_pattern(text[offset], idx)) {
6464 return {offset, idx};
6465 }
6466 offset++;
6467 }
6468 } else {
6469 while (offset < len) {
6470 if (size_t idx = index_of(text[offset]); idx != -1) {
6471 return {offset, idx};
6472 }
6473 offset++;
6474 }
6475 }
6476 return {-1, -1};
6477 }
6478};
6479
6480template<typename K, StrExprForType<K> T>
6481struct force_copy {
6482 const T& t_;
6483 force_copy(const T& t) : t_(t){}
6484};
6485
6486template<typename K, typename T>
6487struct symb_type_from_src<force_copy<K, T>> {
6488 using type = K;
6489};
6490
6491
6492template<StrExpr T>
6493force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6494
6495template<typename K, typename T>
6496constexpr auto to_subst(T&& t) {
6497 return to_strexpr<K>(std::forward<T>(t));
6498}
6499
6500template<typename K, typename T, size_t N>
6501constexpr auto to_subst(const T(&t)[N]) {
6502 return expr_literal<T, N - 1>{t};
6503}
6504
6505template<typename K, StrExprForType<K> T>
6506constexpr decltype(auto) to_subst(T&& t) {
6507 return std::forward<T>(t);
6508}
6509
6510template<typename K, typename T>
6511constexpr T to_subst(const force_copy<K, T>& t) {
6512 return t.t_;
6513}
6514
6515template<typename K, typename T>
6516constexpr T to_subst(force_copy<K, T>&& t) {
6517 return t.t_;
6518}
6519
6520template<typename K, typename T>
6521constexpr T to_subst(force_copy<K, T>& t) {
6522 return t.t_;
6523}
6524
6525template<typename K, typename T>
6526using to_str_exp_t = decltype(to_subst<K>(std::declval<T>()));
6527
6535template<typename K, typename G, typename Arg, typename...Args>
6536struct e_concat : expr_to_std_string<e_concat<K, G, Arg, Args...>> {
6537 using symb_type = K;
6538 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6539 using arg_t = to_str_exp_t<K, Arg>;
6540 to_str_exp_t<K, G> glue_;
6541 arg_t arg_;
6542 store_t args_;
6543 mutable size_t glue_len_;
6576 constexpr e_concat(G&& glue, Arg&& arg, Args&&...args)
6577 : glue_(to_subst<K>(std::forward<G>(glue)))
6578 , arg_(to_subst<K>(std::forward<Arg>(arg)))
6579 , args_(to_subst<K>(std::forward<Args>(args))...) {}
6580
6581 constexpr size_t length() const noexcept {
6582 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6583 glue_len_ = glue_.length();
6584 size_t l = arg_.length() + glue_len_ * sizeof...(Args);
6585 ((l += std::get<Indexes>(args_).length()),...);
6586 return l;
6587 }(std::make_index_sequence<sizeof...(Args)>());
6588 }
6589 constexpr K* place(K* ptr) const noexcept {
6590 return [this]<size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6591 ptr = (K*)arg_.place((typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6592 const K* glueStart = ptr;
6593 ptr = glue_.place(ptr);
6594 (
6595 (
6596 ptr = (K*)std::get<Indexes>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6597 glue_len_ > 0 && Indexes < sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) : nullptr
6598 ),
6599 ...);
6600 return ptr;
6601 }(ptr, std::make_index_sequence<sizeof...(Args)>());
6602 }
6603};
6604// CTAD deducing rule for e_concat
6605template<typename T, typename ... Args> requires (sizeof...(Args) > 1)
6606e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6607
6608struct parse_subst_string_error {
6609 parse_subst_string_error(const char*){}
6610};
6611
6612namespace details {
6613
6614template<typename K, size_t NParams>
6615constexpr size_t parse_pattern_string(str_src<K> pattern, auto&& add_part, auto&& add_param) {
6616 char used_args[NParams] = {0};
6617 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
6618 size_t all_len = 0;
6619
6620 auto find = [](const K* from, const K* last, K s) {
6621 while (from != last) {
6622 if (*from == s) {
6623 break;
6624 }
6625 from++;
6626 }
6627 return from;
6628 };
6629 size_t idx_in_params = 0;
6630
6631 while (first != last) {
6632 const K* open_pos = first;
6633 if (*first != '{') {
6634 open_pos = find(first, last, '{');
6635
6636 for (;;) {
6637 const K* close_pos = find(first, open_pos, '}');
6638 if (close_pos == open_pos) {
6639 unsigned len = open_pos - first;
6640 add_part(first - start, len);
6641 all_len += len;
6642 break;
6643 }
6644 ++close_pos;
6645 if (close_pos == open_pos || *close_pos != '}') {
6646 throw parse_subst_string_error{"unescaped }"};
6647 }
6648 unsigned len = close_pos - first;
6649 add_part(first - start, len);
6650 all_len += len;
6651 first = ++close_pos;
6652 }
6653 if (open_pos == last) {
6654 break;
6655 }
6656 }
6657 if (++open_pos == last) {
6658 throw parse_subst_string_error{"unescaped {"};
6659 }
6660 if (*open_pos == '}') {
6661 if (idx_in_params == -1) {
6662 throw parse_subst_string_error{"already used param ids"};
6663 }
6664 if (idx_in_params == NParams) {
6665 throw parse_subst_string_error{"too many params"};
6666 }
6667 used_args[idx_in_params] = 1;
6668 add_param(idx_in_params++);
6669 first = open_pos + 1;
6670 } else if (*open_pos == '{') {
6671 add_part(open_pos - start, 1);
6672 all_len++;
6673 first = open_pos + 1;
6674 } else {
6675 if (idx_in_params != 0 && idx_in_params != -1) {
6676 throw parse_subst_string_error{"already used non id params"};
6677 }
6678 idx_in_params = -1;
6679 const K* end = find(open_pos, last, '}');
6680 if (end == last) {
6681 throw parse_subst_string_error{"not found }"};
6682 }
6683 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
6684 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
6685 throw parse_subst_string_error{"bad param id"};
6686 }
6687 used_args[--p] = 1;
6688 add_param(p);
6689 first = end + 1;
6690 }
6691 }
6692 for (auto c : used_args) {
6693 if (!c) {
6694 throw parse_subst_string_error{"unused param"};
6695 }
6696 }
6697 return all_len;
6698}
6699
6700struct portion {
6701 unsigned start: 16;
6702 unsigned len: 15;
6703 unsigned is_param: 1;
6704
6705 portion() = default;
6706
6707 constexpr void set_param(unsigned param) {
6708 if (param >= (1 << 16)) {
6709 throw parse_subst_string_error{"the parameter id is too large"};
6710 }
6711 start = param;
6712 is_param = 1;
6713 }
6714 constexpr void set_part(unsigned from, unsigned l) {
6715 if (from >= (1 << 16) || len >= (1 << 15)) {
6716 throw parse_subst_string_error{"the string part is too large"};
6717 }
6718 start = from;
6719 len = l;
6720 is_param = 0;
6721 }
6722};
6723
6724template<typename K, size_t PtLen, size_t NParams>
6725struct subst_params {
6726 const K(&source_)[PtLen];
6727 unsigned all_len_{};
6728 unsigned actual_{};
6729 // The pattern string can be divided into a maximum of this number of portions.
6730 // "a{}a{}a{}a" - One portion of one symbol from the edge, and two portions for every three symbols
6731 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
6732
6733 consteval subst_params(const K(&pattern)[PtLen]) : source_(pattern) {
6734 all_len_ = parse_pattern_string<K, NParams>(pattern,
6735 [this](unsigned from, unsigned len) {
6736 portions_[actual_++].set_part(from, len);
6737 }, [this](unsigned param) {
6738 portions_[actual_++].set_param(param);
6739 });
6740 }
6741};
6742
6743} // namespace details
6744
6750template<typename K, size_t PtLen, typename ... Args>
6751struct e_subst : expr_to_std_string<e_subst<K, PtLen, Args...>> {
6752 inline static constexpr size_t NParams = sizeof...(Args);
6753 using symb_type = K;
6754 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6755
6756 const details::subst_params<K, PtLen, NParams>& subst_;
6757 store_t args_;
6798 constexpr e_subst(const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
6799 : subst_(subst)
6800 , args_(to_subst<K>(std::forward<Args>(args))...){}
6801
6802 constexpr size_t length() const noexcept {
6803 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6804 size_t idx = 0;
6805 size_t expr_length_[NParams] = {};
6806 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6807 size_t l = subst_.all_len_;
6808 for (idx = 0; idx < subst_.actual_; idx++) {
6809 if (subst_.portions_[idx].is_param) {
6810 l += expr_length_[subst_.portions_[idx].start];
6811 }
6812 }
6813 return l;
6814 }(std::make_index_sequence<sizeof...(Args)>());
6815 }
6816 template<size_t Idx>
6817 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6818 if (idx == Idx) {
6819 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6820 }
6821 if constexpr (Idx < NParams - 1) {
6822 return place_idx<Idx + 1>(ptr, idx);
6823 }
6824 return ptr;
6825 }
6826 constexpr K* place(K* ptr) const noexcept {
6827 for (size_t idx = 0; idx < subst_.actual_; idx++) {
6828 if (subst_.portions_[idx].is_param) {
6829 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
6830 } else {
6831 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
6832 ptr += subst_.portions_[idx].len;
6833 }
6834 }
6835 return ptr;
6836 }
6837};
6838
6839// CTAD deducing rule for e_subst
6840template<typename K, size_t N, typename...Args> requires (sizeof...(Args) > 0)
6841e_subst(const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
6842
6848template<typename K, typename ... Args>
6849struct e_vsubst : expr_to_std_string<e_vsubst<K, Args...>> {
6850 inline static constexpr size_t Nparams = sizeof...(Args);
6851 using symb_type = K;
6852 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6853
6854 details::portion portions_[Nparams * 3];
6855 std::vector<details::portion> more_portions_;
6856 store_t args_;
6857 str_src<K> pattern_;
6858 size_t all_len_{};
6859 unsigned actual_{};
6897 constexpr e_vsubst(str_src<K> pattern, Args&&...args)
6898 : pattern_(pattern)
6899 , args_(to_subst<K>(std::forward<Args>(args))...) {
6900
6901 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [this](unsigned from, unsigned len) {
6902 if (actual_ < std::size(portions_)) {
6903 portions_[actual_++].set_part(from, len);
6904 } else {
6905 if (actual_ == std::size(portions_)) {
6906 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6907 }
6908 more_portions_.emplace_back().set_part(from, len);
6909 }
6910 }, [this](unsigned param) {
6911 if (actual_ < std::size(portions_)) {
6912 portions_[actual_++].set_param(param);
6913 } else {
6914 if (actual_ == std::size(portions_)) {
6915 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6916 }
6917 more_portions_.emplace_back().set_param(param);
6918 }
6919 }
6920 );
6921 }
6922 constexpr size_t length() const noexcept {
6923 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6924 size_t idx = 0;
6925 size_t expr_length_[Nparams] = {};
6926 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6927 size_t l = all_len_;
6928 for (idx = 0; idx < actual_; idx++) {
6929 if (portions_[idx].is_param) {
6930 l += expr_length_[portions_[idx].start];
6931 }
6932 }
6933 for (const auto& p : more_portions_) {
6934 if (p.is_param) {
6935 l += expr_length_[p.start];
6936 }
6937 }
6938 return l;
6939 }(std::make_index_sequence<sizeof...(Args)>());
6940 }
6941 template<size_t Idx>
6942 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6943 if (idx == Idx) {
6944 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6945 }
6946 if constexpr (Idx < Nparams - 1) {
6947 return place_idx<Idx + 1>(ptr, idx);
6948 }
6949 return ptr;
6950 }
6951 constexpr K* place(K* ptr) const noexcept {
6952 for (size_t idx = 0; idx < actual_; idx++) {
6953 if (portions_[idx].is_param) {
6954 ptr = place_idx<0>(ptr, portions_[idx].start);
6955 } else {
6956 ch_traits<K>::copy(ptr, pattern_.symbols() + portions_[idx].start, portions_[idx].len);
6957 ptr += portions_[idx].len;
6958 }
6959 }
6960 for (const auto& p : more_portions_) {
6961 if (p.is_param) {
6962 ptr = place_idx<0>(ptr, p.start);
6963 } else {
6964 const K* from = pattern_.symbols() + p.start;
6965 for (size_t idx = p.len; idx--;) {
6966 *ptr++ = *from++;
6967 }
6968 //ch_traits<K>::copy(ptr, pattern_.symbols() + p.start, p.len);
6969 //ptr += p.len;
6970 }
6971 }
6972 return ptr;
6973 }
6974};
6975// CTAD deducing rule for e_vsubst
6976template<StrSourceNoLiteral T, typename...Args> requires (sizeof...(Args) > 0)
6977e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
6978
6979template<is_one_of_char_v K, bool upper>
6980struct expr_change_case_ascii : expr_to_std_string<expr_change_case_ascii<K, upper>>{
6981 using symb_type = K;
6982
6983 str_src<K> src_;
6984
6985 template<StrSource S>
6986 expr_change_case_ascii(S&& s) : src_(std::forward<S>(s)){}
6987
6988 constexpr size_t length() const noexcept {
6989 return src_.length();
6990 }
6991 constexpr K* place(K* ptr) const noexcept {
6992 const K* read = src_.str;
6993 for (size_t l = src_.len; l--;) {
6994 if constexpr (upper) {
6995 *ptr++ = makeAsciiUpper(*read++);
6996 } else {
6997 *ptr++ = makeAsciiLower(*read++);
6998 }
6999 }
7000 return ptr;
7001 }
7002};
7003
7018template<is_one_of_char_v K>
7019struct e_ascii_upper : expr_change_case_ascii<K, true> {
7020 using base = expr_change_case_ascii<K, true>;
7021 using base::base;
7022};
7023
7024template<StrSource S>
7026
7041template<is_one_of_char_v K>
7042struct e_ascii_lower : expr_change_case_ascii<K, false> {
7043 using base = expr_change_case_ascii<K, false>;
7044 using base::base;
7045};
7046
7047template<StrSource S>
7049
7054namespace str {
7055
7088template<typename K, typename A, StrExprForType<K> E>
7089std::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) {
7090 size_t expr_length = expr.length();
7091 if (!expr_length) {
7092 str.erase(from, count);
7093 return str;
7094 }
7095 size_t str_length = str.length();
7096 if (from > str_length) {
7097 from = str_length;
7098 }
7099 if (count > str_length || from + count > str_length) {
7100 count = str_length - from;
7101 }
7102 size_t new_length = str_length - count + expr_length;
7103 size_t tail_length = str_length - count - from;
7104
7105 if (new_length <= str_length) {
7106 K* data = str.data();
7107 expr.place((typename E::symb_type*)data + from);
7108 if (expr_length < count) {
7109 if (tail_length) {
7110 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7111 }
7112 str.resize(new_length);
7113 }
7114 } else {
7115 auto fill = [&](K* data, size_t) -> size_t {
7116 if (tail_length) {
7117 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7118 }
7119 expr.place((typename E::symb_type*)data + from);
7120 return new_length;
7121 };
7122 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
7123 str.resize_and_overwrite(new_length, fill);
7124 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
7125 str._Resize_and_overwrite(new_length, fill);
7126 } else {
7127 str.resize(new_length);
7128 fill(str.data(), 0);
7129 }
7130 }
7131 return str;
7132}
7133
7161template<typename K, typename A, StrExprForType<K> E>
7162std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7163 return change(str, str.length(), 0, expr);
7164}
7165
7194template<typename K, typename A, StrExprForType<K> E>
7195std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7196 return change(str, 0, 0, expr);
7197}
7198
7229template<typename K, typename A, StrExprForType<K> E>
7230std::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) {
7231 return change(str, from, 0, expr);
7232}
7233
7258template<typename K, typename A, StrExprForType<K> E>
7259std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7260 if (size_t expr_length = expr.length()) {
7261 if (expr_length <= str.length()) {
7262 K* data = str.data();
7263 expr.place((typename E::symb_type*)data);
7264 str.resize(expr_length);
7265 } else {
7266 auto fill = [&](K* data, size_t) -> size_t {
7267 expr.place((typename E::symb_type*)data);
7268 return expr_length;
7269 };
7270 if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
7271 str.resize_and_overwrite(expr_length, fill);
7272 } else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
7273 str._Resize_and_overwrite(expr_length, fill);
7274 } else {
7275 str.resize(expr_length);
7276 expr.place((typename E::symb_type*)str.data());
7277 }
7278 }
7279 } else {
7280 str.clear();
7281 }
7282 return str;
7283}
7284
7285namespace details {
7286
7287template<typename K, typename A, typename E>
7288struct replace_grow_helper {
7289 using my_type = std::basic_string<K, std::char_traits<K>, A>;
7290
7291 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)
7292 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
7293 my_type& str;
7294
7295 const str_src<K> source;
7296 const str_src<K> pattern;
7297 K* repl;
7298 const size_t replLen;
7299
7300 size_t maxCount;
7301 const size_t delta;
7302 size_t all_delta{};
7303 const E& expr;
7304
7305 K* reserve_for_copy{};
7306 size_t end_of_piece{};
7307 size_t total_length{};
7308
7309 std::optional<my_type> dst;
7310
7311 void replace(size_t offset) {
7312 size_t found[16] = {offset};
7313 maxCount--;
7314
7315 offset += pattern.len;
7316 all_delta += delta;
7317 size_t idx = 1;
7318 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
7319 found[idx] = source.find(pattern, offset);
7320 if (found[idx] == npos) {
7321 break;
7322 }
7323 offset = found[idx] + pattern.len;
7324 all_delta += delta;
7325 }
7326 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
7327 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
7328 }
7329 // Теперь делаем свои замены
7330 // Now we make our replacements
7331 if (!reserve_for_copy) {
7332 // Только начинаем
7333 // Just getting started
7334 end_of_piece = source.length();
7335 total_length = end_of_piece + all_delta;
7336 my_type* dst_str{};
7337 if (total_length <= str.capacity()) {
7338 // Строка поместится в старое место | The string will be placed in the old location.
7339 dst_str = &str;
7340 } else {
7341 // Будем создавать в другом буфере | We will create in another buffer.
7342 dst_str = &dst.emplace();
7343 }
7344 auto fill = [this](K* p, size_t) -> size_t {
7345 reserve_for_copy = p;
7346 return total_length;
7347 };
7348 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
7349 dst_str->_Resize_and_overwrite(total_length, fill);
7350 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
7351 dst_str->resize_and_overwrite(total_length, fill);
7352 } else {
7353 dst_str->resize(total_length);
7354 reserve_for_copy = dst_str->data();
7355 }
7356 }
7357 const K* src_start = str.c_str();
7358 while(idx-- > 0) {
7359 size_t pos = found[idx] + pattern.len;
7360 size_t lenOfPiece = end_of_piece - pos;
7361 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
7362 if constexpr (std::is_same_v<E, int>) {
7363 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7364 } else {
7365 if (!repl) {
7366 repl = reserve_for_copy + pos + all_delta - replLen;
7367 expr.place(repl);
7368 } else {
7369 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7370 }
7371 }
7372 all_delta -= delta;
7373 end_of_piece = found[idx];
7374 }
7375 if (!all_delta && reserve_for_copy != src_start) {
7376 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
7377 str = std::move(*dst);
7378 }
7379 }
7380};
7381
7382} // namespace details
7383
7418template<typename K, typename A, StrExprForType<K> E, typename T>
7419requires (std::is_constructible_v<str_src<K>, T>)
7420std::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) {
7421 if (!max_count) {
7422 return str;
7423 }
7424 str_src<K> src = str;
7425 str_src<K> spattern{std::forward<T>(pattern)};
7426 offset = src.find(pattern, offset);
7427 if (offset == npos) {
7428 return str;
7429 }
7430 size_t replLen = repl.length();
7431 K* replStart{};
7432 if (spattern.len == replLen) {
7433 // Заменяем inplace на подстроку такой же длины
7434 // Replace inplace with a substring of the same length
7435 K* ptr = str.data();
7436 replStart = ptr + offset;
7437 repl.place(replStart);
7438
7439 while (--max_count) {
7440 offset = src.find(spattern, offset + replLen);
7441 if (offset == npos)
7442 break;
7443 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7444 }
7445 } else if (spattern.len > replLen) {
7446 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7447 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7448 K* ptr = str.data();
7449 replStart = ptr + offset;
7450 repl.place(replStart);
7451 size_t posWrite = offset + replLen;
7452 offset += spattern.len;
7453
7454 while (--max_count) {
7455 size_t idx = src.find(spattern, offset);
7456 if (idx == npos)
7457 break;
7458 size_t lenOfPiece = idx - offset;
7459 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7460 posWrite += lenOfPiece;
7461 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7462 posWrite += replLen;
7463 offset = idx + spattern.len;
7464 }
7465 size_t tailLen = src.len - offset;
7466 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7467 str.resize(posWrite + tailLen);
7468 } else {
7469 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7470 }
7471 return str;
7472}
7473
7494template<typename K, typename A>
7495std::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) {
7496 if (!max_count) {
7497 return str;
7498 }
7499 str_src<K> src = str;
7500 offset = src.find(pattern, offset);
7501 if (offset == npos) {
7502 return str;
7503 }
7504 if (pattern.len == repl.len) {
7505 // Заменяем inplace на подстроку такой же длины
7506 // Replace inplace with a substring of the same length
7507 K* ptr = str.data();
7508 while (max_count--) {
7509 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7510 offset = src.find(pattern, offset + repl.len);
7511 if (offset == npos)
7512 break;
7513 }
7514 } else if (pattern.len > repl.len) {
7515 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7516 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7517 K* ptr = str.data();
7518 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7519 size_t posWrite = offset + repl.len;
7520 offset += pattern.len;
7521
7522 while (--max_count) {
7523 size_t idx = src.find(pattern, offset);
7524 if (idx == npos)
7525 break;
7526 size_t lenOfPiece = idx - offset;
7527 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7528 posWrite += lenOfPiece;
7529 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7530 posWrite += repl.len;
7531 offset = idx + pattern.len;
7532 }
7533 size_t tailLen = src.len - offset;
7534 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7535 str.resize(posWrite + tailLen);
7536 } else {
7537 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7538 }
7539 return str;
7540}
7541
7542template<typename K, typename A, typename T, typename M>
7543requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7544std::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) {
7545 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7546}
7547
7560template<typename K, typename A>
7561std::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) {
7562 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7563 K s = *ptr;
7564 if (isAsciiLower(s)) {
7565 *ptr = s & ~0x20;
7566 }
7567 }
7568 return str;
7569}
7570
7583template<typename K, typename A>
7584std::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) {
7585 for (K *ptr = str.data() + std::min(from, str.length()), *end = str.data() + std::min(to, str.length()); ptr < end; ptr++) {
7586 K s = *ptr;
7587 if (isAsciiUpper(s)) {
7588 *ptr = s | 0x20;
7589 }
7590 }
7591 return str;
7592}
7593
7594} // namespace str
7595
7596} // namespace simstr
7597
7602namespace std {
7622template<simstr::StdStrSource T>
7624 return {str};
7625}
7626
7654template<typename K, typename A, simstr::StrExprForType<K> E>
7655std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7656 return simstr::str::change(str, str.length(), 0, expr);
7657}
7658
7686template<typename K, typename A, simstr::StrExprForType<K> E>
7687std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7688 return simstr::str::change(str, 0, 0, expr);
7689}
7690
7691} // namespace std
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3234
constexpr bool is_done() const
Find out if substrings are running out.
Definition strexpr.h:3245
constexpr str_t next()
Get the next substring.
Definition strexpr.h:3254
constexpr R trimmed_right_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:5020
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Find the last occurrence of a character in this string.
Definition strexpr.h:4065
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition strexpr.h:3747
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition strexpr.h:3632
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition strexpr.h:3969
constexpr std::optional< R > strip_suffix(str_piece suffix) const
Get a string without a suffix if it ends with one.
Definition strexpr.h:4733
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition strexpr.h:4570
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition strexpr.h:3683
constexpr std::basic_string_view< D > to_sv() const noexcept
Convert to std::basic_string_view.
Definition strexpr.h:3388
constexpr str_piece get(S start, E end) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3505
constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by a whitespace...
Definition strexpr.h:4516
constexpr R trimmed_right() const
Get a string with whitespace removed on the right.
Definition strexpr.h:4816
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix,...
Definition strexpr.h:5059
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the end of the first occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:3865
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition strexpr.h:4452
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition strexpr.h:3672
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition strexpr.h:3366
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition strexpr.h:3821
constexpr str_piece until(str_piece pattern, size_t offset=0) const noexcept
Get the substring str_src from the beginning to the first found occurrence of the specified substring...
Definition strexpr.h:3594
constexpr R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition strexpr.h:4938
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4216
constexpr str_piece prefix(size_t count) const noexcept
Wraps get(0, count).
Definition strexpr.h:3539
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition strexpr.h:3770
constexpr T split(str_piece delimiter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition strexpr.h:4437
constexpr R trimmed_right_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:4921
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition strexpr.h:3559
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Get a substring. Works similarly to operator(), only the result is the same type as the method applie...
Definition strexpr.h:4167
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition strexpr.h:4647
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3436
constexpr bool starts_with_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified substring followed by a whitespace ASCII character.
Definition strexpr.h:4461
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Get the substring str_src from position from to position to (not including it).
Definition strexpr.h:3581
constexpr R trimmed_left_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:5002
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Get a copy of the string with occurrences of substrings replaced.
Definition strexpr.h:4679
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition strexpr.h:3643
constexpr R trimmed_left() const
Get a string with whitespace removed on the left.
Definition strexpr.h:4804
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Whether the string ends with the specified substring in a case-insensitive ASCII character.
Definition strexpr.h:4596
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character not from the given character set.
Definition strexpr.h:4112
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:4084
constexpr R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition strexpr.h:4966
constexpr R trimmed_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:4883
constexpr str_piece suffix(size_t count) const noexcept
Wraps get(from_end(count), to_end).
Definition strexpr.h:3546
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string or the end of the string.
Definition strexpr.h:3928
constexpr R trimmed_prefix(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:5036
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Whether the string begins with the given substring in a case-insensitive ASCII character.
Definition strexpr.h:4507
constexpr R trimmed_left_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:4902
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string.
Definition strexpr.h:3901
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:4125
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Find a character in this string or the end of a string.
Definition strexpr.h:3988
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character not from the given character set.
Definition strexpr.h:4153
std::optional< double > to_double() const noexcept
Convert string to double.
Definition strexpr.h:4261
constexpr void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Call a functor on all found occurrences of a substring in this string.
Definition strexpr.h:4023
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Find the end of the occurrence of a substring in this string.
Definition strexpr.h:3837
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition strexpr.h:4296
constexpr std::optional< R > strip_prefix(str_piece prefix) const
Get a string without a prefix if it starts with one.
Definition strexpr.h:4695
constexpr bool operator!() const noexcept
Check for emptiness.
Definition strexpr.h:3601
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition strexpr.h:4792
constexpr size_t size() const
The size of the string in characters.
Definition strexpr.h:3377
constexpr To find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Find all occurrences of a substring in this string.
Definition strexpr.h:4046
constexpr my_type str_mid(size_t from, size_t len=-1) const
Get part of a string with an object of the same type to which the method is applied,...
Definition strexpr.h:4184
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string.
Definition strexpr.h:3914
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:4097
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition strexpr.h:4603
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string.
Definition strexpr.h:3801
constexpr SplitterBase< K, str_piece > splitter(str_piece delimiter) const
Retrieve a Splitter object by the given splitter, which allows sequential get substrings using the ne...
Definition strexpr.h:5119
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:4138
constexpr R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition strexpr.h:4846
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string or the end of the string.
Definition strexpr.h:3851
constexpr R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition strexpr.h:4952
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition strexpr.h:3702
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition strexpr.h:3692
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition strexpr.h:3956
constexpr R trimmed_suffix_ia(str_piece suffix) const
Returns a string with the end of the string removed if it ends with the specified suffix,...
Definition strexpr.h:5102
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3463
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition strexpr.h:3614
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition strexpr.h:4659
constexpr R trimmed_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:4984
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition strexpr.h:3712
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Convert to std::basic_string.
Definition strexpr.h:3408
constexpr R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition strexpr.h:4861
constexpr T splitf(str_piece delimiter, const Op &beforeFunc, size_t offset=0) const
Split a string into parts at a given delimiter, possibly applying a functor to each substring.
Definition strexpr.h:4421
constexpr R trimmed(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left and right.
Definition strexpr.h:4831
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:3942
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition strexpr.h:4555
constexpr std::optional< R > strip_prefix_ia(str_piece prefix) const
Get a string without a prefix if it starts with it, insensitive to ASCII characters.
Definition strexpr.h:4714
constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by one of the s...
Definition strexpr.h:4529
constexpr std::optional< R > strip_suffix_ia(str_piece suffix) const
Get a string without a suffix if it ends with one in case-insensitive ASCII characters.
Definition strexpr.h:4752
constexpr R trimmed_suffix(str_piece suffix) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:5082
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition strexpr.h:3351
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4249
constexpr bool equal_ia(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
Definition strexpr.h:3759
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4324
constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified substring followed by one of the specified characters.
Definition strexpr.h:4474
The concept of a string expression compatible with a given character type.
Definition strexpr.h:525
Concept of "String Expressions".
Definition strexpr.h:507
Base concept of string object.
Definition strexpr.h:311
Checks whether two types are compatible string types.
Definition strexpr.h:172
constexpr expr_if< A > e_if(bool c, const A &a)
Creating a conditional string expression expr_if.
Definition strexpr.h:1544
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Unary operator + for converting standard strings to string expressions.
Definition strexpr.h:7623
constexpr expr_spaces< uws, N > e_spcw()
Generates a string of N wchar_t spaces.
Definition strexpr.h:1104
expr_fill< K, A, true > e_fill_left(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:2699
constexpr empty_expr< uws > eew
Empty string expression of type wchar_t.
Definition strexpr.h:862
constexpr expr_spaces< u8s, N > e_spca()
Generates a string of N char spaces.
Definition strexpr.h:1086
constexpr empty_expr< u32s > eeuu
Empty string expression of type char32_t.
Definition strexpr.h:874
constexpr expr_num< K, T > e_num(T t)
Convert an integer to a string expression.
Definition strexpr.h:1720
HexFlags
Flags for the e_hex function.
Definition strexpr.h:2527
constexpr auto e_hex(T v)
Creates an object that can be converted to a string expression that generates a hexadecimal represent...
Definition strexpr.h:2584
constexpr strexprjoin< A, B > operator+(const A &a, const B &b)
An addition operator for two arbitrary string expressions of the same character type.
Definition strexpr.h:719
constexpr expr_repeat_lit< K, M - 1 > e_repeat(T &&s, size_t l)
Generate a string from l string constants s of type K.
Definition strexpr.h:1218
constexpr expr_literal< typename const_lit< T >::symb_type, static_cast< size_t >(N - 1)> e_t(T &&s)
Converts a string literal to a string expression.
Definition strexpr.h:991
expr_fill< K, A, false > e_fill_right(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:2725
constexpr expr_choice< A, B > e_choice(bool c, const A &a, const B &b)
Create a conditional string expression expr_choice.
Definition strexpr.h:1466
constexpr expr_char< K > e_char(K s)
Generates a string of 1 given character.
Definition strexpr.h:918
constexpr empty_expr< u8s > eea
Empty string expression of type char.
Definition strexpr.h:850
constexpr expr_pad< K > e_c(size_t l, K s)
Generates a string of l characters s of type K.
Definition strexpr.h:1149
constexpr empty_expr< u16s > eeu
Empty string expression of type char16_t.
Definition strexpr.h:868
auto e_repl_const_symbols(A &&src, Repl &&... other)
Returns a string expression that generates a string containing the given characters replaced with giv...
Definition strexpr.h:6237
constexpr auto e_int(T v)
Creates an object that is converted to a string expression that generates a string representation of ...
Definition strexpr.h:2383
constexpr empty_expr< u8s > eeb
Empty string expression of type char8_t.
Definition strexpr.h:856
constexpr auto e_repl(A &&w, T &&p, X &&r)
Get a string expression that generates a string with all occurrences of a given substring replaced.
Definition strexpr.h:5744
constexpr auto e_join(const T &s, L &&d)
Get a string expression concatenating the strings in the container into a single string with the give...
Definition strexpr.h:2803
@ Short
without leading zeroes
Definition strexpr.h:2528
Small namespace for standard string methods.
Definition strexpr.h:1600
std::basic_string< K, std::char_traits< K >, A > & append(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Append a string expression to a standard string.
Definition strexpr.h:7162
std::basic_string< K, std::char_traits< K >, A > & prepend(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Insert a string expression at the beginning of a standard string.
Definition strexpr.h:7195
std::basic_string< K, std::char_traits< K >, A > & make_ascii_upper(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change lowercase ASCII string characters (a-z) to uppercase.
Definition strexpr.h:7561
std::basic_string< K, std::char_traits< K >, A > & change(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, size_t count, const E &expr)
Replace a portion of a standard string with the given string expression.
Definition strexpr.h:7089
std::basic_string< K, std::char_traits< K >, A > & replace(std::basic_string< K, std::char_traits< K >, A > &str, T &&pattern, const E &repl, size_t offset=0, size_t max_count=-1)
A function for searching for substrings in a standard string and replacing the found occurrences with...
Definition strexpr.h:7420
std::basic_string< K, std::char_traits< K >, A > & make_ascii_lower(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change uppercase ASCII string characters (A-Z) to lowercase.
Definition strexpr.h:7584
std::basic_string< K, std::char_traits< K >, A > & overwrite(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Rewrite the entire string with the given string expression, resizing the string if necessary.
Definition strexpr.h:7259
std::basic_string< K, std::char_traits< K >, A > & insert(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, const E &expr)
Insert a string expression at the specified position in a standard string.
Definition strexpr.h:7230
Library namespace.
Definition sstring.cpp:12
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition strexpr.h:2870
@ Overflow
Переполнение, число не помещается в заданный тип
Definition strexpr.h:2873
@ Success
Успешно
Definition strexpr.h:2871
@ NotNumber
Вообще не число
Definition strexpr.h:2874
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition strexpr.h:2872
Some methods for working with standard strings.
Definition strexpr.h:7602
Generate a string based on the original one, replacing all ASCII uppercase letters (A-Z) with lowerca...
Definition strexpr.h:7042
Generate a string based on the original one, replacing all ASCII lowercase letters (a-z) with upperca...
Definition strexpr.h:7019
constexpr e_concat(G &&glue, Arg &&arg, Args &&...args)
Create a string expression concatenating the specified string expressions using the specified delimit...
Definition strexpr.h:6576
constexpr e_subst(const details::subst_params< K, PtLen, NParams > &subst, Args &&...args)
Creates a string expression that substitutes the values ​​of the passed string expressions into the s...
Definition strexpr.h:6798
constexpr e_vsubst(str_src< K > pattern, Args &&...args)
Creates a string expression that substitutes the values ​​of the passed string expressions into the s...
Definition strexpr.h:6897
An "empty" string expression.
Definition strexpr.h:835
Conditional selection string expression.
Definition strexpr.h:1345
Conditional selection string expression.
Definition strexpr.h:1411
Conditional selection string expression.
Definition strexpr.h:1254
Conditional selection string expression.
Definition strexpr.h:1283
A type of string expression that returns N specified characters.
Definition strexpr.h:1120
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Expression constructor.
Definition strexpr.h:6321
A string expression that generates a string replacing all occurrences of the given substring to strin...
Definition strexpr.h:5862
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Constructor.
Definition strexpr.h:5881
A string expression that generates a string replacing all occurrences of the given substring to anoth...
Definition strexpr.h:5756
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Constructor.
Definition strexpr.h:5774
A type of string expression that returns N specified characters.
Definition strexpr.h:1060
A type for using std::basic_string and std::basic_string_view as sources in string expressions.
Definition strexpr.h:1569
Base class for converting string expressions to standard strings.
Definition strexpr.h:659
A class that claims to refer to a null-terminated string.
Definition strexpr.h:5290
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition strexpr.h:5353
constexpr str_src_nt(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5324
constexpr str_src_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition strexpr.h:5315
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5342
constexpr str_src_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5330
The simplest class of an immutable non-owning string.
Definition strexpr.h:5154
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5183
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition strexpr.h:5208
constexpr size_t length() const noexcept
Get the length of the string.
Definition strexpr.h:5194
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition strexpr.h:5237
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition strexpr.h:5248
constexpr str_src(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5173
constexpr bool is_same(str_src< K > other) const noexcept
Check if two objects point to the same string.
Definition strexpr.h:5217
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition strexpr.h:5188
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5201
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition strexpr.h:5261
constexpr bool is_part_of(str_src< K > other) const noexcept
Check if a string is part of another string.
Definition strexpr.h:5226
constexpr str_src(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5167
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:744
Template class for concatenating two string expressions into one using operator +
Definition strexpr.h:682