simstr 1.6.6
Yet another strings library
 
Загрузка...
Поиск...
Не найдено
strexpr.h
1/*
2 * ver. 1.6.6
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>
1605struct ch_traits : std::char_traits<K>{};
1606
1607template<typename T>
1608concept FromIntNumber =
1609 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;
1610
1611template<typename T>
1612concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1613
1614template<typename K, bool I, typename T>
1615struct need_sign { // NOLINT
1616 bool negate;
1617 std::make_unsigned_t<T> val;
1618 constexpr need_sign(T t) : negate(t < 0), val(t < 0 ? std::make_unsigned_t<T>{} - t : t) {}
1619 constexpr void after(K*& ptr) {
1620 if (negate)
1621 *--ptr = '-';
1622 }
1623};
1624
1625template<typename K, typename T>
1626struct need_sign<K, false, T> {
1627 T val;
1628 constexpr need_sign(T t) : val(t){}
1629 constexpr void after(K*&) {}
1630};
1631
1632template<typename K, typename T>
1633constexpr size_t fromInt(K* bufEnd, T val) {
1634 const char* twoDigit =
1635 "0001020304050607080910111213141516171819"
1636 "2021222324252627282930313233343536373839"
1637 "4041424344454647484950515253545556575859"
1638 "6061626364656667686970717273747576777879"
1639 "8081828384858687888990919293949596979899";
1640 if (val) {
1641 need_sign<K, std::is_signed_v<T>, T> store(val);
1642 K* itr = bufEnd;
1643 while (store.val >= 100) {
1644 const char* ptr = twoDigit + (store.val % 100) * 2;
1645 *--itr = static_cast<K>(ptr[1]);
1646 *--itr = static_cast<K>(ptr[0]);
1647 store.val /= 100;
1648 }
1649 if (store.val < 10) {
1650 *--itr = static_cast<K>('0' + store.val);
1651 } else {
1652 const char* ptr = twoDigit + store.val * 2;
1653 *--itr = static_cast<K>(ptr[1]);
1654 *--itr = static_cast<K>(ptr[0]);
1655 }
1656 store.after(itr);
1657 return size_t(bufEnd - itr);
1658 }
1659 bufEnd[-1] = '0';
1660 return 1;
1661}
1662
1663template<typename K, typename T>
1664struct expr_num : expr_to_std_string<expr_num<K, T>> {
1665 using symb_type = K;
1666 using my_type = expr_num<K, T>;
1667
1668 enum { bufSize = 24 };
1669 mutable K buf[bufSize];
1670 mutable T value;
1671
1672 constexpr expr_num(T t) : value(t) {}
1673 constexpr expr_num(expr_num&& t) noexcept : value(t.value) {}
1674
1675 constexpr size_t length() const noexcept {
1676 value = (T)fromInt(buf + bufSize, value);
1677 return (size_t)value;
1678 }
1679 constexpr K* place(K* ptr) const noexcept {
1680 size_t len = (size_t)value;
1681 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1682 return ptr + len;
1683 }
1684};
1685
1696template<typename K, FromIntNumber T>
1697struct convert_to_strexpr<K, T> {
1698 using type = expr_num<K, T>;
1699};
1700
1716template<typename K, FromIntNumber T>
1717constexpr expr_num<K, T> e_num(T t) {
1718 return {t};
1719}
1720
1721namespace f{
1722
1723enum class int_align { none, left, right, center };
1724enum class int_plus_sign {none, plus, space};
1725enum class int_prefix {none, lcase, ucase};
1726
1727enum fmt_kinds : unsigned {
1728 fmt_none = 0,
1729 fmt_align = 1,
1730 fmt_width = 2,
1731 fmt_fill = 4,
1732 fmt_sign = 8,
1733 fmt_upper = 16,
1734 fmt_prefix = 32,
1735 fmt_zero = 64,
1736};
1737
1738template<typename A, typename B, unsigned Kind>
1739struct fmt_info {
1740 using a_t = A;
1741 using b_t = B;
1742 inline static constexpr unsigned kind = Kind;
1743};
1744
1745struct fmt_default {
1746 inline static constexpr unsigned kind = fmt_none;
1747};
1748
1749template<int_align A>
1750struct align_info{
1751 inline static constexpr unsigned kind = fmt_align;
1752};
1753
1754template<size_t Width = 0>
1755struct width_info {
1756 inline static constexpr unsigned kind = fmt_width;
1757};
1758
1759template<unsigned Fill = ' '>
1760struct fill_info {
1761 inline static constexpr unsigned kind = fmt_fill;
1762};
1763
1764template<int_plus_sign S>
1765struct sign_info {
1766 inline static constexpr unsigned kind = fmt_sign;
1767};
1768
1769struct upper_info {
1770 inline static constexpr unsigned kind = fmt_upper;
1771};
1772
1773template<int_prefix>
1774struct prefix_info {
1775 inline static constexpr unsigned kind = fmt_prefix;
1776};
1777
1778struct zero_info {
1779 inline static constexpr unsigned kind = fmt_zero;
1780};
1781
1782template<typename T>
1783concept FmtParam = requires {
1784 {std::remove_cvref_t<T>::kind} -> std::same_as<const unsigned&>;
1785};
1786
1787template<FmtParam A, FmtParam B>
1788requires((A::kind & B::kind) == 0)
1789constexpr auto operator | (const A&, const B&) {
1790 return fmt_info<A, B, A::kind | B::kind>{};
1791}
1792
1793inline constexpr align_info<int_align::left> l;
1794inline constexpr align_info<int_align::right> r;
1795inline constexpr align_info<int_align::center> c;
1796
1797template<size_t W>
1798inline constexpr width_info<W> w;
1799
1800template<unsigned F>
1801inline constexpr fill_info<F> f;
1802
1803inline constexpr sign_info<int_plus_sign::plus> sp;
1804inline constexpr sign_info<int_plus_sign::space> ss;
1805inline constexpr upper_info u;
1806inline constexpr prefix_info<int_prefix::lcase> p;
1807inline constexpr prefix_info<int_prefix::ucase> P;
1808inline constexpr zero_info z;
1809
1810inline constexpr width_info<unsigned(-1)> wp;
1811inline constexpr fmt_default df;
1812
1813template<typename T>
1814struct extract_align : std::false_type {
1815 inline static constexpr int_align align = int_align::none;
1816};
1817
1818template<int_align A>
1819struct extract_align<align_info<A>> : std::true_type {
1820 inline static constexpr int_align align = A;
1821};
1822
1823template<typename A, typename B, unsigned U>
1824struct extract_align<fmt_info<A, B, U>> {
1825 inline static constexpr bool in_a = extract_align<A>::value, in_b = extract_align<B>::value, value = in_a | in_b;
1826 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;
1827};
1828
1829template<typename T>
1830struct extract_width : std::false_type {
1831 inline static constexpr size_t width = 0;
1832};
1833
1834template<size_t W>
1835struct extract_width<width_info<W>> : std::true_type {
1836 inline static constexpr size_t width = W;
1837};
1838
1839template<typename A, typename B, unsigned U>
1840struct extract_width<fmt_info<A, B, U>> {
1841 inline static constexpr bool in_a = extract_width<A>::value, in_b = extract_width<B>::value, value = in_a | in_b;
1842 inline static constexpr size_t width = extract_width<std::conditional_t<in_a, A, std::conditional_t<in_b, B, width_info<0>>>>::width;
1843};
1844
1845template<typename T>
1846struct extract_fill : std::false_type {
1847 inline static constexpr unsigned fill = ' ';
1848};
1849
1850template<unsigned F>
1851struct extract_fill<fill_info<F>> : std::true_type {
1852 inline static constexpr unsigned fill = F;
1853};
1854
1855template<typename A, typename B, unsigned U>
1856struct extract_fill<fmt_info<A, B, U>> {
1857 inline static constexpr bool in_a = extract_fill<A>::value, in_b = extract_fill<B>::value, value = in_a | in_b;
1858 inline static constexpr unsigned fill = extract_fill<std::conditional_t<in_a, A, std::conditional_t<in_b, B, fill_info<' '>>>>::fill;
1859};
1860
1861template<typename T>
1862struct extract_sign : std::false_type {
1863 inline static constexpr int_plus_sign sign = int_plus_sign::none;
1864};
1865
1866template<int_plus_sign S>
1867struct extract_sign<sign_info<S>> : std::true_type {
1868 inline static constexpr int_plus_sign sign = S;
1869};
1870
1871template<typename A, typename B, unsigned U>
1872struct extract_sign<fmt_info<A, B, U>> {
1873 inline static constexpr bool in_a = extract_sign<A>::value, in_b = extract_sign<B>::value, value = in_a | in_b;
1874 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;
1875};
1876
1877template<typename T>
1878struct extract_upper : std::false_type {};
1879
1880template<>
1881struct extract_upper<upper_info> : std::true_type {};
1882
1883template<typename A, typename B, unsigned U>
1884struct extract_upper<fmt_info<A, B, U>> {
1885 inline static constexpr bool in_a = extract_upper<A>::value, in_b = extract_upper<B>::value,
1886 value = extract_upper<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1887};
1888
1889template<typename T>
1890struct extract_prefix : std::false_type{
1891 inline static constexpr int_prefix prefix = int_prefix::none;
1892};
1893
1894template<int_prefix P>
1895struct extract_prefix<prefix_info<P>> : std::true_type {
1896 inline static constexpr int_prefix prefix = P;
1897};
1898
1899template<typename A, typename B, unsigned U>
1900struct extract_prefix<fmt_info<A, B, U>> {
1901 inline static constexpr bool in_a = extract_prefix<A>::value, in_b = extract_prefix<B>::value, value = in_a | in_b;
1902 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;
1903};
1904
1905template<typename T>
1906struct extract_zero : std::false_type{};
1907
1908template<>
1909struct extract_zero<zero_info> : std::true_type {};
1910
1911template<typename A, typename B, unsigned U>
1912struct extract_zero<fmt_info<A, B, U>> {
1913 inline static constexpr bool in_a = extract_zero<A>::value, in_b = extract_zero<B>::value,
1914 value = extract_zero<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1915};
1916
1917template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1918struct fmt_params {
1919 inline static constexpr int_align align = Align;
1920 inline static constexpr unsigned width = Width;
1921 inline static constexpr unsigned fill = Fill;
1922 inline static constexpr int_plus_sign sign = Sign;
1923 inline static constexpr bool upper = Upper;
1924 inline static constexpr int_prefix prefix = Prefix;
1925 inline static constexpr bool zero = Zero;
1926};
1927
1928template<FmtParam T>
1929using p_to_fmt_t = fmt_params<
1930 extract_align<T>::align,
1931 extract_width<T>::width,
1932 extract_fill<T>::fill,
1933 extract_sign<T>::sign,
1934 extract_upper<T>::value,
1935 extract_prefix<T>::prefix,
1936 extract_zero<T>::value>;
1937
1938template<FmtParam T>
1939using to_fmt_t = p_to_fmt_t<std::remove_cvref_t<T>>;
1940
1941template<typename T>
1942struct is_fmt_params : std::false_type{};
1943
1944template<int_align Align, unsigned Width, unsigned Fill, int_plus_sign Sign, bool Upper, int_prefix Prefix, bool Zero>
1945struct is_fmt_params<fmt_params<Align, Width, Fill, Sign, Upper, Prefix, Zero>> : std::true_type{};
1946
1947template<typename T>
1948concept FmtParamSet = is_fmt_params<T>::value;
1949
1950struct fmt_end{};
1951
1952template<f::FmtParam F>
1953constexpr auto operator|(const F& f, fmt_end) {
1954 return f;
1955}
1956
1957template<char C = 0, char...Chars>
1958constexpr auto parse_fmt_symbol();
1959
1960template<unsigned N, char C = 'Z', char...Chars>
1961constexpr auto parse_width() {
1962 if constexpr (C >= '0' && C <= '9') {
1963 return parse_width<N * 10 + C - '0', Chars...>();
1964 } else {
1965 return w<N> | parse_fmt_symbol<C, Chars...>();
1966 }
1967}
1968
1969template<unsigned N, char C = 'Z', char...Chars>
1970constexpr auto parse_fill() {
1971 if constexpr (C >= '0' && C <= '9') {
1972 return parse_fill<N * 16 + C - '0', Chars...>();
1973 } else if constexpr (C >= 'a' && C <= 'f') {
1974 return parse_fill<N * 16 + C - 'a' + 10, Chars...>();
1975 } else if constexpr (C >= 'A' && C <= 'F') {
1976 return parse_fill<N * 16 + C - 'A' + 10, Chars...>();
1977 } else {
1978 return f<N> | parse_fmt_symbol<C, Chars...>();
1979 }
1980}
1981
1982template<char C, char...Chars>
1983constexpr auto parse_fmt_symbol() {
1984 if constexpr (C == '0') {
1985 return z | parse_fmt_symbol<Chars...>();
1986 } else if constexpr (C == 'a') {
1987 return p | parse_fmt_symbol<Chars...>();
1988 } else if constexpr (C == 'A') {
1989 return P | parse_fmt_symbol<Chars...>();
1990 } else if constexpr (C == 'b') {
1991 return l | parse_fmt_symbol<Chars...>();
1992 } else if constexpr (C == 'c') {
1993 return c | parse_fmt_symbol<Chars...>();
1994 } else if constexpr (C == 'd') {
1995 return r | parse_fmt_symbol<Chars...>();
1996 } else if constexpr (C == 'e') {
1997 return sp | parse_fmt_symbol<Chars...>();
1998 } else if constexpr (C == 'f') {
1999 return ss | parse_fmt_symbol<Chars...>();
2000 } else if constexpr (C == 'E') {
2001 return u | parse_fmt_symbol<Chars...>();
2002 } else if constexpr (C == '\'') {
2003 return parse_fmt_symbol<Chars...>();
2004 } else if constexpr (C == 'F') {
2005 return parse_fill<0, Chars...>();
2006 } else if constexpr (C >= '1' && C <= '9') {
2007 return parse_width<C - '0', Chars...>();
2008 } else {
2009 return fmt_end{};
2010 }
2011}
2012
2013template<unsigned R, typename T>
2014struct fmt_radix_info {};
2015
2016template<unsigned N, char C = 0, char...Chars>
2017constexpr auto parse_radix() {
2018 if constexpr (C >='0' && C <= '9') {
2019 return parse_radix<N * 10 + C - '0', Chars...>();
2020 } else if constexpr (C == 0) {
2021 return fmt_radix_info<N, to_fmt_t<fmt_default>>{};
2022 } else {
2023 return fmt_radix_info<N, decltype(parse_fmt_symbol<C, Chars...>())>{};
2024 }
2025};
2026
2027template<char C1, char C2, char C3, char...Chars>
2028constexpr auto skip_0x() {
2029 static_assert(C1 == '0' && C2 == 'x' && "Fmt symbols must begin with 0x");
2030 static_assert(C3 >= '1' && C3 <= '9' && "Radix must begin with 1-9");
2031 return parse_radix<C3 - '0', Chars...>();
2032}
2033
2034} // namespace f
2035
2036template<typename K, bool Ucase>
2037inline 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'),
2038 K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'), K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'),
2039 K(Ucase ? 'F' : 'f'), K(Ucase ? 'G' : 'g'), K(Ucase ? 'H' : 'h'), K(Ucase ? 'I' : 'i'), K(Ucase ? 'J' : 'j'),
2040 K(Ucase ? 'K' : 'k'), K(Ucase ? 'L' : 'l'), K(Ucase ? 'M' : 'm'), K(Ucase ? 'N' : 'n'), K(Ucase ? 'O' : 'o'),
2041 K(Ucase ? 'P' : 'p'), K(Ucase ? 'Q' : 'q'), K(Ucase ? 'R' : 'r'), K(Ucase ? 'S' : 's'), K(Ucase ? 'T' : 't'),
2042 K(Ucase ? 'U' : 'u'), K(Ucase ? 'V' : 'v'), K(Ucase ? 'W' : 'w'), K(Ucase ? 'X' : 'x'), K(Ucase ? 'Y' : 'y'),
2043 K(Ucase ? 'Z' : 'z'),
2044};
2045
2046template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2047struct expr_integer_src {
2048 T value_;
2049 unsigned width_{};
2050 constexpr expr_integer_src(T v) : value_(v){}
2051 constexpr expr_integer_src(T v, unsigned w) : value_(v), width_(w){}
2052
2053 template<is_std_string_v S>
2054 operator S() const;
2055
2056 template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2057 operator S() const;
2058};
2059
2060template<is_one_of_char_v K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2061requires (Radix > 1 && Radix <= 36)
2062struct expr_integer : expr_to_std_string<expr_integer<K, T, Radix, FP>> {
2063 using symb_type = K;
2064 using my_type = expr_num<K, T>;
2065
2066 enum { bufSize = 64 };
2067 mutable K buf[bufSize];
2068 mutable T value_;
2069 unsigned width_{};
2070
2071 mutable bool negate_{};
2072
2073 constexpr expr_integer(T t) : value_(t){}
2074 constexpr expr_integer(T t, unsigned w) : value_(t), width_(w){}
2075 constexpr expr_integer(const expr_integer_src<T, Radix, FP>& v) : value_(v.value_), width_(v.width_){}
2076
2077 constexpr expr_integer(expr_integer&& t) noexcept : value_(t.value_), width_(t.width_){}
2078
2079 constexpr size_t length() const noexcept {
2080 K* bufEnd = std::end(buf), *itr = bufEnd;
2081
2082 if (value_) {
2083 need_sign<K, std::is_signed_v<T>, T> store(value_);
2084 while (store.val) {
2085 *--itr = digits_symbols<K, FP::upper>[store.val % Radix];
2086 store.val /= Radix;
2087 }
2088 if constexpr (std::is_signed_v<T>) {
2089 negate_ = store.negate;
2090 }
2091 } else {
2092 *--itr = digits_symbols<K, FP::upper>[0];
2093 }
2094 size_t len = bufEnd - itr;
2095 value_ = T(len);
2096 if constexpr (std::is_signed_v<T>) {
2097 if constexpr (FP::sign != f::int_plus_sign::none) {
2098 len++;
2099 } else {
2100 if (negate_) {
2101 len++;
2102 }
2103 }
2104 }
2105 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2106 len++; // add 0
2107 if constexpr (Radix != 8) { // for octo just add 0,
2108 len++; // for other b or x
2109 }
2110 }
2111 if constexpr (FP::width == unsigned(-1)) {
2112 return std::max<size_t>(width_, len);
2113 } else {
2114 return std::max<size_t>(FP::width, len);
2115 }
2116 }
2117 constexpr K* place(K* ptr) const noexcept {
2118 size_t len = (size_t)value_, all_len = len;
2119 if constexpr (std::is_signed_v<T>) {
2120 if constexpr (FP::sign != f::int_plus_sign::none) {
2121 all_len++;
2122 } else {
2123 if (negate_) {
2124 all_len++;
2125 }
2126 }
2127 }
2128 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2129 all_len++; // add 0
2130 if constexpr (Radix != 8) { // for octo just add 0,
2131 all_len++; // for other b or x
2132 }
2133 }
2134 if constexpr (FP::zero) {
2135 if constexpr (std::is_signed_v<T>) {
2136 if (negate_) {
2137 *ptr++ = K('-');
2138 } else {
2139 if constexpr (FP::sign == f::int_plus_sign::plus) {
2140 *ptr++ = K('+');
2141 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2142 *ptr++ = K(' ');
2143 }
2144 }
2145 }
2146 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2147 *ptr++ = K('0');
2148 if constexpr (Radix == 2) {
2149 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2150 } else if constexpr (Radix == 16) {
2151 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2152 }
2153 }
2154 size_t before = 0;
2155 if constexpr (FP::width == unsigned(-1)) {
2156 if (width_ > all_len) {
2157 before = width_ - all_len;
2158 }
2159 } else {
2160 if (FP::width > all_len) {
2161 before = FP::width - all_len;
2162 }
2163 }
2164 if (before) {
2165 ch_traits<K>::assign(ptr, before, K('0'));
2166 ptr += before;
2167 }
2168 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2169 ptr += len;
2170 } else {
2171 size_t before = 0, after = 0;
2172 if constexpr (FP::width == unsigned(-1)) {
2173 if (width_ > all_len) {
2174 if constexpr (FP::align == f::int_align::left) {
2175 after = width_ - all_len;
2176 } else if constexpr (FP::align == f::int_align::center) {
2177 before = (width_ - all_len) / 2;
2178 after = width_ - all_len - before;
2179 } else {
2180 before = width_ - all_len;
2181 }
2182 }
2183 } else {
2184 if (FP::width > all_len) {
2185 if constexpr (FP::align == f::int_align::left) {
2186 after = FP::width - all_len;
2187 } else if constexpr (FP::align == f::int_align::center) {
2188 before = (FP::width - all_len) / 2;
2189 after = FP::width - all_len - before;
2190 } else {
2191 before = FP::width - all_len;
2192 }
2193 }
2194 }
2195 if (before) {
2196 ch_traits<K>::assign(ptr, before, K(FP::fill));
2197 ptr += before;
2198 }
2199 if constexpr (std::is_signed_v<T>) {
2200 if (negate_) {
2201 *ptr++ = K('-');
2202 } else {
2203 if constexpr (FP::sign == f::int_plus_sign::plus) {
2204 *ptr++ = K('+');
2205 } else if constexpr (FP::sign == f::int_plus_sign::space) {
2206 *ptr++ = K(' ');
2207 }
2208 }
2209 }
2210 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2211 *ptr++ = K('0');
2212 if constexpr (Radix == 2) {
2213 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('b') : K('B');
2214 } else if constexpr (Radix == 16) {
2215 *ptr++ = FP::prefix == f::int_prefix::lcase ? K('x') : K('X');
2216 }
2217 }
2218 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2219 ptr += len;
2220
2221 if (after) {
2222 ch_traits<K>::assign(ptr, after, K(FP::fill));
2223 ptr += after;
2224 }
2225 }
2226 return ptr;
2227 }
2228};
2229
2230template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2231template<is_std_string_v S>
2232expr_integer_src<T, Radix, FP>::operator S() const {
2233 using st = typename S::value_type;
2234 using Al = typename S::allocator_type;
2235 return to_std_string<st, Al>(expr_integer<st, T, Radix, FP>{value_, width_});
2236}
2237
2238template<FromIntNumber T, unsigned Radix, f::FmtParamSet FP> requires (Radix > 1 && Radix <= 36)
2239template<typename S> requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2240expr_integer_src<T, Radix, FP>::operator S() const {
2241 using st = typename S::symb_type;
2242 return S{expr_integer<st, T, Radix, FP>{value_, width_}};
2243}
2244
2245template<typename K, FromIntNumber T, unsigned Radix, f::FmtParamSet FP>
2246struct convert_to_strexpr<K, expr_integer_src<T, Radix, FP>> {
2247 using type = expr_integer<K, T, Radix, FP>;
2248};
2249
2250template<unsigned R, typename T, typename F>
2251struct flags_checker {
2252 using val_t = T;
2253 using flags_t = f::to_fmt_t<F>;
2254 inline static constexpr unsigned Radix = R;
2255};
2256
2257template<typename T, bool ArgWidth>
2258concept good_int_flags =
2259 T::Radix > 1 && T::Radix <= 36
2260 // Должно быть задано только или выравнивание, или заполнение нулём.
2261 // Only either alignment or zero-padding must be specified.
2262 && (T::flags_t::align == f::int_align::none || !T::flags_t::zero)
2263 // Флаг вывода знака должен задаваться только для знаковых чисел
2264 // The sign output flag should only be set for signed numbers.
2265 && (std::is_signed_v<typename T::val_t> || T::flags_t::sign == f::int_plus_sign::none)
2266 // Неверное поле ширины
2267 // Invalid width field
2268 && (T::flags_t::width == unsigned(-1)) == ArgWidth
2269 // Префикс может быть только при основании 2, 8, 16
2270 // Prefix may by only for radix 2, 8, 16
2271 && (T::flags_t::prefix == f::int_prefix::none || T::Radix == 2 || T::Radix == 8 || T::Radix == 16);
2272
2380template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, false>
2381constexpr auto e_int(T v) {
2382 using fmt_param = f::to_fmt_t<decltype(fp)>;
2383 return expr_integer_src<T, R, fmt_param>{v};
2384}
2385
2389template<unsigned R, auto fp = f::df, FromIntNumber T> requires good_int_flags<flags_checker<R, T, decltype(fp)>, true>
2390constexpr auto e_int(T v, unsigned w) {
2391 using fmt_param = f::to_fmt_t<decltype(fp)>;
2392 return expr_integer_src<T, R, fmt_param>{v, w};
2393}
2394
2395template<typename T, unsigned R, typename F> requires good_int_flags<flags_checker<R, T, F>, false>
2396auto operator / (T v, const f::fmt_radix_info<R, F>&) {
2397 return expr_integer_src<T, R, f::to_fmt_t<F>>{v};
2398}
2399
2400template<typename K>
2401struct expr_real : expr_to_std_string<expr_real<K>> {
2402 using symb_type = K;
2403 mutable u8s buf[40];
2404 mutable size_t l;
2405 double v;
2406 constexpr expr_real(double d) : v(d) {}
2407 constexpr expr_real(float d) : v(d) {}
2408
2409 size_t length() const noexcept {
2410 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
2411 l = ec != std::errc{} ? 0 : ptr - buf;
2412 return l;
2413 }
2414 K* place(K* ptr) const noexcept {
2415 if constexpr (sizeof(K) == sizeof(buf[0])) {
2416 ch_traits<K>::copy(ptr, (K*)buf, l);
2417 } else {
2418 for (size_t i = 0; i < l; i++) {
2419 ptr[i] = buf[i];
2420 }
2421 }
2422 return ptr + l;
2423 }
2424};
2425
2436template<typename K, std::floating_point T>
2437struct convert_to_strexpr<K, T> {
2438 using type = expr_real<K>;
2439};
2440
2452template<typename K> requires is_one_of_char_v<K>
2453inline constexpr expr_real<K> e_num(double t) {
2454 return {t};
2455}
2456
2457template<FromIntNumber Val, bool All, bool Ucase, bool Ox>
2458struct expr_hex_src {
2459 explicit constexpr expr_hex_src(Val v) : v_(v){}
2460 Val v_;
2461};
2462
2463template<typename K, FromIntNumber Val, bool All, bool Ucase, bool Ox>
2464struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
2465 using symb_type = K;
2466 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
2467 mutable K buf_[sizeof(Val) * 2]{};
2468
2469 explicit constexpr expr_hex(Val v) : v_(v){}
2470 constexpr expr_hex(const expr_hex_src<Val, All, Ucase, Ox>& v) : v_(v.v_){}
2471
2472 constexpr size_t length() const noexcept {
2473 K *ptr = buf_ + std::size(buf_);
2474 size_t l = 0;
2475 for (;;) {
2476 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2477 v_.val >>= 4;
2478 l++;
2479 if (v_.val) {
2480 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2481 v_.val >>= 4;
2482 l++;
2483 }
2484 if (!v_.val) {
2485 if constexpr (All) {
2486 if (size_t need = sizeof(Val) * 2 - l) {
2487 ch_traits<K>::assign(buf_, need, K('0'));
2488 }
2489 l = sizeof(Val) * 2;
2490 }
2491 break;
2492 }
2493 }
2494 v_.val = l;
2495 if constexpr (std::is_signed_v<Val>) {
2496 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
2497 }
2498 return l + (Ox ? 2 : 0);
2499 }
2500 constexpr K* place(K* ptr) const noexcept {
2501 if constexpr (std::is_signed_v<Val>) {
2502 if (v_.negate) {
2503 *ptr++ = K('-');
2504 }
2505 }
2506 if constexpr (Ox) {
2507 *ptr++ = K('0');
2508 *ptr++ = K('x');
2509 }
2510 if constexpr (All) {
2511 ch_traits<K>::copy(ptr, buf_, sizeof(Val) * 2);
2512 return ptr + sizeof(Val) * 2;
2513 } else {
2514 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
2515 return ptr + v_.val;
2516 }
2517 }
2518};
2519
2525enum HexFlags : unsigned {
2526 Short = 1,
2527 No0x = 2, //< without 0x prefix
2528 Lcase = 4, //< Use lower case
2529};
2530
2581template<unsigned Flags = 0, FromIntNumber T>
2582constexpr auto e_hex(T v) {
2583 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2584}
2585
2586template<char c = 0, char...Chars>
2587constexpr unsigned parse_f16_flags() {
2588 if constexpr (c == '1') {
2589 return HexFlags::Short | parse_f16_flags<Chars...>();
2590 } else if constexpr (c == '2') {
2591 return HexFlags::Lcase | parse_f16_flags<Chars...>();
2592 } else if constexpr (c == '3') {
2593 return HexFlags::No0x | parse_f16_flags<Chars...>();
2594 } else {
2595 return 0;
2596 }
2597}
2598
2599template<unsigned N>
2600struct f16flags{};
2601
2602template<FromIntNumber T, unsigned Flags>
2603constexpr auto operator/(T v, const f16flags<Flags>&) {
2604 return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2605}
2606
2607inline namespace literals {
2608template<char...Chars>
2609constexpr auto operator""_f16() {
2610 return f16flags<parse_f16_flags<Chars...>()>{};
2611}
2612} // namespace literals
2613
2622template<typename K, typename Val, bool All, bool Ucase, bool Ox>
2623struct convert_to_strexpr<K, expr_hex_src<Val, All, Ucase, Ox>> {
2624 using type = expr_hex<K, Val, All, Ucase, Ox>;
2625};
2626
2627template<typename K>
2628struct expr_pointer : expr_hex<K, uintptr_t, true, true, true> {
2629 constexpr expr_pointer(const void* ptr) : expr_hex<K, uintptr_t, true, true, true>((uintptr_t)ptr){}
2630};
2631
2640template<typename K, typename T>
2641struct convert_to_strexpr<K, const T*> {
2642 using type = expr_pointer<K>;
2643};
2644
2645template<typename K, StrExprForType<K> A, bool Left>
2646struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
2647 using symb_type = K;
2648 K symbol_;
2649 size_t width_;
2650 const A& a_;
2651 mutable size_t alen_{};
2652 constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
2653
2654 constexpr size_t length() const noexcept {
2655 alen_ = a_.length();
2656 return std::max(alen_, width_);
2657 }
2658 constexpr K* place(K* ptr) const noexcept {
2659 if (alen_ >= width_) {
2660 return (K*)a_.place((typename A::symb_type*)ptr);
2661 }
2662 size_t w = width_ - alen_;
2663 if constexpr (Left) {
2664 ch_traits<K>::assign(ptr, w, symbol_);
2665 ptr += w;
2666 return (K*)a_.place((typename A::symb_type*)ptr);
2667 } else {
2668 ptr = (K*)a_.place((typename A::symb_type*)ptr);
2669 ch_traits<K>::assign(ptr, w, symbol_);
2670 return ptr + w;
2671 }
2672 }
2673};
2674
2696template<StrExpr A, typename K = typename A::symb_type>
2697expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
2698 return {symbol, width, a};
2699}
2700
2722template<StrExpr A, typename K = typename A::symb_type>
2723expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
2724 return {symbol, width, a};
2725}
2726
2727/*
2728* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
2729* K - тип символов строки
2730* T - тип контейнера строк (vector, list)
2731* I - длина разделителя в символах
2732* tail - добавлять разделитель после последнего элемента контейнера.
2733* Если контейнер пустой, разделитель в любом случае не добавляется
2734* skip_empty - пропускать пустые строки без добавления разделителя
2735* To create string concatenations with vectors and lists joined by a constant delimiter
2736* K is the symbols
2737* T - type of string container (vector, list)
2738* I - length of separator in characters
2739* tail - add a separator after the last element of the container.
2740* If the container is empty, the separator is not added anyway
2741* skip_empty - skip empty lines without adding a separator
2742*/
2743template<typename K, typename T, size_t I, bool tail, bool skip_empty>
2744struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
2745 using symb_type = K;
2746 using my_type = expr_join<K, T, I, tail, skip_empty>;
2747
2748 const T& s;
2749 const K* delim;
2750 constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
2751
2752 constexpr size_t length() const noexcept {
2753 size_t l = 0;
2754 for (const auto& t: s) {
2755 size_t len = t.length();
2756 if (len > 0 || !skip_empty) {
2757 if (I > 0 && l > 0) {
2758 l += I;
2759 }
2760 l += len;
2761 }
2762 }
2763 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
2764 }
2765 constexpr K* place(K* ptr) const noexcept {
2766 if (s.empty()) {
2767 return ptr;
2768 }
2769 K* write = ptr;
2770 for (const auto& t: s) {
2771 size_t copyLen = t.length();
2772 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
2773 ch_traits<K>::copy(write, delim, I);
2774 write += I;
2775 }
2776 ch_traits<K>::copy(write, t.data(), copyLen);
2777 write += copyLen;
2778 }
2779 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
2780 ch_traits<K>::copy(write, delim, I);
2781 write += I;
2782 }
2783 return write;
2784 }
2785};
2786
2800template<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>
2801inline constexpr auto e_join(const T& s, L&& d) {
2802 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
2803}
2804
2805template<size_t N>
2806concept is_const_pattern = N > 1 && N <= 17;
2807
2808template<typename K, size_t I>
2809struct _ascii_mask { // NOLINT
2810 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
2811};
2812
2813template<typename K>
2814struct _ascii_mask<K, 0> {
2815 constexpr static const size_t value = 0;
2816};
2817
2818template<typename K>
2819struct ascii_mask { // NOLINT
2820 using uns = std::make_unsigned_t<K>;
2821 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
2822 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
2823};
2824
2825template<typename K>
2826constexpr inline bool isAsciiUpper(K k) {
2827 return k >= 'A' && k <= 'Z';
2828}
2829
2830template<typename K>
2831constexpr inline bool isAsciiLower(K k) {
2832 return k >= 'a' && k <= 'z';
2833}
2834
2835template<typename K>
2836constexpr inline K makeAsciiLower(K k) {
2837 return isAsciiUpper(k) ? k | 0x20 : k;
2838}
2839
2840template<typename K>
2841constexpr inline K makeAsciiUpper(K k) {
2842 return isAsciiLower(k) ? k & ~0x20 : k;
2843}
2844
2845enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
2846template<TrimSides S, typename K, size_t N, bool withSpaces = false>
2847struct trim_operator;
2848
2849template<size_t I>
2850struct digits_selector {
2851 using wider_type = uint16_t;
2852};
2853
2854template<>
2855struct digits_selector<2> {
2856 using wider_type = uint32_t;
2857};
2858
2859template<>
2860struct digits_selector<4> {
2861 using wider_type = uint64_t;
2862};
2863
2874
2875template<bool CanNegate, bool CheckOverflow, typename T>
2876struct result_type_selector { // NOLINT
2877 using type = T;
2878};
2879
2880template<typename T>
2881struct result_type_selector<true, false, T> {
2882 using type = std::make_unsigned_t<T>;
2883};
2884
2885template<unsigned Base>
2886constexpr unsigned digit_width() {
2887 if (Base <=2) {
2888 return 1;
2889 }
2890 if (Base <= 4) {
2891 return 2;
2892 }
2893 if (Base <= 8) {
2894 return 3;
2895 }
2896 if (Base <= 16) {
2897 return 4;
2898 }
2899 if (Base <= 32) {
2900 return 5;
2901 }
2902 return 6;
2903}
2904
2905template<typename T, unsigned Base>
2906constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
2907
2908template<typename T>
2909struct convert_result {
2910 T value;
2912 size_t read;
2913};
2914
2915struct int_convert { // NOLINT
2916 inline static constexpr uint8_t NUMBERS[] = {
2917 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,
2918 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,
2919 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,
2920 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,
2921 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,
2922 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2923 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2924 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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};
2927
2928 template<typename K, unsigned Base>
2929 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2930 auto us = static_cast<std::make_unsigned_t<K>>(s);
2931 if constexpr (Base <= 10) {
2932 return us - '0';
2933 } else {
2934 if constexpr (sizeof(K) == 1) {
2935 return NUMBERS[us];
2936 } else {
2937 return us < 256 ? NUMBERS[us] : us;
2938 }
2939 }
2940 }
2941
2942 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
2943 requires(Base != 0)
2944 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
2945 using u_type = std::make_unsigned_t<T>;
2946 #ifndef HAS_BUILTIN_OVERFLOW
2947 u_type maxMult = 0, maxAdd = 0;
2948 if constexpr (CheckOverflow) {
2949 maxMult = std::numeric_limits<u_type>::max() / Base;
2950 maxAdd = std::numeric_limits<u_type>::max() % Base;
2951 }
2952 #endif
2953 u_type number = 0;
2954 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2956 const K* from = current;
2957
2958 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2959
2960 if (no_need_check_o_f) {
2961 for (;;) {
2962 const u_type digit = toDigit<K, Base>(*current);
2963 if (digit >= Base) {
2964 break;
2965 }
2966 number = number * Base + digit;
2967 if (++current == end) {
2969 break;
2970 }
2971 }
2972 } else {
2973 for (;maxDigits; maxDigits--) {
2974 const u_type digit = toDigit<K, Base>(*current);
2975 if (digit >= Base) {
2976 break;
2977 }
2978 number = number * Base + digit;
2979 ++current;
2980 }
2981 if (!maxDigits) {
2982 // Прошли все цифры, дальше надо с проверкой на overflow
2983 // All numbers have passed, then we need to check for overflow
2984 for (;;) {
2985 const u_type digit = toDigit<K, Base>(*current);
2986 if (digit >= Base) {
2987 break;
2988 }
2989 #ifdef HAS_BUILTIN_OVERFLOW
2990 if (__builtin_mul_overflow(number, Base, &number) ||
2991 __builtin_add_overflow(number, digit, &number)) {
2992 #else
2993 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2994 number = number * Base + digit;
2995 } else {
2996 #endif
2998 while(++current < end) {
2999 if (toDigit<K, Base>(*current) >= Base) {
3000 break;
3001 }
3002 }
3003 break;
3004 }
3005 if (++current == end) {
3007 break;
3008 }
3009 }
3010 }
3011 }
3012 T result;
3013 if constexpr (std::is_signed_v<T>) {
3014 result = negate ? 0 - number : number;
3015 if constexpr (CheckOverflow) {
3016 if (error != IntConvertResult::Overflow) {
3017 if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
3019 }
3020 }
3021 }
3022 } else {
3023 result = number;
3024 }
3025 if (error == IntConvertResult::NotNumber && current > from) {
3027 }
3028 return {result, error, size_t(current - start)};
3029 }
3030public:
3031 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
3032 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
3033 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
3034 // 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
3035 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
3036 requires(Base == -1 || (Base < 37 && Base != 1))
3037 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
3038 const K *ptr = start, *end = ptr + len;
3039 bool negate = false;
3040 if constexpr (SkipWs) {
3041 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
3042 ptr++;
3043 }
3044 if (ptr != end) {
3045 if constexpr (std::is_signed_v<T>) {
3046 if constexpr (AllowSign) {
3047 // Может быть число, +число или -число
3048 // Can be a number, +number or -number
3049 if (*ptr == '+') {
3050 ptr++;
3051 } else if (*ptr == '-') {
3052 negate = true;
3053 ptr++;
3054 }
3055 } else {
3056 // Может быть число или -число
3057 // Can be a number or -number
3058 if (*ptr == '-') {
3059 negate = true;
3060 ptr++;
3061 }
3062 }
3063 } else if constexpr (AllowSign) {
3064 // Может быть число или +число
3065 // Can be a number or +number
3066 if (*ptr == '+') {
3067 ptr++;
3068 }
3069 }
3070 }
3071 if (ptr != end) {
3072 if constexpr (Base == 0 || Base == -1) {
3073 if (*ptr == '0') {
3074 ptr++;
3075 if (ptr != end) {
3076 if (*ptr == 'x' || *ptr == 'X') {
3077 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
3078 }
3079 if constexpr (Base == -1) {
3080 if (*ptr == 'b' || *ptr == 'B') {
3081 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
3082 }
3083 if (*ptr == 'o' || *ptr == 'O') {
3084 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
3085 }
3086 }
3087 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
3088 }
3089 return {0, IntConvertResult::Success, size_t(ptr - start)};
3090 }
3091 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
3092 } else
3093 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
3094 }
3095 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
3096 }
3097};
3098
3099template<typename K, typename Impl>
3100class null_terminated {
3101public:
3108 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
3109};
3110
3111template<typename K, typename Impl, bool Mutable> class buffer_pointers;
3112
3121template<typename K, typename Impl>
3122class buffer_pointers<K, Impl, false> {
3123 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
3124public:
3131 constexpr const K* data() const { return d().symbols(); }
3138 constexpr const K* begin() const { return d().symbols(); }
3145 constexpr const K* end() const { return d().symbols() + d().length(); }
3152 constexpr const K* cbegin() const { return d().symbols(); }
3159 constexpr const K* cend() const { return d().symbols() + d().length(); }
3160};
3161
3162template<typename K, typename Impl>
3163class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
3164 constexpr Impl& d() { return *static_cast<Impl*>(this); }
3165 using base = buffer_pointers<K, Impl, false>;
3166public:
3173 constexpr const K* data() const { return base::data(); }
3180 constexpr const K* begin() const { return base::begin(); }
3187 constexpr const K* end() const { return base::end(); }
3194 constexpr const K* cbegin() const { return base::cbegin(); }
3201 constexpr const K* cend() const { return base::cend(); }
3208 constexpr K* data() { return d().str(); }
3215 constexpr K* begin() { return d().str(); }
3222 constexpr K* end() { return d().str() + d().length(); }
3223};
3224
3231template<typename K, typename StrSrc>
3232class SplitterBase {
3233 using str_t = StrSrc;
3234 str_t text_;
3235 str_t delim_;
3236
3237public:
3238 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
3243 constexpr bool is_done() const {
3244 return text_.length() == str::npos;
3245 }
3246
3252 constexpr str_t next() {
3253 if (!text_.length()) {
3254 auto ret = text_;
3255 text_.str++;
3256 text_.len--;
3257 return ret;
3258 } else if (text_.length() == str::npos) {
3259 return {nullptr, 0};
3260 }
3261 size_t pos = text_.find(delim_), next = 0;
3262 if (pos == str::npos) {
3263 pos = text_.length();
3264 next = pos + 1;
3265 } else {
3266 next = pos + delim_.length();
3267 }
3268 str_t result{text_.str, pos};
3269 text_.str += next;
3270 text_.len -= next;
3271 return result;
3272 }
3273};
3274
3299template<typename K, typename StrRef, typename Impl, bool Mutable>
3300class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
3301 constexpr const Impl& d() const noexcept {
3302 return *static_cast<const Impl*>(this);
3303 }
3304 constexpr size_t _len() const noexcept {
3305 return d().length();
3306 }
3307 constexpr const K* _str() const noexcept {
3308 return d().symbols();
3309 }
3310 constexpr bool _is_empty() const noexcept {
3311 return d().is_empty();
3312 }
3313
3314public:
3315 using symb_type = K;
3316 using str_piece = StrRef;
3317 using traits = ch_traits<K>;
3318 using uns_type = std::make_unsigned_t<K>;
3319 using my_type = Impl;
3320 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3321 str_src_algs() = default;
3322
3335 constexpr K* place(K* ptr) const noexcept {
3336 size_t myLen = _len();
3337 traits::copy(ptr, _str(), myLen);
3338 return ptr + myLen;
3339 }
3340
3350 void copy_to(K* buffer, size_t bufSize) {
3351 size_t tlen = std::min(_len(), bufSize - 1);
3352 traits::copy(buffer, _str(), tlen);
3353 buffer[tlen] = 0;
3354 }
3355
3361 constexpr size_t size() const {
3362 return _len();
3363 }
3364
3371 template<typename D = K> requires is_equal_str_type_v<K, D>
3372 constexpr std::basic_string_view<D> to_sv() const noexcept {
3373 return {(const D*)_str(), _len()};
3374 }
3375
3381 template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
3382 constexpr operator std::basic_string_view<D, Traits>() const {
3383 return {(const D*)_str(), _len()};
3384 }
3385
3391 template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
3392 constexpr std::basic_string<D, Traits, Allocator> to_string() const {
3393 return {(const D*)_str(), _len()};
3394 }
3395
3401 template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
3402 constexpr operator std::basic_string<D, Traits, Allocator>() const {
3403 return {(const D*)_str(), _len()};
3404 }
3405
3411 constexpr operator str_piece() const noexcept {
3412 return str_piece{_str(), _len()};
3413 }
3414
3420 constexpr str_piece to_str() const noexcept {
3421 return {_str(), _len()};
3422 }
3423
3446 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3447 size_t myLen = _len(), idxStart = from >= 0 ? from : myLen > -from ? myLen + from : 0,
3448 idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
3449 if (idxEnd > myLen)
3450 idxEnd = myLen;
3451 if (idxStart > idxEnd)
3452 idxStart = idxEnd;
3453 return str_piece{_str() + idxStart, idxEnd - idxStart};
3454 }
3455
3465 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
3466 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3467 if (idxEnd > myLen)
3468 idxEnd = myLen;
3469 if (idxStart > idxEnd)
3470 idxStart = idxEnd;
3471 return str_piece{_str() + idxStart, idxEnd - idxStart};
3472 }
3473
3487 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
3488 return str_piece{_str() + from, to - from};
3489 }
3490
3500 constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
3501 return (*this)(0, find_or_all(pattern, offset));
3502 }
3503
3507 constexpr bool operator!() const noexcept {
3508 return _is_empty();
3509 }
3510
3520 constexpr K at(ptrdiff_t idx) const {
3521 return _str()[idx >= 0 ? idx : _len() + idx];
3522 }
3523 // Сравнение строк
3524 // String comparison
3525 constexpr int compare(const K* text, size_t len) const {
3526 size_t myLen = _len();
3527 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3528 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3529 }
3538 constexpr int compare(str_piece o) const {
3539 return compare(o.symbols(), o.length());
3540 }
3541
3549 constexpr int strcmp(const K* text) const {
3550 size_t myLen = _len(), idx = 0;
3551 const K* ptr = _str();
3552 for (; idx < myLen; idx++) {
3553 uns_type s1 = (uns_type)text[idx];
3554 if (!s1) {
3555 return 1;
3556 }
3557 uns_type s2 = (uns_type)ptr[idx];
3558 if (s1 < s2) {
3559 return 1;
3560 } else if (s1 > s2) {
3561 return -1;
3562 }
3563 }
3564 return text[idx] == 0 ? 0 : -1;
3565 }
3566
3567 constexpr bool equal(const K* text, size_t len) const noexcept {
3568 return len == _len() && traits::compare(_str(), text, len) == 0;
3569 }
3578 constexpr bool equal(str_piece other) const noexcept {
3579 return equal(other.symbols(), other.length());
3580 }
3581
3589 constexpr bool operator==(const base& other) const noexcept {
3590 return equal(other._str(), other._len());
3591 }
3592
3598 constexpr auto operator<=>(const base& other) const noexcept {
3599 return compare(other._str(), other._len()) <=> 0;
3600 }
3601
3607 template<typename T, size_t N = const_lit_for<K, T>::Count>
3608 constexpr bool operator==(T&& other) const noexcept {
3609 return N - 1 == _len() && traits::compare(_str(), (const K*)other, N - 1) == 0;
3610 }
3611
3617 template<typename T, size_t N = const_lit_for<K, T>::Count>
3618 constexpr auto operator<=>(T&& other) const noexcept {
3619 size_t myLen = _len();
3620 int cmp = traits::compare(_str(), (const K*)other, std::min(myLen, N - 1));
3621 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3622 return res <=> 0;
3623 }
3624
3625 // Сравнение ascii строк без учёта регистра
3626 // Compare ascii strings without taking into account case
3627 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
3628 if (!len)
3629 return _is_empty() ? 0 : 1;
3630 size_t myLen = _len(), checkLen = std::min(myLen, len);
3631 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
3632 while (checkLen--) {
3633 uns_type s1 = *ptr1++, s2 = *ptr2++;
3634 if (s1 == s2)
3635 continue;
3636 s1 = makeAsciiLower(s1);
3637 s2 = makeAsciiLower(s2);
3638 if (s1 > s2)
3639 return 1;
3640 else if (s1 < s2)
3641 return -1;
3642 }
3643 return myLen == len ? 0 : myLen > len ? 1 : -1;
3644 }
3653 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
3654 return compare_ia(text.symbols(), text.length());
3655 }
3656
3665 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
3666 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
3667 }
3668
3676 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
3677 return compare_ia(text.symbols(), text.length()) < 0;
3678 }
3679
3680 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3681 size_t lenText = _len();
3682 // Образец, не вмещающийся в строку и пустой образец не находим
3683 // We don't look for an empty line or a line longer than the text.
3684 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3685 return str::npos;
3686 lenPattern--;
3687 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3688 pattern++;
3689 for (const K* fnd = text + offset;; ++fnd) {
3690 fnd = traits::find(fnd, last - fnd, first);
3691 if (!fnd)
3692 return str::npos;
3693 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3694 return static_cast<size_t>(fnd - text);
3695 }
3696 }
3707 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
3708 return find(pattern.symbols(), pattern.length(), offset);
3709 }
3710
3726 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
3727 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
3728 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3729 return fnd;
3730 }
3731 throw Exc(std::forward<Args>(args)...);
3732 }
3733
3743 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
3744 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3745 return fnd == str::npos ? fnd : fnd + pattern.length();
3746 }
3747
3757 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3758 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3759 return fnd == str::npos ? _len() : fnd;
3760 }
3761
3771 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
3772 auto fnd = find(pattern.symbols(), pattern.length(), offset);
3773 return fnd == str::npos ? _len() : fnd + pattern.length();
3774 }
3775
3776 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
3777 if (lenPattern == 1)
3778 return find_last(pattern[0], offset);
3779 size_t lenText = std::min(_len(), offset);
3780 // Образец, не вмещающийся в строку и пустой образец не находим
3781 // We don't look for an empty line or a line longer than the text.
3782 if (!lenPattern || lenPattern > lenText)
3783 return str::npos;
3784
3785 lenPattern--;
3786 const K *text = _str() + lenPattern, last = pattern[lenPattern];
3787 lenText -= lenPattern;
3788 while(lenText) {
3789 if (text[--lenText] == last) {
3790 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
3791 return lenText;
3792 }
3793 }
3794 }
3795 return str::npos;
3796 }
3807 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
3808 return find_last(pattern.symbols(), pattern.length(), offset);
3809 }
3810
3820 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
3821 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3822 return fnd == str::npos ? fnd : fnd + pattern.length();
3823 }
3824
3834 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3835 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
3836 return fnd == str::npos ? _len() : fnd;
3837 }
3838
3848 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
3849 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
3850 return fnd == str::npos ? _len() : fnd + pattern.length();
3851 }
3852
3862 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
3863 return find(pattern, offset) != str::npos;
3864 }
3865
3875 constexpr size_t find(K s, size_t offset = 0) const noexcept {
3876 size_t len = _len();
3877 if (offset < len) {
3878 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3879 if (fnd)
3880 return static_cast<size_t>(fnd - str);
3881 }
3882 return str::npos;
3883 }
3884
3894 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
3895 size_t len = _len();
3896 if (offset < len) {
3897 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
3898 if (fnd)
3899 return static_cast<size_t>(fnd - str);
3900 }
3901 return len;
3902 }
3903
3904 template<typename Op>
3905 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3906 if (!maxCount)
3907 maxCount--;
3908 while (maxCount-- > 0) {
3909 size_t fnd = find(pattern, patternLen, offset);
3910 if (fnd == str::npos)
3911 break;
3912 op(fnd);
3913 offset = fnd + patternLen;
3914 }
3915 }
3928 template<typename Op>
3929 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3930 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
3931 }
3932
3933 template<typename To = std::vector<size_t>>
3934 constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
3935 To result;
3936 for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
3937 return result;
3938 }
3951 template<typename To = std::vector<size_t>>
3952 constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
3953 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
3954 }
3955 template<typename To = std::vector<size_t>>
3956 constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
3957 return for_all_finded([&](size_t pos) {
3958 to.emplace_back(pos);
3959 }, pattern, len, offset, maxCount);
3960 }
3971 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
3972 size_t len = std::min(_len(), offset);
3973 const K *text = _str();
3974 while (len > 0) {
3975 if (text[--len] == s)
3976 return len;
3977 }
3978 return str::npos;
3979 }
3980
3990 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
3991 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
3992 }
3993
4003 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
4004 const K* text = _str();
4005 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4006 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4007 }
4008
4018 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
4019 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4020 }
4021
4031 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4032 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4033 }
4034
4044 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
4045 const K* text = _str();
4046 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4047 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4048 }
4049
4059 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
4060 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4061 }
4062
4072 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
4073 return my_type{d()(from, len)};
4074 }
4075
4085 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
4086 return my_type{d().mid(from, len)};
4087 }
4088
4116 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4117 constexpr T as_int() const noexcept {
4118 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4119 return err == IntConvertResult::Overflow ? 0 : res;
4120 }
4121
4149 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
4150 constexpr convert_result<T> to_int() const noexcept {
4151 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4152 }
4153
4159 template<bool SkipWS = true, bool AllowPlus = true> requires (sizeof(K) == 1)
4160 std::optional<double> to_double() const noexcept {
4161 size_t len = _len();
4162 const K* ptr = _str();
4163 if constexpr (SkipWS) {
4164 while (len && uns_type(*ptr) <= ' ') {
4165 len--;
4166 ptr++;
4167 }
4168 }
4169 if constexpr (AllowPlus) {
4170 if (len && *ptr == K('+')) {
4171 ptr++;
4172 len--;
4173 }
4174 }
4175 if (!len) {
4176 return {};
4177 }
4178 double d{};
4179 if (std::from_chars((const u8s*)ptr, (const u8s*)ptr + len, d).ec == std::errc{}) {
4180 return d;
4181 }
4182 return {};
4183 }
4184
4190 template<bool SkipWS = true> requires (sizeof(K) == 1)
4191 std::optional<double> to_double_hex() const noexcept {
4192 size_t len = _len();
4193 const K* ptr = _str();
4194 if constexpr (SkipWS) {
4195 while (len && uns_type(*ptr) <= ' ') {
4196 len--;
4197 ptr++;
4198 }
4199 }
4200 if (len) {
4201 double d{};
4202 if (std::from_chars(ptr, ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4203 return d;
4204 }
4205 }
4206 return {};
4207 }
4208
4216 template<ToIntNumber T>
4217 constexpr void as_number(T& t) const {
4218 t = as_int<T>();
4219 }
4220
4221 template<typename T, typename Op>
4222 constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
4223 size_t mylen = _len();
4224 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4225 str_piece me{_str(), mylen};
4226 for (int i = 0;; i++) {
4227 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4228 if (beginOfDelim == str::npos) {
4229 str_piece last{me.symbols() + offset, me.length() - offset};
4230 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4231 beforeFunc(last);
4232 }
4233 if constexpr (requires { results.emplace_back(last); }) {
4234 if (last.is_same(me)) {
4235 // Пробуем положить весь объект.
4236 // Try to put the entire object.
4237 results.emplace_back(d());
4238 } else {
4239 results.emplace_back(last);
4240 }
4241 } else if constexpr (requires { results.push_back(last); }) {
4242 if (last.is_same(me)) {
4243 // Пробуем положить весь объект.
4244 // Try to put the entire object.
4245 results.push_back(d());
4246 } else {
4247 results.push_back(last);
4248 }
4249 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
4250 if (i < std::size(results)) {
4251 if (last.is_same(me)) {
4252 // Пробуем положить весь объект.
4253 // Try to put the entire object.
4254 results[i] = d();
4255 } else
4256 results[i] = last;
4257 }
4258 }
4259 break;
4260 }
4261 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4262 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4263 beforeFunc(piece);
4264 }
4265 if constexpr (requires { results.emplace_back(piece); }) {
4266 results.emplace_back(piece);
4267 } else if constexpr (requires { results.push_back(piece); }) {
4268 results.push_back(piece);
4269 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
4270 if (i < std::size(results)) {
4271 results[i] = piece;
4272 if (i == results.size() - 1) {
4273 break;
4274 }
4275 }
4276 }
4277 offset = beginOfDelim + lendelimiter;
4278 }
4279 if constexpr (!std::is_same_v<T, void>) {
4280 return results;
4281 }
4282 }
4313 template<typename T, typename Op>
4314 constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
4315 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4316 }
4317
4329 template<typename T>
4330 constexpr T split(str_piece delimiter, size_t offset = 0) const {
4331 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4332 }
4333
4334 // Начинается ли эта строка с указанной подстроки
4335 // Does this string start with the specified substring
4336 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
4337 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4338 }
4345 constexpr bool starts_with(str_piece prefix) const noexcept {
4346 return starts_with(prefix.symbols(), prefix.length());
4347 }
4348
4349 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
4350 size_t myLen = _len();
4351 if (myLen < len) {
4352 return false;
4353 }
4354 const K* ptr1 = _str();
4355 while (len--) {
4356 K s1 = *ptr1++, s2 = *prefix++;
4357 if (s1 == s2)
4358 continue;
4359 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4360 return false;
4361 }
4362 return true;
4363 }
4370 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
4371 return starts_with_ia(prefix.symbols(), prefix.length());
4372 }
4373
4374 // Является ли эта строка началом указанной строки
4375 // Is this string the beginning of the specified string
4376 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
4377 size_t myLen = _len();
4378 if (myLen > len)
4379 return false;
4380 return !myLen || 0 == traits::compare(text, _str(), myLen);
4381 }
4388 constexpr bool prefix_in(str_piece text) const noexcept {
4389 return prefix_in(text.symbols(), text.length());
4390 }
4391 // Заканчивается ли строка указанной подстрокой
4392 // Does the string end with the specified substring
4393 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
4394 size_t myLen = _len();
4395 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4396 }
4403 constexpr bool ends_with(str_piece suffix) const noexcept {
4404 return ends_with(suffix.symbols(), suffix.length());
4405 }
4406 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
4407 // Whether the string ends with the specified substring, case insensitive ASCII
4408 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
4409 size_t myLen = _len();
4410 if (myLen < len) {
4411 return false;
4412 }
4413 const K* ptr1 = _str() + myLen - len;
4414 while (len--) {
4415 K s1 = *ptr1++, s2 = *suffix++;
4416 if (s1 == s2)
4417 continue;
4418 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4419 return false;
4420 }
4421 return true;
4422 }
4429 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
4430 return ends_with_ia(suffix.symbols(), suffix.length());
4431 }
4432
4436 constexpr bool is_ascii() const noexcept {
4437 if (_is_empty())
4438 return true;
4439 if (std::is_constant_evaluated()) {
4440 for (size_t idx = 0; idx < _len(); idx++) {
4441 if (uns_type(_str()[idx]) > 127) {
4442 return false;
4443 }
4444 }
4445 return true;
4446 }
4447 const int sl = ascii_mask<K>::WIDTH;
4448 const size_t mask = ascii_mask<K>::VALUE;
4449 size_t len = _len();
4450 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
4451 if constexpr (sl > 1) {
4452 const size_t roundMask = sizeof(size_t) - 1;
4453 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4454 if (*ptr++ > 127)
4455 return false;
4456 len--;
4457 }
4458 while (len >= sl) {
4459 if (*reinterpret_cast<const size_t*>(ptr) & mask)
4460 return false;
4461 ptr += sl;
4462 len -= sl;
4463 }
4464 }
4465 while (len--) {
4466 if (*ptr++ > 127)
4467 return false;
4468 }
4469 return true;
4470 }
4471
4479 template<typename R = my_type>
4481 return R::upperred_only_ascii_from(d());
4482 }
4483
4491 template<typename R = my_type>
4493 return R::lowered_only_ascii_from(d());
4494 }
4495
4511 template<typename R = my_type>
4512 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
4513 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4514 }
4515
4516 template<StrType<K> From>
4517 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
4518 str_piece sfrom = from, newPos = opTrim(sfrom);
4519 if (newPos.is_same(sfrom)) {
4520 my_type res = from;
4521 return res;
4522 }
4523 return my_type{newPos};
4524 }
4525 template<TrimSides S, StrType<K> From>
4526 constexpr static my_type trim_static(const From& from) {
4527 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
4528 }
4529
4530 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
4531 requires is_const_pattern<N>
4532 constexpr static my_type trim_static(const From& from, T&& pattern) {
4533 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
4534 }
4535
4536 template<TrimSides S, bool withSpaces, StrType<K> From>
4537 constexpr static my_type trim_static(const From& from, str_piece pattern) {
4538 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
4539 }
4548 template<typename R = str_piece>
4549 constexpr R trimmed() const {
4550 return R::template trim_static<TrimSides::TrimAll>(d());
4551 }
4552
4560 template<typename R = str_piece>
4561 R trimmed_left() const {
4562 return R::template trim_static<TrimSides::TrimLeft>(d());
4563 }
4564
4572 template<typename R = str_piece>
4573 R trimmed_right() const {
4574 return R::template trim_static<TrimSides::TrimRight>(d());
4575 }
4576
4586 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4587 requires is_const_pattern<N>
4588 R trimmed(T&& pattern) const {
4589 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4590 }
4591
4601 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4602 requires is_const_pattern<N>
4603 R trimmed_left(T&& pattern) const {
4604 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4605 }
4606
4616 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4617 requires is_const_pattern<N>
4618 R trimmed_right(T&& pattern) const {
4619 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4620 }
4621 // Триминг по символам в литерале и пробелам
4622 // Trimming by characters in literal and spaces
4623
4638 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4639 requires is_const_pattern<N>
4640 R trimmed_with_spaces(T&& pattern) const {
4641 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4642 }
4643
4657 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4658 requires is_const_pattern<N>
4659 R trimmed_left_with_spaces(T&& pattern) const {
4660 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4661 }
4662
4676 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
4677 requires is_const_pattern<N>
4678 R trimmed_right_with_spaces(T&& pattern) const {
4679 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4680 }
4681 // Триминг по динамическому источнику
4682 // Trimming by dynamic source
4683
4694 template<typename R = str_piece>
4695 R trimmed(str_piece pattern) const {
4696 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
4697 }
4698
4708 template<typename R = str_piece>
4709 R trimmed_left(str_piece pattern) const {
4710 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
4711 }
4712
4722 template<typename R = str_piece>
4723 R trimmed_right(str_piece pattern) const {
4724 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
4725 }
4726
4740 template<typename R = str_piece>
4741 R trimmed_with_spaces(str_piece pattern) const {
4742 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
4743 }
4744
4758 template<typename R = str_piece>
4759 R trimmed_left_with_spaces(str_piece pattern) const {
4760 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
4761 }
4762
4776 template<typename R = str_piece>
4777 R trimmed_right_with_spaces(str_piece pattern) const {
4778 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
4779 }
4780
4791 constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
4792 return SplitterBase<K, str_piece>{*this, delimiter};
4793 }
4794};
4795
4796template<size_t N> requires (N > 1)
4797struct find_all_container {
4798 static constexpr size_t max_capacity = N;
4799 size_t positions_[N];
4800 size_t added_{};
4801
4802 constexpr void emplace_back(size_t pos) {
4803 positions_[added_++] = pos;
4804 }
4805};
4806
4825template<typename K>
4826struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
4827 using symb_type = K;
4828 using my_type = str_src<K>;
4829
4830 const symb_type* str;
4831 size_t len;
4832
4833 str_src() = default;
4838 template<typename T, size_t N = const_lit_for<K, T>::Count>
4839 constexpr str_src(T&& v) noexcept : str((const K*)v), len(N - 1) {}
4840
4845 constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
4846
4847 template<StrType<K> T>
4848 constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
4849
4854 template<typename A>
4855 constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
4860 constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
4861
4866 constexpr size_t length() const noexcept {
4867 return len;
4868 }
4869
4873 constexpr const symb_type* symbols() const noexcept {
4874 return str;
4875 }
4876
4880 constexpr bool is_empty() const noexcept {
4881 return len == 0;
4882 }
4883
4889 constexpr bool is_same(str_src<K> other) const noexcept {
4890 return str == other.str && len == other.len;
4891 }
4892
4898 constexpr bool is_part_of(str_src<K> other) const noexcept {
4899 return str >= other.str && str + len <= other.str + other.len;
4900 }
4901
4909 constexpr K operator[](size_t idx) const {
4910 return str[idx];
4911 }
4912
4920 constexpr my_type& remove_prefix(size_t delta) {
4921 str += delta;
4922 len -= delta;
4923 return *this;
4924 }
4925
4933 constexpr my_type& remove_suffix(size_t delta) {
4934 len -= delta;
4935 return *this;
4936 }
4937};
4938
4961template<typename K>
4962struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
4963 using symb_type = K;
4964 using my_type = str_src_nt<K>;
4965 using base = str_src<K>;
4966
4967 constexpr static const K empty_string[1] = {0};
4968
4969 str_src_nt() = default;
4986 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
4987 constexpr explicit str_src_nt(T&& p) noexcept {
4988 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
4989 base::str = base::len ? p : empty_string;
4990 }
4991
4995 template<typename T, size_t N = const_lit_for<K, T>::Count>
4996 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
4997
5002 constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
5003
5004 template<StrType<K> T>
5005 constexpr str_src_nt(T&& t) {
5006 base::str = t.symbols();
5007 base::len = t.length();
5008 }
5013 template<typename A>
5014 constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5015
5016 static const my_type empty_str;
5025 constexpr my_type to_nts(size_t from) {
5026 if (from > base::len) {
5027 from = base::len;
5028 }
5029 return {base::str + from, base::len - from};
5030 }
5031};
5032
5033template<typename K>
5034inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5035template<typename K> struct simple_str_selector;
5036
5037inline namespace literals {
5097template<char...Chars>
5098SS_CONSTEVAL auto operator""_fmt() {
5099 return f::skip_0x<Chars...>();
5100}
5101
5102} // namespace literals
5103
5104#ifndef IN_FULL_SIMSTR
5105
5106template<typename K>
5107using simple_str = str_src<K>;
5108
5109template<typename K>
5110struct simple_str_selector {
5111 using type = simple_str<K>;
5112};
5113
5114template<typename K>
5115using simple_str_nt = str_src_nt<K>;
5116
5117template<typename K>
5118using Splitter = SplitterBase<K, str_src<K>>;
5119
5120using ssa = str_src<u8s>;
5121using ssb = str_src<ubs>;
5122using ssw = str_src<wchar_t>;
5123using ssu = str_src<u16s>;
5124using ssuu = str_src<u32s>;
5125using stra = str_src_nt<u8s>;
5126using strb = str_src_nt<ubs>;
5127using strw = str_src_nt<wchar_t>;
5128using stru = str_src_nt<u16s>;
5129using struu = str_src_nt<u32s>;
5130
5131template<typename K>
5132consteval 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) {
5133 if constexpr (std::is_same_v<K, u8s>)
5134 return s8;
5135 if constexpr (std::is_same_v<K, ubs>)
5136 return sb;
5137 if constexpr (std::is_same_v<K, uws>)
5138 return sw;
5139 if constexpr (std::is_same_v<K, u16s>)
5140 return s16;
5141 if constexpr (std::is_same_v<K, u32s>)
5142 return s32;
5143}
5144
5145#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5146
5147inline namespace literals {
5148
5159SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
5160 return str_src_nt<u8s>{ptr, l};
5161}
5162
5172SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
5173 return str_src_nt<ubs>{ptr, l};
5174}
5175
5185SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
5186 return str_src_nt<uws>{ptr, l};
5187}
5188
5198SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
5199 return str_src_nt<u16s>{ptr, l};
5200}
5201
5212SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
5213 return str_src_nt<u32s>{ptr, l};
5214}
5215
5216} // namespace literals
5217
5218#endif
5219
5220template<typename K, bool withSpaces>
5221struct CheckSpaceTrim {
5222 constexpr bool is_trim_spaces(K s) const {
5223 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
5224 }
5225};
5226template<typename K>
5227struct CheckSpaceTrim<K, false> {
5228 constexpr bool is_trim_spaces(K) const {
5229 return false;
5230 }
5231};
5232
5233template<typename K>
5234struct CheckSymbolsTrim {
5235 str_src<K> symbols;
5236 constexpr bool is_trim_symbols(K s) const {
5237 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
5238 }
5239};
5240
5241template<typename K, size_t N>
5242struct CheckConstSymbolsTrim {
5243 const const_lit_to_array<K, N> symbols;
5244
5245 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
5246 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5247
5248 constexpr bool is_trim_symbols(K s) const noexcept {
5249 return symbols.contain(s);
5250 }
5251};
5252
5253template<typename K>
5254struct CheckConstSymbolsTrim<K, 0> {
5255 constexpr bool is_trim_symbols(K) const {
5256 return false;
5257 }
5258};
5259
5260template<typename K, size_t N>
5261struct SymbSelector {
5262 using type = CheckConstSymbolsTrim<K, N>;
5263};
5264
5265template<typename K>
5266struct SymbSelector<K, 0> {
5267 using type = CheckSymbolsTrim<K>;
5268};
5269
5270template<typename K>
5271struct SymbSelector<K, static_cast<size_t>(-1)> {
5272 using type = CheckConstSymbolsTrim<K, 0>;
5273};
5274
5275template<TrimSides S, typename K, size_t N, bool withSpaces>
5276struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5277 constexpr bool isTrim(K s) const {
5278 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5279 }
5280 constexpr str_src<K> operator()(str_src<K> from) const {
5281 if constexpr ((S & TrimSides::TrimLeft) != 0) {
5282 while (from.len) {
5283 if (isTrim(*from.str)) {
5284 from.str++;
5285 from.len--;
5286 } else
5287 break;
5288 }
5289 }
5290 if constexpr ((S & TrimSides::TrimRight) != 0) {
5291 const K* back = from.str + from.len - 1;
5292 while (from.len) {
5293 if (isTrim(*back)) {
5294 back--;
5295 from.len--;
5296 } else
5297 break;
5298 }
5299 }
5300 return from;
5301 }
5302};
5303
5304template<TrimSides S, typename K>
5305using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
5306
5307template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
5308 requires is_const_pattern<N>
5309constexpr inline auto trimOp(T&& pattern) {
5310 return trim_operator<S, K, N - 1, withSpaces>{pattern};
5311}
5312
5313template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
5314constexpr inline auto trimOp(str_src<K> pattern) {
5315 return trim_operator<S, K, 0, withSpaces>{pattern};
5316}
5317
5318static constexpr size_t FIND_CACHE_SIZE = 16;
5319
5320template<typename K, size_t N, size_t L>
5321struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
5322 using symb_type = K;
5323 using my_type = expr_replaces<K, N, L>;
5324 str_src<K> what;
5325 const K(&pattern)[N + 1];
5326 const K(&repl)[L + 1];
5327 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5328 mutable size_t last_;
5329
5330 constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
5331
5332 constexpr size_t length() const {
5333 size_t l = what.length();
5334 if constexpr (N == L) {
5335 return l;
5336 }
5337 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
5338 if (matches_.added_) {
5339 last_ = matches_.positions_[matches_.added_ - 1] + N;
5340 l += int(L - N) * matches_.added_;
5341
5342 if (matches_.added_ == FIND_CACHE_SIZE) {
5343 for (;;) {
5344 size_t next = what.find(pattern, N, last_);
5345 if (next == str::npos) {
5346 break;
5347 }
5348 last_ = next + N;
5349 l += L - N;
5350 }
5351 }
5352 }
5353 if (!l) {
5354 matches_.added_ = -1;
5355 }
5356 return l;
5357 }
5358 constexpr K* place(K* ptr) const noexcept {
5359 if constexpr (N == L) {
5360 const K* from = what.symbols();
5361 for (size_t start = 0; start < what.length();) {
5362 size_t next = what.find(pattern, N, start);
5363 if (next == str::npos) {
5364 next = what.length();
5365 }
5366 size_t delta = next - start;
5367 ch_traits<K>::copy(ptr, from + start, delta);
5368 ptr += delta;
5369 ch_traits<K>::copy(ptr, repl, L);
5370 ptr += L;
5371 start = next + N;
5372 }
5373 return ptr;
5374 }
5375 if (matches_.added_ == 0) {
5376 return what.place(ptr);
5377 } else if (matches_.added_ == -1) {
5378 // after replaces text become empty
5379 return ptr;
5380 }
5381 const K* from = what.symbols();
5382 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5383 ch_traits<K>::copy(ptr, from + start, offset - start);
5384 ptr += offset - start;
5385 ch_traits<K>::copy(ptr, repl, L);
5386 ptr += L;
5387 start = offset + N;
5388 if (start >= last_) {
5389 size_t tail = what.length() - last_;
5390 ch_traits<K>::copy(ptr, from + last_, tail);
5391 ptr += tail;
5392 break;
5393 } else {
5394 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
5395 }
5396 }
5397 return ptr;
5398 }
5399};
5400
5414template<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>
5415 requires(N > 1)
5416constexpr auto e_repl(A&& w, T&& p, X&& r) {
5417 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
5418}
5419
5427template<typename K>
5428struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
5429 using symb_type = K;
5430 using my_type = expr_replaced<K>;
5431 str_src<K> what;
5432 const str_src<K> pattern;
5433 const str_src<K> repl;
5434 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5435 mutable size_t last_;
5446 constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
5447
5448 constexpr size_t length() const {
5449 size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
5450 if (!plen || plen == rlen) {
5451 return l;
5452 }
5453 what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
5454 if (matches_.added_) {
5455 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5456 l += int(rlen - plen) * matches_.added_;
5457
5458 if (matches_.added_ == FIND_CACHE_SIZE) {
5459 for (;;) {
5460 size_t next = what.find(pattern.symbols(), plen, last_);
5461 if (next == str::npos) {
5462 break;
5463 }
5464 last_ = next + plen;
5465 l += rlen - plen;
5466 }
5467 }
5468 }
5469 if (!l) {
5470 matches_.added_ = -1;
5471 }
5472 return l;
5473 }
5474 constexpr K* place(K* ptr) const noexcept {
5475 size_t plen = pattern.length(), rlen = repl.length();
5476 if (plen == rlen) {
5477 const K* from = what.symbols();
5478 for (size_t start = 0; start < what.length();) {
5479 size_t next = what.find(pattern, start);
5480 if (next == str::npos) {
5481 next = what.length();
5482 }
5483 size_t delta = next - start;
5484 ch_traits<K>::copy(ptr, from + start, delta);
5485 ptr += delta;
5486 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5487 ptr += rlen;
5488 start = next + plen;
5489 }
5490 return ptr;
5491 }
5492 if (matches_.added_ == 0) {
5493 return what.place(ptr);
5494 } else if (matches_.added_ == -1) {
5495 // after replaces text become empty
5496 return ptr;
5497 }
5498 const K* from = what.symbols();
5499 for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
5500 ch_traits<K>::copy(ptr, from + start, offset - start);
5501 ptr += offset - start;
5502 ch_traits<K>::copy(ptr, repl.symbols(), rlen);
5503 ptr += rlen;
5504 start = offset + plen;
5505 if (start >= last_) {
5506 size_t tail = what.length() - start;
5507 ch_traits<K>::copy(ptr, from + start, tail);
5508 ptr += tail;
5509 break;
5510 } else {
5511 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
5512 }
5513 }
5514 return ptr;
5515 }
5516};
5517
5533template<typename K, StrExprForType<K> E>
5534struct expr_replaced_e : expr_to_std_string<expr_replaced_e<K, E>> {
5535 using symb_type = K;
5536 using my_type = expr_replaced<K>;
5537 str_src<K> what;
5538 const str_src<K> pattern;
5539 mutable size_t replLen;
5540 mutable find_all_container<FIND_CACHE_SIZE> matches_;
5541 mutable size_t last_;
5542 const E& expr;
5553 constexpr expr_replaced_e(str_src<K> w, str_src<K> p, const E& e) : what(w), pattern(p), expr(e) {}
5554
5555 constexpr size_t length() const {
5556 size_t l = what.length(), plen = pattern.length();
5557 if (!plen) {
5558 return l;
5559 }
5560 matches_.positions_[0] = what.find(pattern);
5561 if (matches_.positions_[0] == -1) {
5562 // Не нашли вхождений, нечего менять
5563 return l;
5564 }
5565 matches_.added_ = 1;
5566 // Вхождение есть, надо теперь получить длину замены
5567 replLen = expr.length();
5568 if (replLen == plen) {
5569 // Замена той же длины, общая длина не изменится
5570 return l;
5571 }
5572 what.find_all_to(matches_, pattern.symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
5573
5574 last_ = matches_.positions_[matches_.added_ - 1] + plen;
5575 l += int(replLen - plen) * matches_.added_;
5576
5577 if (matches_.added_ == FIND_CACHE_SIZE) {
5578 for (;;) {
5579 size_t next = what.find(pattern.symbols(), plen, last_);
5580 if (next == str::npos) {
5581 break;
5582 }
5583 last_ = next + plen;
5584 l += replLen - plen;
5585 }
5586 }
5587 if (!l) {
5588 matches_.added_ = -1;
5589 }
5590 return l;
5591 }
5592 constexpr K* place(K* ptr) const noexcept {
5593 if (matches_.added_ == 0) {
5594 // не было найдено вхождений
5595 return what.place(ptr);
5596 } else if (matches_.added_ == -1) {
5597 // Строка стала пустой
5598 return ptr;
5599 }
5600 size_t plen = pattern.length();
5601 const K* from = what.symbols();
5602 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
5603 ptr += matches_.positions_[0];
5604 const K* repl = ptr;
5605 expr.place((typename E::symb_type*)ptr);
5606 ptr += replLen;
5607 size_t start = matches_.positions_[0] + plen;
5608
5609 if (plen == replLen) {
5610 for (;;) {
5611 size_t next = what.find(pattern, start);
5612 if (next == str::npos) {
5613 break;
5614 }
5615 size_t delta = next - start;
5616 ch_traits<K>::copy(ptr, from + start, delta);
5617 ptr += delta;
5618 ch_traits<K>::copy(ptr, repl, replLen);
5619 ptr += replLen;
5620 start = next + plen;
5621 }
5622 } else {
5623 for (size_t idx = 1;;) {
5624 if (start >= last_) {
5625 break;
5626 }
5627 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
5628 size_t delta = next - start;
5629 ch_traits<K>::copy(ptr, from + start, delta);
5630 ptr += delta;
5631 ch_traits<K>::copy(ptr, repl, replLen);
5632 ptr += replLen;
5633 start = next + plen;
5634 }
5635 }
5636 size_t tail = what.length() - start;
5637 ch_traits<K>::copy(ptr, from + start, tail);
5638 return ptr + tail;
5639 }
5640};
5641
5655template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, typename X>
5656 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>))
5657constexpr auto e_repl(A&& w, T&& p, X&& r) {
5658 str_src<K> pattern{std::forward<T>(p)};
5659 str_src<K> repl{std::forward<X>(r)};
5660 return expr_replaced<K>{std::forward<A>(w), pattern, repl};
5661}
5662
5676template<StrSource A, typename K = symb_type_from_src_t<A>, typename T, StrExprForType<K> E>
5677 requires std::is_constructible_v<str_src<K>, T>
5678constexpr auto e_repl(A&& w, T&& p, const E& expr) {
5679 str_src<K> pattern{std::forward<T>(p)};
5680 return expr_replaced_e<K, E>{std::forward<A>(w), pattern, expr};
5681}
5682
5683template<bool UseVectorForReplace>
5684struct replace_search_result_store {
5685 size_t count_{};
5686 std::pair<size_t, size_t> replaces_[16];
5687};
5688
5689template<>
5690struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
5691
5692// Строковое выражение для замены символов
5693// String expression to replace characters
5694template<typename K, size_t N, bool UseVectorForReplace>
5695struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
5696 using symb_type = K;
5697 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5698 const K pattern_[N];
5699 const str_src<K> source_;
5700 const str_src<K> replaces_[N];
5701
5702 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5703
5704 [[_no_unique_address]]
5705 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
5706
5707 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
5708 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
5709
5710 size_t length() const {
5711 size_t l = source_.length();
5712 auto [fnd, num] = find_first_of(source_.str, source_.len);
5713 if (fnd == str::npos) {
5714 return l;
5715 }
5716 l += replaces_[num].len - 1;
5717 if constexpr (UseVectorForReplace) {
5718 search_results_.reserve((l >> 4) + 8);
5719 search_results_.emplace_back(fnd, num);
5720 for (size_t start = fnd + 1;;) {
5721 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5722 if (fnd == str::npos) {
5723 break;
5724 }
5725 search_results_.emplace_back(fnd, idx);
5726 start = fnd + 1;
5727 l += replaces_[idx].len - 1;
5728 }
5729 } else {
5730 const size_t max_store = std::size(search_results_.replaces_);
5731 search_results_.replaces_[0] = {fnd, num};
5732 search_results_.count_++;
5733 for (size_t start = fnd + 1;;) {
5734 auto [found, idx] = find_first_of(source_.str, source_.len, start);
5735 if (found == str::npos) {
5736 break;
5737 }
5738 if (search_results_.count_ < max_store) {
5739 search_results_.replaces_[search_results_.count_] = {found, idx};
5740 }
5741 l += replaces_[idx].len - 1;
5742 search_results_.count_++;
5743 start = found + 1;
5744 }
5745 }
5746 return l;
5747 }
5748 K* place(K* ptr) const noexcept {
5749 size_t start = 0;
5750 const K* text = source_.str;
5751 if constexpr (UseVectorForReplace) {
5752 for (const auto& [pos, num] : search_results_) {
5753 size_t delta = pos - start;
5754 ch_traits<K>::copy(ptr, text + start, delta);
5755 ptr += delta;
5756 ptr = replaces_[num].place(ptr);
5757 start = pos + 1;
5758 }
5759 } else {
5760 const size_t max_store = std::size(search_results_.replaces_);
5761 size_t founded = search_results_.count_;
5762 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
5763 const auto [pos, num] = search_results_.replaces_[idx];
5764 size_t delta = pos - start;
5765 ch_traits<K>::copy(ptr, text + start, delta);
5766 ptr += delta;
5767 ptr = replaces_[num].place(ptr);
5768 start = pos + 1;
5769 }
5770 if (founded > max_store) {
5771 founded -= max_store;
5772 while (founded--) {
5773 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5774 size_t delta = fnd - start;
5775 ch_traits<K>::copy(ptr, text + start, delta);
5776 ptr += delta;
5777 ptr = replaces_[idx].place(ptr);
5778 start = fnd + 1;
5779 }
5780 }
5781 }
5782 size_t tail = source_.len - start;
5783 ch_traits<K>::copy(ptr, text + start, tail);
5784 return ptr + tail;
5785 }
5786
5787protected:
5788 template<typename ... Repl>
5789 constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
5790 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
5791
5792 template<typename ... Repl> requires (sizeof...(Repl) == N)
5793 constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
5794 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
5795 {
5796 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5797 for (size_t idx = 0; idx < N; idx++) {
5798 uu8s s = static_cast<uu8s>(pattern_[idx]);
5799 if constexpr (sizeof(K) == 1) {
5800 bit_mask_[s >> 3] |= 1 << (s & 7);
5801 } else {
5802 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
5803 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
5804 } else {
5805 bit_mask_[s >> 3] |= 1 << (s & 7);
5806 }
5807 }
5808 }
5809 }
5810 }
5811
5812 template<size_t Idx>
5813 size_t index_of(K s) const {
5814 if constexpr (Idx < N) {
5815 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
5816 }
5817 return -1;
5818 }
5819 bool is_in_mask(uu8s s) const {
5820 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
5821 }
5822 bool is_in_mask2(uu8s s) const {
5823 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
5824 }
5825 bool is_in_pattern(K s, size_t& idx) const {
5826 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5827 if constexpr (sizeof(K) == 1) {
5828 if (is_in_mask(s)) {
5829 idx = index_of<0>(s);
5830 return true;
5831 }
5832 } else {
5833 if (std::make_unsigned_t<const K>(s) > 255) {
5834 if (is_in_mask2(s)) {
5835 return (idx = index_of<0>(s)) != -1;
5836 }
5837 } else {
5838 if (is_in_mask(s)) {
5839 idx = index_of<0>(s);
5840 return true;
5841 }
5842 }
5843 }
5844 }
5845 return false;
5846 }
5847 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
5848 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5849 size_t idx;
5850 while (offset < len) {
5851 if (is_in_pattern(text[offset], idx)) {
5852 return {offset, idx};
5853 }
5854 offset++;
5855 }
5856 } else {
5857 while (offset < len) {
5858 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
5859 return {offset, idx};
5860 }
5861 offset++;
5862 }
5863 }
5864 return {-1, -1};
5865 }
5866};
5867
5907template<bool UseVector = false, StrSource A, typename K = symb_type_from_src_t<A>, typename ... Repl>
5908 requires (sizeof...(Repl) % 2 == 0)
5909auto e_repl_const_symbols(A&& src, Repl&& ... other) {
5910 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
5911}
5912
5952template<typename K, bool UseVectorForReplace = false>
5953struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
5954 using symb_type = K;
5955 using str_t = typename simple_str_selector<K>::type;
5956 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5957
5958 const str_src<K> source_;
5959 const std::vector<std::pair<K, str_t>>& replaces_;
5960
5961 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
5962
5963 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5964
5965 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
5993 constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
5994 : source_(source), replaces_(repl)
5995 {
5996 size_t pattern_len = replaces_.size();
5997 pattern_.resize(pattern_len);
5998 K* pattern = pattern_.data();
5999
6000 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6001 *pattern++ = replaces_[idx].first;
6002 }
6003
6004 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6005 for (size_t idx = 0; idx < pattern_len; idx++) {
6006 uu8s s = static_cast<uu8s>(pattern_[idx]);
6007 if constexpr (sizeof(K) == 1) {
6008 bit_mask_[s >> 3] |= (1 << (s & 7));
6009 } else {
6010 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6011 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6012 } else {
6013 bit_mask_[s >> 3] |= (1 << (s & 7));
6014 }
6015 }
6016 }
6017 }
6018 }
6019
6020 size_t length() const {
6021 size_t l = source_.length();
6022 auto [fnd, num] = find_first_of(source_.str, source_.len);
6023 if (fnd == str::npos) {
6024 return l;
6025 }
6026 l += replaces_[num].second.len - 1;
6027 if constexpr (UseVectorForReplace) {
6028 search_results_.reserve((l >> 4) + 8);
6029 search_results_.emplace_back(fnd, num);
6030 for (size_t start = fnd + 1;;) {
6031 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6032 if (fnd == str::npos) {
6033 break;
6034 }
6035 search_results_.emplace_back(fnd, idx);
6036 start = fnd + 1;
6037 l += replaces_[idx].second.len - 1;
6038 }
6039 } else {
6040 const size_t max_store = std::size(search_results_.replaces_);
6041 search_results_.replaces_[0] = {fnd, num};
6042 search_results_.count_++;
6043 for (size_t start = fnd + 1;;) {
6044 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6045 if (found == str::npos) {
6046 break;
6047 }
6048 if (search_results_.count_ < max_store) {
6049 search_results_.replaces_[search_results_.count_] = {found, idx};
6050 }
6051 l += replaces_[idx].second.len - 1;
6052 search_results_.count_++;
6053 start = found + 1;
6054 }
6055 }
6056 return l;
6057 }
6058 K* place(K* ptr) const noexcept {
6059 size_t start = 0;
6060 const K* text = source_.str;
6061 if constexpr (UseVectorForReplace) {
6062 for (const auto& [pos, num] : search_results_) {
6063 size_t delta = pos - start;
6064 ch_traits<K>::copy(ptr, text + start, delta);
6065 ptr += delta;
6066 ptr = replaces_[num].second.place(ptr);
6067 start = pos + 1;
6068 }
6069 } else {
6070 const size_t max_store = std::size(search_results_.replaces_);
6071 size_t founded = search_results_.count_;
6072 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6073 const auto [pos, num] = search_results_.replaces_[idx];
6074 size_t delta = pos - start;
6075 ch_traits<K>::copy(ptr, text + start, delta);
6076 ptr += delta;
6077 ptr = replaces_[num].second.place(ptr);
6078 start = pos + 1;
6079 }
6080 if (founded > max_store) {
6081 founded -= max_store;
6082 while (founded--) {
6083 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6084 size_t delta = fnd - start;
6085 ch_traits<K>::copy(ptr, text + start, delta);
6086 ptr += delta;
6087 ptr = replaces_[idx].second.place(ptr);
6088 start = fnd + 1;
6089 }
6090 }
6091 }
6092 size_t tail = source_.len - start;
6093 ch_traits<K>::copy(ptr, text + start, tail);
6094 return ptr + tail;
6095 }
6096
6097protected:
6098 size_t index_of(K s) const {
6099 return pattern_.find(s);
6100 }
6101
6102 bool is_in_mask(uu8s s) const {
6103 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6104 }
6105 bool is_in_mask2(uu8s s) const {
6106 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6107 }
6108
6109 bool is_in_pattern(K s, size_t& idx) const {
6110 if constexpr (sizeof(K) == 1) {
6111 if (is_in_mask(s)) {
6112 idx = index_of(s);
6113 return true;
6114 }
6115 } else {
6116 if (std::make_unsigned_t<const K>(s) > 255) {
6117 if (is_in_mask2(s)) {
6118 return (idx = index_of(s)) != -1;
6119 }
6120 } else {
6121 if (is_in_mask(s)) {
6122 idx = index_of(s);
6123 return true;
6124 }
6125 }
6126 }
6127 return false;
6128 }
6129
6130 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6131 size_t pl = pattern_.length();
6132 if (pl >= BIT_SEARCH_TRESHHOLD) {
6133 size_t idx;
6134 while (offset < len) {
6135 if (is_in_pattern(text[offset], idx)) {
6136 return {offset, idx};
6137 }
6138 offset++;
6139 }
6140 } else {
6141 while (offset < len) {
6142 if (size_t idx = index_of(text[offset]); idx != -1) {
6143 return {offset, idx};
6144 }
6145 offset++;
6146 }
6147 }
6148 return {-1, -1};
6149 }
6150};
6151
6152template<typename K, StrExprForType<K> T>
6153struct force_copy {
6154 const T& t_;
6155 force_copy(const T& t) : t_(t){}
6156};
6157
6158template<typename K, typename T>
6159struct symb_type_from_src<force_copy<K, T>> {
6160 using type = K;
6161};
6162
6163
6164template<StrExpr T>
6165force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6166
6167template<typename K, typename T>
6168constexpr auto to_subst(T&& t) {
6169 return to_strexpr<K>(std::forward<T>(t));
6170}
6171
6172template<typename K, typename T, size_t N>
6173constexpr auto to_subst(const T(&t)[N]) {
6174 return expr_literal<T, N - 1>{t};
6175}
6176
6177template<typename K, StrExprForType<K> T>
6178constexpr decltype(auto) to_subst(T&& t) {
6179 return std::forward<T>(t);
6180}
6181
6182template<typename K, typename T>
6183constexpr T to_subst(const force_copy<K, T>& t) {
6184 return t.t_;
6185}
6186
6187template<typename K, typename T>
6188constexpr T to_subst(force_copy<K, T>&& t) {
6189 return t.t_;
6190}
6191
6192template<typename K, typename T>
6193constexpr T to_subst(force_copy<K, T>& t) {
6194 return t.t_;
6195}
6196
6197template<typename K, typename T>
6198using to_str_exp_t = decltype(to_subst<K>(std::declval<T>()));
6199
6207template<typename K, typename G, typename Arg, typename...Args>
6208struct e_concat : expr_to_std_string<e_concat<K, G, Arg, Args...>> {
6209 using symb_type = K;
6210 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6211 using arg_t = to_str_exp_t<K, Arg>;
6212 to_str_exp_t<K, G> glue_;
6213 arg_t arg_;
6214 store_t args_;
6215 mutable size_t glue_len_;
6248 constexpr e_concat(G&& glue, Arg&& arg, Args&&...args)
6249 : glue_(to_subst<K>(std::forward<G>(glue)))
6250 , arg_(to_subst<K>(std::forward<Arg>(arg)))
6251 , args_(to_subst<K>(std::forward<Args>(args))...) {}
6252
6253 constexpr size_t length() const noexcept {
6254 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6255 glue_len_ = glue_.length();
6256 size_t l = arg_.length() + glue_len_ * sizeof...(Args);
6257 ((l += std::get<Indexes>(args_).length()),...);
6258 return l;
6259 }(std::make_index_sequence<sizeof...(Args)>());
6260 }
6261 constexpr K* place(K* ptr) const noexcept {
6262 return [this]<size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6263 ptr = (K*)arg_.place((typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6264 const K* glueStart = ptr;
6265 ptr = glue_.place(ptr);
6266 (
6267 (
6268 ptr = (K*)std::get<Indexes>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6269 glue_len_ > 0 && Indexes < sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) : nullptr
6270 ),
6271 ...);
6272 return ptr;
6273 }(ptr, std::make_index_sequence<sizeof...(Args)>());
6274 }
6275};
6276// CTAD deducing rule for e_concat
6277template<typename T, typename ... Args> requires (sizeof...(Args) > 1)
6278e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6279
6280struct parse_subst_string_error {
6281 parse_subst_string_error(const char*){}
6282};
6283
6284namespace details {
6285
6286template<typename K, size_t NParams>
6287constexpr size_t parse_pattern_string(str_src<K> pattern, auto&& add_part, auto&& add_param) {
6288 char used_args[NParams] = {0};
6289 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
6290 size_t all_len = 0;
6291
6292 auto find = [](const K* from, const K* last, K s) {
6293 while (from != last) {
6294 if (*from == s) {
6295 break;
6296 }
6297 from++;
6298 }
6299 return from;
6300 };
6301 size_t idx_in_params = 0;
6302
6303 while (first != last) {
6304 const K* open_pos = first;
6305 if (*first != '{') {
6306 open_pos = find(first, last, '{');
6307
6308 for (;;) {
6309 const K* close_pos = find(first, open_pos, '}');
6310 if (close_pos == open_pos) {
6311 unsigned len = open_pos - first;
6312 add_part(first - start, len);
6313 all_len += len;
6314 break;
6315 }
6316 ++close_pos;
6317 if (close_pos == open_pos || *close_pos != '}') {
6318 throw parse_subst_string_error{"unescaped }"};
6319 }
6320 unsigned len = close_pos - first;
6321 add_part(first - start, len);
6322 all_len += len;
6323 first = ++close_pos;
6324 }
6325 if (open_pos == last) {
6326 break;
6327 }
6328 }
6329 if (++open_pos == last) {
6330 throw parse_subst_string_error{"unescaped {"};
6331 }
6332 if (*open_pos == '}') {
6333 if (idx_in_params == -1) {
6334 throw parse_subst_string_error{"already used param ids"};
6335 }
6336 if (idx_in_params == NParams) {
6337 throw parse_subst_string_error{"too many params"};
6338 }
6339 used_args[idx_in_params] = 1;
6340 add_param(idx_in_params++);
6341 first = open_pos + 1;
6342 } else if (*open_pos == '{') {
6343 add_part(open_pos - start, 1);
6344 all_len++;
6345 first = open_pos + 1;
6346 } else {
6347 if (idx_in_params != 0 && idx_in_params != -1) {
6348 throw parse_subst_string_error{"already used non id params"};
6349 }
6350 idx_in_params = -1;
6351 const K* end = find(open_pos, last, '}');
6352 if (end == last) {
6353 throw parse_subst_string_error{"not found }"};
6354 }
6355 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
6356 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
6357 throw parse_subst_string_error{"bad param id"};
6358 }
6359 used_args[--p] = 1;
6360 add_param(p);
6361 first = end + 1;
6362 }
6363 }
6364 for (auto c : used_args) {
6365 if (!c) {
6366 throw parse_subst_string_error{"unused param"};
6367 }
6368 }
6369 return all_len;
6370}
6371
6372struct portion {
6373 unsigned start: 16;
6374 unsigned len: 15;
6375 unsigned is_param: 1;
6376
6377 portion() = default;
6378
6379 constexpr void set_param(unsigned param) {
6380 if (param >= (1 << 16)) {
6381 throw parse_subst_string_error{"the parameter id is too large"};
6382 }
6383 start = param;
6384 is_param = 1;
6385 }
6386 constexpr void set_part(unsigned from, unsigned l) {
6387 if (from >= (1 << 16) || len >= (1 << 15)) {
6388 throw parse_subst_string_error{"the string part is too large"};
6389 }
6390 start = from;
6391 len = l;
6392 is_param = 0;
6393 }
6394};
6395
6396template<typename K, size_t PtLen, size_t NParams>
6397struct subst_params {
6398 const K(&source_)[PtLen];
6399 unsigned all_len_{};
6400 unsigned actual_{};
6401 // The pattern string can be divided into a maximum of this number of portions.
6402 // "a{}a{}a{}a" - One portion of one symbol from the edge, and two portions for every three symbols
6403 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
6404
6405 consteval subst_params(const K(&pattern)[PtLen]) : source_(pattern) {
6406 all_len_ = parse_pattern_string<K, NParams>(pattern,
6407 [this](unsigned from, unsigned len) {
6408 portions_[actual_++].set_part(from, len);
6409 }, [this](unsigned param) {
6410 portions_[actual_++].set_param(param);
6411 });
6412 }
6413};
6414
6415} // namespace details
6416
6422template<typename K, size_t PtLen, typename ... Args>
6423struct e_subst : expr_to_std_string<e_subst<K, PtLen, Args...>> {
6424 inline static constexpr size_t NParams = sizeof...(Args);
6425 using symb_type = K;
6426 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6427
6428 const details::subst_params<K, PtLen, NParams>& subst_;
6429 store_t args_;
6470 constexpr e_subst(const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
6471 : subst_(subst)
6472 , args_(to_subst<K>(std::forward<Args>(args))...){}
6473
6474 constexpr size_t length() const noexcept {
6475 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6476 size_t idx = 0;
6477 size_t expr_length_[NParams] = {};
6478 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6479 size_t l = subst_.all_len_;
6480 for (idx = 0; idx < subst_.actual_; idx++) {
6481 if (subst_.portions_[idx].is_param) {
6482 l += expr_length_[subst_.portions_[idx].start];
6483 }
6484 }
6485 return l;
6486 }(std::make_index_sequence<sizeof...(Args)>());
6487 }
6488 template<size_t Idx>
6489 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6490 if (idx == Idx) {
6491 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6492 }
6493 if constexpr (Idx < NParams - 1) {
6494 return place_idx<Idx + 1>(ptr, idx);
6495 }
6496 return ptr;
6497 }
6498 constexpr K* place(K* ptr) const noexcept {
6499 for (size_t idx = 0; idx < subst_.actual_; idx++) {
6500 if (subst_.portions_[idx].is_param) {
6501 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
6502 } else {
6503 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
6504 ptr += subst_.portions_[idx].len;
6505 }
6506 }
6507 return ptr;
6508 }
6509};
6510
6511// CTAD deducing rule for e_subst
6512template<typename K, size_t N, typename...Args> requires (sizeof...(Args) > 0)
6513e_subst(const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
6514
6520template<typename K, typename ... Args>
6521struct e_vsubst : expr_to_std_string<e_vsubst<K, Args...>> {
6522 inline static constexpr size_t Nparams = sizeof...(Args);
6523 using symb_type = K;
6524 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6525
6526 details::portion portions_[Nparams * 3];
6527 std::vector<details::portion> more_portions_;
6528 store_t args_;
6529 str_src<K> pattern_;
6530 size_t all_len_{};
6531 unsigned actual_{};
6569 constexpr e_vsubst(str_src<K> pattern, Args&&...args)
6570 : pattern_(pattern)
6571 , args_(to_subst<K>(std::forward<Args>(args))...) {
6572
6573 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [this](unsigned from, unsigned len) {
6574 if (actual_ < std::size(portions_)) {
6575 portions_[actual_++].set_part(from, len);
6576 } else {
6577 if (actual_ == std::size(portions_)) {
6578 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6579 }
6580 more_portions_.emplace_back().set_part(from, len);
6581 }
6582 }, [this](unsigned param) {
6583 if (actual_ < std::size(portions_)) {
6584 portions_[actual_++].set_param(param);
6585 } else {
6586 if (actual_ == std::size(portions_)) {
6587 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
6588 }
6589 more_portions_.emplace_back().set_param(param);
6590 }
6591 }
6592 );
6593 }
6594 constexpr size_t length() const noexcept {
6595 return [this]<size_t...Indexes>(std::index_sequence<Indexes...>) {
6596 size_t idx = 0;
6597 size_t expr_length_[Nparams] = {};
6598 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
6599 size_t l = all_len_;
6600 for (idx = 0; idx < actual_; idx++) {
6601 if (portions_[idx].is_param) {
6602 l += expr_length_[portions_[idx].start];
6603 }
6604 }
6605 for (const auto& p : more_portions_) {
6606 if (p.is_param) {
6607 l += expr_length_[p.start];
6608 }
6609 }
6610 return l;
6611 }(std::make_index_sequence<sizeof...(Args)>());
6612 }
6613 template<size_t Idx>
6614 constexpr K* place_idx(K* ptr, size_t idx) const noexcept {
6615 if (idx == Idx) {
6616 return (K*)std::get<Idx>(args_).place((typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
6617 }
6618 if constexpr (Idx < Nparams - 1) {
6619 return place_idx<Idx + 1>(ptr, idx);
6620 }
6621 return ptr;
6622 }
6623 constexpr K* place(K* ptr) const noexcept {
6624 for (size_t idx = 0; idx < actual_; idx++) {
6625 if (portions_[idx].is_param) {
6626 ptr = place_idx<0>(ptr, portions_[idx].start);
6627 } else {
6628 ch_traits<K>::copy(ptr, pattern_.symbols() + portions_[idx].start, portions_[idx].len);
6629 ptr += portions_[idx].len;
6630 }
6631 }
6632 for (const auto& p : more_portions_) {
6633 if (p.is_param) {
6634 ptr = place_idx<0>(ptr, p.start);
6635 } else {
6636 const K* from = pattern_.symbols() + p.start;
6637 for (size_t idx = p.len; idx--;) {
6638 *ptr++ = *from++;
6639 }
6640 //ch_traits<K>::copy(ptr, pattern_.symbols() + p.start, p.len);
6641 //ptr += p.len;
6642 }
6643 }
6644 return ptr;
6645 }
6646};
6647// CTAD deducing rule for e_vsubst
6648template<StrSourceNoLiteral T, typename...Args> requires (sizeof...(Args) > 0)
6649e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
6650
6655namespace str {
6656
6689template<typename K, typename A, StrExprForType<K> E>
6690std::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) {
6691 size_t expr_length = expr.length();
6692 if (!expr_length) {
6693 str.erase(from, count);
6694 return str;
6695 }
6696 size_t str_length = str.length();
6697 if (from > str_length) {
6698 from = str_length;
6699 }
6700 if (from + count > str_length) {
6701 count = str_length - from;
6702 }
6703 size_t new_length = str_length - count + expr_length;
6704 size_t tail_length = str_length - count - from;
6705
6706 if (new_length <= str_length) {
6707 K* data = str.data();
6708 expr.place((typename E::symb_type*)data + from);
6709 if (expr_length < count) {
6710 if (tail_length) {
6711 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
6712 }
6713 str.resize(new_length);
6714 }
6715 } else {
6716 auto fill = [&](K* data, size_t) -> size_t {
6717 if (tail_length) {
6718 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
6719 }
6720 expr.place((typename E::symb_type*)data + from);
6721 return new_length;
6722 };
6723 if constexpr (requires{str.resize_and_overwrite(new_length, fill);}) {
6724 str.resize_and_overwrite(new_length, fill);
6725 } else if constexpr (requires{str._Resize_and_overwrite(new_length, fill);}) {
6726 str._Resize_and_overwrite(new_length, fill);
6727 } else {
6728 str.resize(new_length);
6729 fill(str.data(), 0);
6730 }
6731 }
6732 return str;
6733}
6734
6762template<typename K, typename A, StrExprForType<K> E>
6763std::basic_string<K, std::char_traits<K>, A>& append(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
6764 return change(str, str.length(), 0, expr);
6765}
6766
6795template<typename K, typename A, StrExprForType<K> E>
6796std::basic_string<K, std::char_traits<K>, A>& prepend(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
6797 return change(str, 0, 0, expr);
6798}
6799
6830template<typename K, typename A, StrExprForType<K> E>
6831std::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) {
6832 return change(str, from, 0, expr);
6833}
6834
6859template<typename K, typename A, StrExprForType<K> E>
6860std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
6861 if (size_t expr_length = expr.length()) {
6862 if (expr_length <= str.length()) {
6863 K* data = str.data();
6864 expr.place((typename E::symb_type*)data);
6865 str.resize(expr_length);
6866 } else {
6867 auto fill = [&](K* data, size_t) -> size_t {
6868 expr.place((typename E::symb_type*)data);
6869 return expr_length;
6870 };
6871 if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
6872 str.resize_and_overwrite(expr_length, fill);
6873 } else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
6874 str._Resize_and_overwrite(expr_length, fill);
6875 } else {
6876 str.resize(expr_length);
6877 expr.place((typename E::symb_type*)str.data());
6878 }
6879 }
6880 } else {
6881 str.clear();
6882 }
6883 return str;
6884}
6885
6886namespace details {
6887
6888template<typename K, typename A, typename E>
6889struct replace_grow_helper {
6890 using my_type = std::basic_string<K, std::char_traits<K>, A>;
6891
6892 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)
6893 : str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
6894 my_type& str;
6895
6896 const str_src<K> source;
6897 const str_src<K> pattern;
6898 K* repl;
6899 const size_t replLen;
6900
6901 size_t maxCount;
6902 const size_t delta;
6903 size_t all_delta{};
6904 const E& expr;
6905
6906 K* reserve_for_copy{};
6907 size_t end_of_piece{};
6908 size_t total_length{};
6909
6910 std::optional<my_type> dst;
6911
6912 void replace(size_t offset) {
6913 size_t found[16] = {offset};
6914 maxCount--;
6915
6916 offset += pattern.len;
6917 all_delta += delta;
6918 size_t idx = 1;
6919 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
6920 found[idx] = source.find(pattern, offset);
6921 if (found[idx] == npos) {
6922 break;
6923 }
6924 offset = found[idx] + pattern.len;
6925 all_delta += delta;
6926 }
6927 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
6928 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
6929 }
6930 // Теперь делаем свои замены
6931 // Now we make our replacements
6932 if (!reserve_for_copy) {
6933 // Только начинаем
6934 // Just getting started
6935 end_of_piece = source.length();
6936 total_length = end_of_piece + all_delta;
6937 my_type* dst_str{};
6938 if (total_length <= str.capacity()) {
6939 // Строка поместится в старое место | The line will be placed in the old location.
6940 dst_str = &str;
6941 } else {
6942 // Будем создавать в другом буфере | We will create in another buffer.
6943 dst_str = &dst.emplace();
6944 }
6945 auto fill = [this](K* p, size_t) -> size_t {
6946 reserve_for_copy = p;
6947 return total_length;
6948 };
6949 if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
6950 dst_str->_Resize_and_overwrite(total_length, fill);
6951 } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) {
6952 dst_str->resize_and_overwrite(total_length, fill);
6953 } else {
6954 dst_str->resize(total_length);
6955 reserve_for_copy = dst_str->data();
6956 }
6957 }
6958 const K* src_start = str.c_str();
6959 while(idx-- > 0) {
6960 size_t pos = found[idx] + pattern.len;
6961 size_t lenOfPiece = end_of_piece - pos;
6962 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
6963 if constexpr (std::is_same_v<E, int>) {
6964 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
6965 } else {
6966 if (!repl) {
6967 repl = reserve_for_copy + pos + all_delta - replLen;
6968 expr.place(repl);
6969 } else {
6970 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
6971 }
6972 }
6973 all_delta -= delta;
6974 end_of_piece = found[idx];
6975 }
6976 if (!all_delta && reserve_for_copy != src_start) {
6977 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
6978 str = std::move(*dst);
6979 }
6980 }
6981};
6982
6983} // namespace details
6984
7019template<typename K, typename A, StrExprForType<K> E, typename T>
7020requires (std::is_constructible_v<str_src<K>, T>)
7021std::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) {
7022 if (!max_count) {
7023 return str;
7024 }
7025 str_src<K> src = str;
7026 str_src<K> spattern{std::forward<T>(pattern)};
7027 offset = src.find(pattern, offset);
7028 if (offset == npos) {
7029 return str;
7030 }
7031 size_t replLen = repl.length();
7032 K* replStart{};
7033 if (spattern.len == replLen) {
7034 // Заменяем inplace на подстроку такой же длины
7035 // Replace inplace with a substring of the same length
7036 K* ptr = str.data();
7037 replStart = ptr + offset;
7038 repl.place(replStart);
7039
7040 while (--max_count) {
7041 offset = src.find(spattern, offset + replLen);
7042 if (offset == npos)
7043 break;
7044 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7045 }
7046 } else if (spattern.len > replLen) {
7047 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7048 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7049 K* ptr = str.data();
7050 replStart = ptr + offset;
7051 repl.place(replStart);
7052 size_t posWrite = offset + replLen;
7053 offset += spattern.len;
7054
7055 while (--max_count) {
7056 size_t idx = src.find(spattern, offset);
7057 if (idx == npos)
7058 break;
7059 size_t lenOfPiece = idx - offset;
7060 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7061 posWrite += lenOfPiece;
7062 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7063 posWrite += replLen;
7064 offset = idx + spattern.len;
7065 }
7066 size_t tailLen = src.len - offset;
7067 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7068 str.resize(posWrite + tailLen);
7069 } else {
7070 details::replace_grow_helper<K, A, E>(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7071 }
7072 return str;
7073}
7074
7095template<typename K, typename A>
7096std::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) {
7097 if (!max_count) {
7098 return str;
7099 }
7100 str_src<K> src = str;
7101 offset = src.find(pattern, offset);
7102 if (offset == npos) {
7103 return str;
7104 }
7105 if (pattern.len == repl.len) {
7106 // Заменяем inplace на подстроку такой же длины
7107 // Replace inplace with a substring of the same length
7108 K* ptr = str.data();
7109 while (max_count--) {
7110 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7111 offset = src.find(pattern, offset + repl.len);
7112 if (offset == npos)
7113 break;
7114 }
7115 } else if (pattern.len > repl.len) {
7116 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
7117 // Replace with a shorter piece, the length of the text will decrease, go from left to right
7118 K* ptr = str.data();
7119 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7120 size_t posWrite = offset + repl.len;
7121 offset += pattern.len;
7122
7123 while (--max_count) {
7124 size_t idx = src.find(pattern, offset);
7125 if (idx == npos)
7126 break;
7127 size_t lenOfPiece = idx - offset;
7128 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7129 posWrite += lenOfPiece;
7130 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7131 posWrite += repl.len;
7132 offset = idx + pattern.len;
7133 }
7134 size_t tailLen = src.len - offset;
7135 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7136 str.resize(posWrite + tailLen);
7137 } else {
7138 details::replace_grow_helper<K, A, int>(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7139 }
7140 return str;
7141}
7142
7143template<typename K, typename A, typename T, typename M>
7144requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7145std::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) {
7146 return replace(str, str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7147}
7148
7149} // namespace str
7150
7151} // namespace simstr
7152
7153namespace std {
7173template<simstr::StdStrSource T>
7175 return {str};
7176}
7177
7206template<typename K, typename A, simstr::StrExprForType<K> E>
7207std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7208 return simstr::str::change(str, str.length(), 0, expr);
7209}
7210
7239template<typename K, typename A, simstr::StrExprForType<K> E>
7240std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
7241 return simstr::str::change(str, 0, 0, expr);
7242}
7243
7244} // namespace std
Класс для последовательного получения подстрок по заданному разделителю.
Определения strexpr.h:3232
constexpr bool is_done() const
Узнать, не закончились ли подстроки.
Определения strexpr.h:3243
constexpr str_t next()
Получить следующую подстроку.
Определения strexpr.h:3252
std::optional< double > to_double_hex() const noexcept
Преобразовать строку в 16ричной записи в double. Пока работает только для char.
Определения strexpr.h:4191
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Найти последнее вхождения символа в этой строке.
Определения strexpr.h:3971
R trimmed_right() const
Получить строку с удалением пробельных символов справа.
Определения strexpr.h:4573
constexpr int compare_ia(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3653
constexpr int compare(str_piece o) const
Сравнение строк посимвольно.
Определения strexpr.h:3538
constexpr size_t find(K s, size_t offset=0) const noexcept
Найти символ в этой строке.
Определения strexpr.h:3875
constexpr bool ends_with(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой.
Определения strexpr.h:4403
R trimmed(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева и справа.
Определения strexpr.h:4695
R trimmed_left(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева.
Определения strexpr.h:4709
constexpr bool operator==(const base &other) const noexcept
Оператор сравнение строк на равенство.
Определения strexpr.h:3589
constexpr std::basic_string_view< D > to_sv() const noexcept
Конвертировать в std::basic_string_view.
Определения strexpr.h:3372
R trimmed_left() const
Получить строку с удалением пробельных символов слева.
Определения strexpr.h:4561
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти конец первого вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3771
constexpr bool starts_with(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки.
Определения strexpr.h:4345
constexpr bool equal(str_piece other) const noexcept
Сравнение строк на равенство.
Определения strexpr.h:3578
void copy_to(K *buffer, size_t bufSize)
Копировать строку в указанный буфер.
Определения strexpr.h:3350
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Найти начало первого вхождения подстроки в этой строке или выкинуть исключение.
Определения strexpr.h:3727
constexpr str_piece until(str_piece pattern, size_t offset=0) const noexcept
Получить подстроку str_src от начала и до первого найденного вхождения указанной подстроки.
Определения strexpr.h:3500
constexpr T as_int() const noexcept
Преобразовать строку в число заданного типа.
Определения strexpr.h:4117
constexpr bool less_ia(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3676
constexpr T split(str_piece delimiter, size_t offset=0) const
Разделить строку на подстроки по заданному разделителю.
Определения strexpr.h:4330
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Получить часть строки как "кусок строки".
Определения strexpr.h:3465
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Получить подстроку. Работает аналогично operator(), только результат выдает того же типа,...
Определения strexpr.h:4072
R upperred_only_ascii() const
Получить копию строки в верхнем регистре ASCII символов.
Определения strexpr.h:4480
constexpr str_piece to_str() const noexcept
Преобразовать себя в "кусок строки", включающий всю строку.
Определения strexpr.h:3420
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Получить подстроку str_src с позиции от from до позиции to (не включая её).
Определения strexpr.h:3487
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Получить копию строки с заменёнными вхождениями подстрок.
Определения strexpr.h:4512
constexpr int strcmp(const K *text) const
Сравнение с C-строкой посимвольно.
Определения strexpr.h:3549
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра ASCII символов.
Определения strexpr.h:4429
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа не из заданного набора символов.
Определения strexpr.h:4018
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов.
Определения strexpr.h:3990
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3834
R trimmed_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:4741
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра ASCII символов.
Определения strexpr.h:4370
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке.
Определения strexpr.h:3807
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов.
Определения strexpr.h:4031
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Найти символ в этой строке или конец строки.
Определения strexpr.h:3894
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа не из заданного набора символов.
Определения strexpr.h:4059
constexpr void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Вызвать функтор для всех найденных вхождений подстроки в этой строке.
Определения strexpr.h:3929
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Найти конец вхождения подстроки в этой строке.
Определения strexpr.h:3743
constexpr bool operator!() const noexcept
Проверка на пустоту.
Определения strexpr.h:3507
R trimmed(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева и справа.
Определения strexpr.h:4588
constexpr R trimmed() const
Получить строку с удалением пробельных символов слева и справа.
Определения strexpr.h:4549
constexpr size_t size() const
Размер строки в символах.
Определения strexpr.h:3361
R trimmed_right(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, справа.
Определения strexpr.h:4723
constexpr To find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Найти все вхождения подстроки в этой строке.
Определения strexpr.h:3952
constexpr my_type str_mid(size_t from, size_t len=-1) const
Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
Определения strexpr.h:4085
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке.
Определения strexpr.h:3820
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов.
Определения strexpr.h:4003
constexpr bool is_ascii() const noexcept
Содержит ли строка только ASCII символы.
Определения strexpr.h:4436
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке.
Определения strexpr.h:3707
constexpr SplitterBase< K, str_piece > splitter(str_piece delimiter) const
Получить объект Splitter по заданному разделителю, который позволяет последовательно получать подстро...
Определения strexpr.h:4791
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов.
Определения strexpr.h:4044
R trimmed_left(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева.
Определения strexpr.h:4603
R trimmed_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4640
std::optional< double > to_double() const noexcept
Преобразовать строку в double.
Определения strexpr.h:4160
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3757
constexpr bool operator==(T &&other) const noexcept
Оператор сравнения строки и строкового литерала на равенство.
Определения strexpr.h:3608
R trimmed_right_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:4777
constexpr auto operator<=>(const base &other) const noexcept
Оператор сравнения строк.
Определения strexpr.h:3598
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Содержит ли строка указанную подстроку.
Определения strexpr.h:3862
R trimmed_right(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, справа.
Определения strexpr.h:4618
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Получить часть строки как "str_src".
Определения strexpr.h:3446
constexpr K at(ptrdiff_t idx) const
Получить символ на заданной позиции .
Определения strexpr.h:3520
R lowered_only_ascii() const
Получить копию строки в нижнем регистре ASCII символов.
Определения strexpr.h:4492
R trimmed_right_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4678
constexpr auto operator<=>(T &&other) const noexcept
Оператор сравнения строки и строкового литерала.
Определения strexpr.h:3618
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Конвертировать в std::basic_string.
Определения strexpr.h:3392
constexpr T splitf(str_piece delimiter, const Op &beforeFunc, size_t offset=0) const
Разделить строку на части по заданному разделителю, с возможным применением функтора к каждой подстро...
Определения strexpr.h:4314
R trimmed_left_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения strexpr.h:4659
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке или конец строки.
Определения strexpr.h:3848
constexpr bool prefix_in(str_piece text) const noexcept
Является ли эта строка началом другой строки.
Определения strexpr.h:4388
R trimmed_left_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения strexpr.h:4759
constexpr K * place(K *ptr) const noexcept
Копировать строку в указанный буфер.
Определения strexpr.h:3335
constexpr convert_result< T > to_int() const noexcept
Преобразовать строку в число заданного типа.
Определения strexpr.h:4150
constexpr bool equal_ia(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра ASCII символов.
Определения strexpr.h:3665
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения strexpr.h:4217
Концепт строкового выражения, совместимого с заданным типом символов.
Определения strexpr.h:525
Концепт "Строковых выражений".
Определения strexpr.h:507
Базовая концепция строкового объекта.
Определения strexpr.h:311
Проверка, являются ли два типа совместимыми строковыми типами.
Определения strexpr.h:172
constexpr expr_if< A > e_if(bool c, const A &a)
Создание условного строкового выражения expr_if.
Определения strexpr.h:1544
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Унарный оператор+ для преобразования стандартных строк в строковые выражения.
Определения strexpr.h:7174
constexpr expr_spaces< uws, N > e_spcw()
Генерирует строку из N wchar_t пробелов.
Определения strexpr.h:1104
expr_fill< K, A, true > e_fill_left(const A &a, size_t width, K symbol=K(' '))
Создает выражение, которое дополняет указанное строковое выражение до заданной длины заданным символо...
Определения strexpr.h:2697
constexpr empty_expr< uws > eew
Пустое строковое выражение типа wchar_t.
Определения strexpr.h:862
constexpr expr_spaces< u8s, N > e_spca()
Генерирует строку из N char пробелов.
Определения strexpr.h:1086
constexpr empty_expr< u32s > eeuu
Пустое строковое выражение типа char32_t.
Определения strexpr.h:874
constexpr expr_num< K, T > e_num(T t)
Преобразование целого числа в строковое выражение.
Определения strexpr.h:1717
HexFlags
Флаги для функции e_hex.
Определения strexpr.h:2525
constexpr auto e_hex(T v)
Создает объект, который может преобразовываться в строковое выражение, генерирующее 16ричное представ...
Определения strexpr.h:2582
constexpr strexprjoin< A, B > operator+(const A &a, const B &b)
Оператор сложения двух произвольных строковых выражения для одинакового типа символов.
Определения strexpr.h:719
constexpr expr_repeat_lit< K, M - 1 > e_repeat(T &&s, size_t l)
Генерирует строку из l строковых констант s типа K.
Определения strexpr.h:1218
constexpr expr_literal< typename const_lit< T >::symb_type, static_cast< size_t >(N - 1)> e_t(T &&s)
Преобразует строковый литерал в строковое выражение.
Определения strexpr.h:991
expr_fill< K, A, false > e_fill_right(const A &a, size_t width, K symbol=K(' '))
Создает выражение, которое дополняет указанное строковое выражение до заданной длины заданным символо...
Определения strexpr.h:2723
constexpr expr_choice< A, B > e_choice(bool c, const A &a, const B &b)
Создание условного строкового выражения expr_choice.
Определения strexpr.h:1466
constexpr expr_char< K > e_char(K s)
Генерирует строку из 1 заданного символа.
Определения strexpr.h:918
constexpr empty_expr< u8s > eea
Пустое строковое выражение типа char.
Определения strexpr.h:850
constexpr expr_pad< K > e_c(size_t l, K s)
Генерирует строку из l символов s типа K.
Определения strexpr.h:1149
constexpr empty_expr< u16s > eeu
Пустое строковое выражение типа char16_t.
Определения strexpr.h:868
auto e_repl_const_symbols(A &&src, Repl &&... other)
Возвращает строковое выражение, генерирующее строку, в которой заданные символы заменены на заданные ...
Определения strexpr.h:5909
constexpr auto e_int(T v)
Создает объект, который преобразовывается в строковое выражение, генерирующее строковое представление...
Определения strexpr.h:2381
constexpr empty_expr< u8s > eeb
Пустое строковое выражение типа char8_t.
Определения strexpr.h:856
constexpr auto e_repl(A &&w, T &&p, X &&r)
Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
Определения strexpr.h:5416
constexpr auto e_join(const T &s, L &&d)
Получить строковое выражение, конкатенирующее строки в контейнере в одну строку с заданным разделител...
Определения strexpr.h:2801
@ Short
without leading zeroes
Определения strexpr.h:2526
Небольшое пространство для методов работы со стандартными строками.
Определения 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)
Добавить к стандартной строке строковое выражение.
Определения strexpr.h:6763
std::basic_string< K, std::char_traits< K >, A > & prepend(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Вставить строковое выражение в начало стандартной строки.
Определения strexpr.h:6796
std::basic_string< K, std::char_traits< K >, A > & change(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, size_t count, const E &expr)
Изменить часть стандартной строки на заданное строковое выражение.
Определения strexpr.h:6690
std::basic_string< K, std::char_traits< K >, A > & replace(std::basic_string< K, std::char_traits< K >, A > &str, T &&pattern, const E &repl, size_t offset=0, size_t max_count=-1)
Функция поиска подстрок в стандартной строке и замены найденных вхождений на значение строкового выра...
Определения strexpr.h:7021
std::basic_string< K, std::char_traits< K >, A > & overwrite(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Переписать всю строку заданным строковым выражением, при необходимости изменив размер строки.
Определения strexpr.h:6860
std::basic_string< K, std::char_traits< K >, A > & insert(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, const E &expr)
Вставить строковое выражение в указанную позицию стандартной строки.
Определения strexpr.h:6831
Пространство имён для объектов библиотеки
Определения sstring.cpp:12
IntConvertResult
Перечисление с возможными результатами преобразования строки в целое число
Определения strexpr.h:2868
@ Overflow
Переполнение, число не помещается в заданный тип
Определения strexpr.h:2871
@ Success
Успешно
Определения strexpr.h:2869
@ NotNumber
Вообще не число
Определения strexpr.h:2872
@ BadSymbolAtTail
Число закончилось не числовым символом
Определения strexpr.h:2870
constexpr e_concat(G &&glue, Arg &&arg, Args &&...args)
Создание строкового выражения, объединяющего указанные строковые выражения, с использованием заданног...
Определения strexpr.h:6248
constexpr e_subst(const details::subst_params< K, PtLen, NParams > &subst, Args &&...args)
Создает строковое выражение, которое подставляет в заданные места в строковом литерале - образце знач...
Определения strexpr.h:6470
constexpr e_vsubst(str_src< K > pattern, Args &&...args)
Создает строковое выражение, которое подставляет в заданные места в строке-образце,...
Определения strexpr.h:6569
"Пустое" строковое выражение.
Определения strexpr.h:835
Строковое выражение условного выбора.
Определения strexpr.h:1345
Строковое выражение условного выбора.
Определения strexpr.h:1411
Строковое выражение условного выбора.
Определения strexpr.h:1254
Строковое выражение условного выбора.
Определения strexpr.h:1283
Тип строкового выражения, возвращающего N заданных символов.
Определения strexpr.h:1120
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Конструктор выражения.
Определения strexpr.h:5993
Строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки на строковое выр...
Определения strexpr.h:5534
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Конструктор.
Определения strexpr.h:5553
Строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки на другую строку...
Определения strexpr.h:5428
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Конструктор.
Определения strexpr.h:5446
Тип строкового выражения, возвращающего N заданных символов.
Определения strexpr.h:1060
Тип для использования std::basic_string и std::basic_string_view как источников в строковых выражения...
Определения strexpr.h:1569
Базовый класс для преобразования строковых выражений в стандартные строки
Определения strexpr.h:659
Класс, заявляющий, что ссылается на нуль-терминированную строку.
Определения strexpr.h:4962
constexpr my_type to_nts(size_t from)
Получить нуль-терминированную строку, сдвинув начало на заданное количество символов.
Определения strexpr.h:5025
constexpr str_src_nt(T &&v) noexcept
Конструктор из строкового литерала.
Определения strexpr.h:4996
constexpr str_src_nt(T &&p) noexcept
Явный конструктор из С-строки.
Определения strexpr.h:4987
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения strexpr.h:5014
constexpr str_src_nt(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения strexpr.h:5002
Простейший класс иммутабельной не владеющей строки.
Определения strexpr.h:4826
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения strexpr.h:4855
constexpr bool is_empty() const noexcept
Проверить, не пуста ли строка.
Определения strexpr.h:4880
constexpr size_t length() const noexcept
Получить длину строки.
Определения strexpr.h:4866
constexpr K operator[](size_t idx) const
Получить символ из указанной позиции. Проверка границ не выполняется.
Определения strexpr.h:4909
constexpr my_type & remove_prefix(size_t delta)
Сдвигает начало строки на заданное количество символов.
Определения strexpr.h:4920
constexpr str_src(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения strexpr.h:4845
constexpr bool is_same(str_src< K > other) const noexcept
Проверить, не указывают ли два объекта на одну строку.
Определения strexpr.h:4889
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Конструктор из std::basic_string_view.
Определения strexpr.h:4860
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки.
Определения strexpr.h:4873
constexpr my_type & remove_suffix(size_t delta)
Укорачивает строку на заданное количество символов.
Определения strexpr.h:4933
constexpr bool is_part_of(str_src< K > other) const noexcept
Проверить, не является ли строка частью другой строки.
Определения strexpr.h:4898
constexpr str_src(T &&v) noexcept
Конструктор из строкового литерала.
Определения strexpr.h:4839
Конкатенация ссылки на строковое выражение и значения строкового выражения.
Определения strexpr.h:744
Шаблонный класс для конкатенации двух строковых выражений в одно с помощью operator +
Определения strexpr.h:682