21#if defined __has_builtin
22# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
23# define HAS_BUILTIN_OVERFLOW
28#define _no_unique_address msvc::no_unique_address
29#define decl_empty_bases __declspec(empty_bases)
31#define _no_unique_address no_unique_address
32#define decl_empty_bases
44#define SS_CONSTEVAL constexpr
46#define SS_CONSTEVAL consteval
57inline constexpr bool wchar_is_u16 =
sizeof(wchar_t) ==
sizeof(
char16_t);
59using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
61inline wchar_type* to_w(
wchar_t* p) {
62 return (
reinterpret_cast<wchar_type*
>(p));
65inline const wchar_type* to_w(
const wchar_t* p) {
66 return (
reinterpret_cast<const wchar_type*
>(p));
69inline wchar_t* from_w(wchar_type* p) {
70 return (
reinterpret_cast<wchar_t*
>(p));
73inline const wchar_t* from_w(
const wchar_type* p) {
74 return (
reinterpret_cast<const wchar_t*
>(p));
83using uu8s = std::make_unsigned<u8s>::type;
85template<
typename T,
typename K = void,
typename... Types>
86struct is_one_of_type {
87 static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
90struct is_one_of_type<T, void> : std::false_type {};
93concept is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
96concept is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
98template<is_one_of_std_
char_v From>
99auto to_one_of_std_char(From* from) {
100 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
102 }
else if constexpr (std::is_same_v<From, ubs>) {
103 return reinterpret_cast<u8s*
>(from);
109template<is_one_of_std_
char_v From>
110auto to_one_of_std_char(
const From* from) {
111 if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
113 }
else if constexpr (std::is_same_v<From, ubs>) {
114 return reinterpret_cast<const u8s*
>(from);
121struct to_std_char_type : std::type_identity<K>{};
124struct to_std_char_type<char8_t>{
129struct to_std_char_type<char16_t>{
130 using type = std::conditional_t<
sizeof(char16_t) ==
sizeof(
wchar_t), wchar_t,
void>;
134struct to_std_char_type<char32_t>{
135 using type = std::conditional_t<
sizeof(char32_t) ==
sizeof(
wchar_t), wchar_t,
void>;
139using to_std_char_t =
typename to_std_char_type<K>::type;
142struct to_base_char_type : std::type_identity<K>{};
145struct to_base_char_type<char8_t>{
150struct to_base_char_type<wchar_t>{
151 using type = std::conditional_t<
sizeof(char16_t) ==
sizeof(
wchar_t), char16_t,
char32_t>;
155using to_base_char_t =
typename to_base_char_type<K>::type;
172template<
typename K1,
typename K2>
221template<
typename T>
struct const_lit;
224template<is_one_of_
char_v T,
size_t N>
225struct const_lit<const T(&)[N]> {
227 constexpr static size_t Count = N;
231concept is_const_lit_v =
requires {
232 typename const_lit<T>::symb_type;
237template<
typename K,
typename T>
struct const_lit_for;
239template<
typename K, is_equal_str_type_v<K> P,
size_t N>
240struct const_lit_for<K, const P(&)[N]> {
241 constexpr static size_t Count = N;
244template<
typename K,
size_t N>
245class const_lit_to_array {
248 constexpr size_t find(K s)
const {
249 if constexpr (Idx < N) {
250 return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
256 constexpr bool exist(K s)
const {
257 if constexpr (Idx < N) {
258 return s == symbols_[Idx] || exist<Idx + 1>(s);
263 const K (&symbols_)[N + 1];
265 template<typename T, size_t M = const_lit_for<K, T>::Count>
requires (M == N + 1)
266 constexpr const_lit_to_array(T&& s)
267 : symbols_((
const K(&)[M])s) {}
269 constexpr bool contain(K s)
const {
272 constexpr size_t index_of(K s)
const {
278concept StrTypeCommon =
requires(
const A& a) {
279 typename std::remove_cvref_t<A>::symb_type;
280 { a.is_empty() } -> std::same_as<bool>;
281 { a.length() } -> std::convertible_to<size_t>;
286concept HasSymbType =
requires {
287 typename std::remove_cvref_t<A>::symb_type;
311template<
typename A,
typename K>
312concept StrType = StrTypeCommon<A> &&
requires(
const A& a) {
313 { a.symbols() } -> std::same_as<const K*>;
314} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
316template<
typename T>
struct is_std_string_source : std::false_type{};
318template<
typename K,
typename A>
319struct is_std_string_source<
std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
322struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
325concept is_std_string_source_v = is_std_string_source<T>::value;
328concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
330template<
typename T,
typename K>
334concept StrSource = StdStrSource<T> || is_const_lit_v<T> || StrTypeCommon<T>;
337concept StrSourceNoLiteral = StdStrSource<T> || StrTypeCommon<T>;
339template<
typename T>
struct is_std_string : std::false_type{};
341template<
typename K,
typename A>
342struct is_std_string<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
345concept is_std_string_v = is_std_string<T>::value;
346template<
typename T,
typename K>
350struct symb_type_from_src {
354template<
typename K,
size_t N>
355struct symb_type_from_src<const K(&)[N]> {
359template<StdStrSource T>
360struct symb_type_from_src<T> {
361 using type =
typename std::remove_cvref_t<T>::value_type;
364template<HasSymbType T>
365struct symb_type_from_src<T> {
366 using type =
typename std::remove_cvref_t<T>::symb_type;
370using symb_type_from_src_t = symb_type_from_src<T>::type;
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*>;
524template<
typename A,
typename K>
527template<
typename K,
typename T>
528struct convert_to_strexpr;
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>;
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>;
540template<
typename A,
typename K>
543template<
typename A,
typename K>
546template<
typename A,
typename K>
547concept to_strexpr_meth =
requires(
const A& a) {
551template<
typename A,
typename K>
552concept to_strexpr_std =
requires(
const A& a) {
553 {a.template to_strexpr<K>()} -> StdStringForType<K>;
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>;
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));
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)};
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)};
574template<
typename K, to_strexpr_meth<K> T>
575constexpr auto to_strexpr(T&& t) {
576 return t.template to_strexpr<K>();
580struct expr_stdstr_c {
584 expr_stdstr_c(T t) : t_(std::move(t)){}
586 constexpr size_t length() const noexcept {
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);
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))};
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>()};
608template<
typename K,
typename T>
609using convert_to_strexpr_t =
decltype(to_strexpr<K>(std::declval<T>()));
627template<
typename K,
typename Allocator, StrExprForType<K> A>
628constexpr std::basic_string<K, std::char_traits<K>, Allocator> to_std_string(
const A& expr) {
629 std::basic_string<K, std::char_traits<K>, Allocator> res;
630 if (
size_t l = expr.length()) {
631 auto fill = [&](K* ptr,
size_t size) ->
size_t {
632 expr.place((
typename A::symb_type*)ptr);
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); }) {
639 res._Resize_and_overwrite(l, fill);
642 expr.place((
typename A::symb_type*)res.data());
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));
681template<StrExpr A, StrExprForType<
typename A::symb_type> B>
683 using symb_type =
typename A::symb_type;
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();
690 constexpr symb_type* place(symb_type* p)
const noexcept {
691 return (symb_type*)b.place((
typename B::symb_type*)a.place(p));
718template<StrExpr A, StrExprForType<
typename A::symb_type> B>
743template<StrExpr A, StrExprForType<
typename A::symb_type> B,
bool last = true>
745 using symb_type =
typename A::symb_type;
748 template<
typename... Args>
requires (std::is_constructible_v<B, Args...>)
749 constexpr strexprjoin_c(
const A& a_, Args&&... args_) : a(a_), b(std::forward<Args>(args_)...) {}
750 constexpr size_t length()
const noexcept {
751 return a.length() + b.length();
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));
757 return a.place((symb_type*)b.place((
typename B::symb_type*)p));
776template<StrExpr A, convertible_to_strexpr<
typename A::symb_type> B>
778 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
795template<StrExpr A, convertible_to_strexpr<
typename A::symb_type> B>
797 return {a, to_strexpr<typename A::symb_type>(std::forward<B>(b))};
837 constexpr size_t length()
const noexcept {
840 constexpr symb_type* place(symb_type* p)
const noexcept {
877struct expr_char : expr_to_std_string<expr_char<K>>{
880 constexpr expr_char(K v) : value(v){}
881 constexpr size_t length() const noexcept {
884 constexpr symb_type* place(symb_type* p)
const noexcept {
902template<
typename K, StrExprForType<K> A>
922template<
typename K,
size_t N>
923struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
925 const K (&str)[N + 1];
926 constexpr expr_literal(
const K (&str_)[N + 1]) : str(str_){}
928 constexpr size_t length() const noexcept {
931 constexpr symb_type* place(symb_type* p)
const noexcept {
932 if constexpr (N != 0)
933 std::char_traits<K>::copy(p, str, N);
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) {
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>> {
998 using atype =
typename A::symb_type;
999 const K (&str)[N + 1];
1001 constexpr expr_literal_join(
const K (&str_)[N + 1],
const A& a_) : str(str_), a(a_){}
1003 constexpr size_t length() const noexcept {
1004 return N + a.length();
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));
1012 p = (symb_type*)a.place((atype*)p);
1013 std::char_traits<K>::copy(p, str, N);
1017 return (symb_type*)a.place((atype*)p);
1030constexpr expr_literal_join<
false, P, (N - 1), A>
operator+(
const A& a, T&& s) {
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) {
1059template<
typename K,
size_t N,
size_t S = ' '>
1061 using symb_type = K;
1062 constexpr size_t length()
const noexcept {
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));
1121 using symb_type = K;
1124 constexpr expr_pad(
size_t len_, K s_) : len(len_), s(s_){}
1125 constexpr size_t length()
const noexcept {
1128 constexpr symb_type* place(symb_type* p)
const noexcept {
1130 std::char_traits<K>::assign(p, len, s);
1153template<
typename K,
size_t N>
1154struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
1155 using symb_type = K;
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 {
1162 constexpr symb_type* place(symb_type* p)
const noexcept {
1164 for (
size_t i = 0; i < repeat_; i++) {
1165 std::char_traits<K>::copy(p, s, N);
1175 using symb_type =
typename A::symb_type;
1178 mutable size_t len_;
1179 constexpr expr_repeat_expr(
size_t repeat,
const A& expr) : repeat_(repeat), expr_(expr){}
1180 constexpr size_t length() const noexcept {
1182 len_ = expr_.length();
1183 return repeat_ * len_;
1187 constexpr symb_type* place(symb_type* p)
const noexcept {
1188 if (repeat_ && len_) {
1189 symb_type* start = p;
1191 for (
size_t i = repeat_; --i; ) {
1192 std::char_traits<symb_type>::copy(p, start, len_);
1213template<typename T, typename K = const_lit<T>::symb_type,
size_t M = const_lit<T>::Count>
requires (M > 0)
1214constexpr expr_repeat_lit<K, M - 1>
e_repeat(T&& s,
size_t l) {
1232constexpr expr_repeat_expr<A>
e_repeat(
const A& s,
size_t l) {
1249template<StrExpr A, StrExprForType<
typename A::symb_type> B>
1251 using symb_type =
typename A::symb_type;
1252 using my_type = expr_choice<A, B>;
1257 constexpr expr_choice(
const A& _a,
const B& _b,
bool _choice) : a(_a), b(_b), choice(_choice){}
1259 constexpr size_t length()
const noexcept {
1260 return choice ? a.length() : b.length();
1262 constexpr symb_type* place(symb_type* ptr)
const noexcept {
1263 return choice ? a.place(ptr) : (symb_type*)b.place((
typename B::symb_type*)ptr);
1280 using symb_type =
typename A::symb_type;
1281 using my_type = expr_if<A>;
1284 constexpr expr_if(
const A& _a,
bool _choice) : a(_a), choice(_choice){}
1286 constexpr size_t length()
const noexcept {
1287 return choice ? a.length() : 0;
1289 constexpr symb_type* place(symb_type* ptr)
const noexcept {
1290 return choice ? a.place(ptr) : ptr;
1340template<
typename L, StrExprForType<L> A,
size_t N,
bool Compare>
1342 using symb_type = L;
1343 const symb_type (&str)[N + 1];
1346 constexpr expr_choice_one_lit(
const symb_type (&_str)[N + 1],
const A& _a,
bool _choice) : str(_str), a(_a), choice(_choice){}
1348 constexpr size_t length()
const noexcept {
1349 return choice == Compare ? a.length() : N;
1351 constexpr symb_type* place(symb_type* ptr)
const noexcept {
1352 if (choice == Compare) {
1353 return (L*)a.place((
typename A::symb_type*)ptr);
1355 if constexpr (N != 0) {
1356 std::char_traits<symb_type>::copy(ptr, str, N);
1406template<
typename K,
size_t N,
typename P,
size_t M>
1408 using symb_type = K;
1409 const K (&str_a)[N + 1];
1410 const P (&str_b)[M + 1];
1412 constexpr expr_choice_two_lit(
const K(&_str_a)[N + 1],
const P(&_str_b)[M + 1],
bool _choice)
1413 : str_a(_str_a), str_b(_str_b), choice(_choice){}
1415 constexpr size_t length()
const noexcept {
1416 return choice ? N : M;
1418 constexpr symb_type* place(symb_type* ptr)
const noexcept {
1420 if constexpr (N != 0) {
1421 std::char_traits<symb_type>::copy(ptr, str_a, N);
1425 if constexpr (M != 0) {
1426 std::char_traits<symb_type>::copy(ptr, (
const K(&)[M + 1])str_b, M);
1461template<StrExpr A, StrExprForType<
typename A::symb_type> B>
1471template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1481template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
1490template<typename T, typename L, typename K = typename const_lit<T>::symb_type,
typename P =
typename const_lit<L>::symb_type,
1491 size_t N = const_lit<T>::Count,
size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
1492 requires is_equal_str_type_v<K, P>
1494 return {str_a, str_b, c};
1548template<typename T, size_t N = const_lit<T>::Count>
1550 using K =
typename const_lit<T>::symb_type;
1551 const K empty[1] = {0};
1564template<
typename K, StdStrSourceForType<K> T>
1566 using symb_type = K;
1569 expr_stdstr(
const T& t) : t_(t){}
1571 constexpr size_t length()
const noexcept {
1574 constexpr symb_type* place(symb_type* p)
const noexcept {
1575 size_t s = t_.size();
1576 std::char_traits<K>::copy(p, (
const K*)t_.data(), s);
1591template<
typename K, StdStrSource T>
1592struct convert_to_strexpr<K, T> {
1597constexpr const size_t npos =
static_cast<size_t>(-1);
1601std::char_traits<K> char_traits_selector(...);
1604using ch_traits =
decltype(char_traits_selector<K>(
int(0)));
1607concept FromIntNumber =
1608 is_one_of_type<std::remove_cv_t<T>,
unsigned char, int, short, long,
long long, unsigned,
unsigned short,
unsigned long,
unsigned long long>::value;
1611concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
1613template<
typename K,
bool I,
typename T>
1616 std::make_unsigned_t<T> val;
1617 constexpr need_sign(T t) : negate(t < 0), val(t < 0 ?
std::make_unsigned_t<T>{} - t : t) {}
1618 constexpr void after(K*& ptr) {
1624template<
typename K,
typename T>
1625struct need_sign<K, false, T> {
1627 constexpr need_sign(T t) : val(t){}
1628 constexpr void after(K*&) {}
1631template<
typename K,
typename T>
1632constexpr size_t fromInt(K* bufEnd, T val) {
1633 const char* twoDigit =
1634 "0001020304050607080910111213141516171819"
1635 "2021222324252627282930313233343536373839"
1636 "4041424344454647484950515253545556575859"
1637 "6061626364656667686970717273747576777879"
1638 "8081828384858687888990919293949596979899";
1640 need_sign<K, std::is_signed_v<T>, T> store(val);
1642 while (store.val >= 100) {
1643 const char* ptr = twoDigit + (store.val % 100) * 2;
1644 *--itr =
static_cast<K
>(ptr[1]);
1645 *--itr =
static_cast<K
>(ptr[0]);
1648 if (store.val < 10) {
1649 *--itr =
static_cast<K
>(
'0' + store.val);
1651 const char* ptr = twoDigit + store.val * 2;
1652 *--itr =
static_cast<K
>(ptr[1]);
1653 *--itr =
static_cast<K
>(ptr[0]);
1656 return size_t(bufEnd - itr);
1662template<
typename K,
typename T>
1664 using symb_type = K;
1665 using my_type = expr_num<K, T>;
1667 enum { bufSize = 24 };
1668 mutable K buf[bufSize];
1671 constexpr expr_num(T t) : value(t) {}
1672 constexpr expr_num(expr_num&& t) noexcept : value(t.value) {}
1674 constexpr size_t length() const noexcept {
1675 value =
static_cast<T
>(fromInt(buf + bufSize, value));
1676 return static_cast<size_t>(value);
1678 constexpr K* place(K* ptr)
const noexcept {
1679 size_t len =
static_cast<size_t>(value);
1680 ch_traits<K>::copy(ptr, buf + bufSize - len, len);
1695template<
typename K, FromIntNumber T>
1696struct convert_to_strexpr<K, T> {
1697 using type = expr_num<K, T>;
1715template<
typename K, FromIntNumber T>
1722enum class int_align { none, left, right, center };
1723enum class int_plus_sign {none, plus, space};
1724enum class int_prefix {none, lcase, ucase};
1726enum fmt_kinds :
unsigned {
1737template<
typename A,
typename B,
unsigned Kind>
1741 inline static constexpr unsigned kind = Kind;
1745 inline static constexpr unsigned kind = fmt_none;
1748template<
int_align A>
1750 inline static constexpr unsigned kind = fmt_align;
1753template<
size_t W
idth = 0>
1755 inline static constexpr unsigned kind = fmt_width;
1758template<
unsigned Fill = ' '>
1760 inline static constexpr unsigned kind = fmt_fill;
1763template<
int_plus_sign S>
1765 inline static constexpr unsigned kind = fmt_sign;
1769 inline static constexpr unsigned kind = fmt_upper;
1774 inline static constexpr unsigned kind = fmt_prefix;
1778 inline static constexpr unsigned kind = fmt_zero;
1782concept FmtParam =
requires {
1783 {std::remove_cvref_t<T>::kind} -> std::same_as<const unsigned&>;
1786template<FmtParam A, FmtParam B>
1787requires((A::kind & B::kind) == 0)
1788constexpr auto operator | (
const A&,
const B&) {
1789 return fmt_info<A, B, A::kind | B::kind>{};
1792inline constexpr align_info<int_align::left> l;
1793inline constexpr align_info<int_align::right> r;
1794inline constexpr align_info<int_align::center> c;
1797inline constexpr width_info<W> w;
1800inline constexpr fill_info<F> f;
1802inline constexpr sign_info<int_plus_sign::plus> sp;
1803inline constexpr sign_info<int_plus_sign::space> ss;
1804inline constexpr upper_info u;
1805inline constexpr prefix_info<int_prefix::lcase> p;
1806inline constexpr prefix_info<int_prefix::ucase> P;
1807inline constexpr zero_info z;
1809inline constexpr width_info<unsigned(-1)> wp;
1810inline constexpr fmt_default df;
1813struct extract_align : std::false_type {
1814 inline static constexpr int_align align = int_align::none;
1817template<
int_align A>
1818struct extract_align<align_info<A>> : std::true_type {
1819 inline static constexpr int_align align = A;
1822template<
typename A,
typename B,
unsigned U>
1823struct extract_align<fmt_info<A, B, U>> {
1824 inline static constexpr bool in_a = extract_align<A>::value, in_b = extract_align<B>::value, value = in_a | in_b;
1825 inline static constexpr int_align align = extract_align<std::conditional_t<in_a, A, std::conditional_t<in_b, B, align_info<int_align::none>>>>::align;
1829struct extract_width : std::false_type {
1830 inline static constexpr size_t width = 0;
1834struct extract_width<width_info<W>> : std::true_type {
1835 inline static constexpr size_t width = W;
1838template<
typename A,
typename B,
unsigned U>
1839struct extract_width<fmt_info<A, B, U>> {
1840 inline static constexpr bool in_a = extract_width<A>::value, in_b = extract_width<B>::value, value = in_a | in_b;
1841 inline static constexpr size_t width = extract_width<std::conditional_t<in_a, A, std::conditional_t<in_b, B, width_info<0>>>>::width;
1845struct extract_fill : std::false_type {
1846 inline static constexpr unsigned fill =
' ';
1850struct extract_fill<fill_info<F>> : std::true_type {
1851 inline static constexpr unsigned fill = F;
1854template<
typename A,
typename B,
unsigned U>
1855struct extract_fill<fmt_info<A, B, U>> {
1856 inline static constexpr bool in_a = extract_fill<A>::value, in_b = extract_fill<B>::value, value = in_a | in_b;
1857 inline static constexpr unsigned fill = extract_fill<std::conditional_t<in_a, A, std::conditional_t<in_b, B, fill_info<
' '>>>>::fill;
1861struct extract_sign : std::false_type {
1862 inline static constexpr int_plus_sign sign = int_plus_sign::none;
1865template<
int_plus_sign S>
1866struct extract_sign<sign_info<S>> : std::true_type {
1867 inline static constexpr int_plus_sign sign = S;
1870template<
typename A,
typename B,
unsigned U>
1871struct extract_sign<fmt_info<A, B, U>> {
1872 inline static constexpr bool in_a = extract_sign<A>::value, in_b = extract_sign<B>::value, value = in_a | in_b;
1873 inline static constexpr int_plus_sign sign = extract_sign<std::conditional_t<in_a, A, std::conditional_t<in_b, B, sign_info<int_plus_sign::none>>>>::sign;
1877struct extract_upper : std::false_type {};
1880struct extract_upper<upper_info> : std::true_type {};
1882template<
typename A,
typename B,
unsigned U>
1883struct extract_upper<fmt_info<A, B, U>> {
1884 inline static constexpr bool in_a = extract_upper<A>::value, in_b = extract_upper<B>::value,
1885 value = extract_upper<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1889struct extract_prefix : std::false_type{
1890 inline static constexpr int_prefix prefix = int_prefix::none;
1893template<
int_prefix P>
1894struct extract_prefix<prefix_info<P>> : std::true_type {
1895 inline static constexpr int_prefix prefix = P;
1898template<
typename A,
typename B,
unsigned U>
1899struct extract_prefix<fmt_info<A, B, U>> {
1900 inline static constexpr bool in_a = extract_prefix<A>::value, in_b = extract_prefix<B>::value, value = in_a | in_b;
1901 inline static constexpr int_prefix prefix = extract_prefix<std::conditional_t<in_a, A, std::conditional_t<in_b, B, prefix_info<int_prefix::none>>>>::prefix;
1905struct extract_zero : std::false_type{};
1908struct extract_zero<zero_info> : std::true_type {};
1910template<
typename A,
typename B,
unsigned U>
1911struct extract_zero<fmt_info<A, B, U>> {
1912 inline static constexpr bool in_a = extract_zero<A>::value, in_b = extract_zero<B>::value,
1913 value = extract_zero<std::conditional_t<in_a, A, std::conditional_t<in_b, B, std::false_type>>>::value;
1916template<
int_align Align,
unsigned W
idth,
unsigned Fill,
int_plus_sign Sign,
bool Upper,
int_prefix Prefix,
bool Zero>
1918 inline static constexpr int_align align = Align;
1919 inline static constexpr unsigned width = Width;
1920 inline static constexpr unsigned fill = Fill;
1921 inline static constexpr int_plus_sign sign = Sign;
1922 inline static constexpr bool upper = Upper;
1923 inline static constexpr int_prefix prefix = Prefix;
1924 inline static constexpr bool zero = Zero;
1928using p_to_fmt_t = fmt_params<
1929 extract_align<T>::align,
1930 extract_width<T>::width,
1931 extract_fill<T>::fill,
1932 extract_sign<T>::sign,
1933 extract_upper<T>::value,
1934 extract_prefix<T>::prefix,
1935 extract_zero<T>::value>;
1938using to_fmt_t = p_to_fmt_t<std::remove_cvref_t<T>>;
1941struct is_fmt_params : std::false_type{};
1943template<
int_align Align,
unsigned W
idth,
unsigned Fill,
int_plus_sign Sign,
bool Upper,
int_prefix Prefix,
bool Zero>
1944struct is_fmt_params<fmt_params<Align, Width, Fill, Sign, Upper, Prefix, Zero>> : std::true_type{};
1947concept FmtParamSet = is_fmt_params<T>::value;
1951template<f::FmtParam F>
1952constexpr auto operator|(
const F& f, fmt_end) {
1956template<
char C = 0,
char...Chars>
1957constexpr auto parse_fmt_symbol();
1959template<
unsigned N,
char C =
'Z',
char...Chars>
1960constexpr auto parse_width() {
1961 if constexpr (C >=
'0' && C <=
'9') {
1962 return parse_width<N * 10 + C -
'0', Chars...>();
1964 return w<N> | parse_fmt_symbol<C, Chars...>();
1968template<
unsigned N,
char C =
'Z',
char...Chars>
1969constexpr auto parse_fill() {
1970 if constexpr (C >=
'0' && C <=
'9') {
1971 return parse_fill<N * 16 + C -
'0', Chars...>();
1972 }
else if constexpr (C >=
'a' && C <=
'f') {
1973 return parse_fill<N * 16 + C -
'a' + 10, Chars...>();
1974 }
else if constexpr (C >=
'A' && C <=
'F') {
1975 return parse_fill<N * 16 + C -
'A' + 10, Chars...>();
1977 return f<N> | parse_fmt_symbol<C, Chars...>();
1981template<
char C,
char...Chars>
1982constexpr auto parse_fmt_symbol() {
1983 if constexpr (C ==
'0') {
1984 return z | parse_fmt_symbol<Chars...>();
1985 }
else if constexpr (C ==
'a') {
1986 return p | parse_fmt_symbol<Chars...>();
1987 }
else if constexpr (C ==
'A') {
1988 return P | parse_fmt_symbol<Chars...>();
1989 }
else if constexpr (C ==
'b') {
1990 return l | parse_fmt_symbol<Chars...>();
1991 }
else if constexpr (C ==
'c') {
1992 return c | parse_fmt_symbol<Chars...>();
1993 }
else if constexpr (C ==
'd') {
1994 return r | parse_fmt_symbol<Chars...>();
1995 }
else if constexpr (C ==
'e') {
1996 return sp | parse_fmt_symbol<Chars...>();
1997 }
else if constexpr (C ==
'f') {
1998 return ss | parse_fmt_symbol<Chars...>();
1999 }
else if constexpr (C ==
'E') {
2000 return u | parse_fmt_symbol<Chars...>();
2001 }
else if constexpr (C ==
'\'') {
2002 return parse_fmt_symbol<Chars...>();
2003 }
else if constexpr (C ==
'F') {
2004 return parse_fill<0, Chars...>();
2005 }
else if constexpr (C >=
'1' && C <=
'9') {
2006 return parse_width<C -
'0', Chars...>();
2012template<
unsigned R,
typename T>
2013struct fmt_radix_info {};
2015template<
unsigned N,
char C = 0,
char...Chars>
2016constexpr auto parse_radix() {
2017 if constexpr (C >=
'0' && C <=
'9') {
2018 return parse_radix<N * 10 + C -
'0', Chars...>();
2019 }
else if constexpr (C == 0) {
2020 return fmt_radix_info<N, to_fmt_t<fmt_default>>{};
2022 return fmt_radix_info<N,
decltype(parse_fmt_symbol<C, Chars...>())>{};
2026template<
char C1,
char C2,
char C3,
char...Chars>
2027constexpr auto skip_0x() {
2028 static_assert(C1 ==
'0' && C2 ==
'x' &&
"Fmt symbols must begin with 0x");
2029 static_assert(C3 >=
'1' && C3 <=
'9' &&
"Radix must begin with 1-9");
2030 return parse_radix<C3 -
'0', Chars...>();
2035template<
typename K,
bool Ucase>
2036inline constexpr K digits_symbols[36] = {K(
'0'), K(
'1'), K(
'2'), K(
'3'), K(
'4'), K(
'5'), K(
'6'), K(
'7'), K(
'8'), K(
'9'),
2037 K(Ucase ?
'A' :
'a'), K(Ucase ?
'B' :
'b'), K(Ucase ?
'C' :
'c'), K(Ucase ?
'D' :
'd'), K(Ucase ?
'E' :
'e'),
2038 K(Ucase ?
'F' :
'f'), K(Ucase ?
'G' :
'g'), K(Ucase ?
'H' :
'h'), K(Ucase ?
'I' :
'i'), K(Ucase ?
'J' :
'j'),
2039 K(Ucase ?
'K' :
'k'), K(Ucase ?
'L' :
'l'), K(Ucase ?
'M' :
'm'), K(Ucase ?
'N' :
'n'), K(Ucase ?
'O' :
'o'),
2040 K(Ucase ?
'P' :
'p'), K(Ucase ?
'Q' :
'q'), K(Ucase ?
'R' :
'r'), K(Ucase ?
'S' :
's'), K(Ucase ?
'T' :
't'),
2041 K(Ucase ?
'U' :
'u'), K(Ucase ?
'V' :
'v'), K(Ucase ?
'W' :
'w'), K(Ucase ?
'X' :
'x'), K(Ucase ?
'Y' :
'y'),
2042 K(Ucase ?
'Z' :
'z'),
2045template<FromIntNumber T,
unsigned Radix, f::FmtParamSet FP>
requires (Radix > 1 && Radix <= 36)
2046struct expr_integer_src {
2049 constexpr expr_integer_src(T v) : value_(v){}
2050 constexpr expr_integer_src(T v,
unsigned w) : value_(v), width_(w){}
2052 template<is_std_
string_v S>
2055 template<
typename S>
requires std::is_constructible_v<S, empty_expr<typename S::symb_type>>
2059template<is_one_of_
char_v K, FromIntNumber T,
unsigned Radix, f::FmtParamSet FP>
2060requires (Radix > 1 && Radix <= 36)
2062 using symb_type = K;
2064 enum { bufSize = 64 };
2065 mutable K buf[bufSize];
2069 mutable bool negate_{};
2071 constexpr expr_integer(T t) : value_(t){}
2072 constexpr expr_integer(T t,
unsigned w) : value_(t), width_(w){}
2073 constexpr expr_integer(
const expr_integer_src<T, Radix, FP>& v) : value_(v.value_), width_(v.width_){}
2075 constexpr expr_integer(expr_integer&& t) noexcept : value_(t.value_), width_(t.width_){}
2077 constexpr size_t length() const noexcept {
2078 K* bufEnd = std::end(buf), *itr = bufEnd;
2081 need_sign<K, std::is_signed_v<T>, T> store(value_);
2083 *--itr = digits_symbols<K, FP::upper>[store.val % Radix];
2086 if constexpr (std::is_signed_v<T>) {
2087 negate_ = store.negate;
2090 *--itr = digits_symbols<K, FP::upper>[0];
2092 size_t len = bufEnd - itr;
2094 if constexpr (std::is_signed_v<T>) {
2095 if constexpr (FP::sign != f::int_plus_sign::none) {
2103 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2105 if constexpr (Radix != 8) {
2109 if constexpr (FP::width == unsigned(-1)) {
2110 return std::max<size_t>(width_, len);
2112 return std::max<size_t>(FP::width, len);
2115 constexpr K* place(K* ptr)
const noexcept {
2116 size_t len =
static_cast<size_t>(value_), all_len = len;
2117 if constexpr (std::is_signed_v<T>) {
2118 if constexpr (FP::sign != f::int_plus_sign::none) {
2126 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2128 if constexpr (Radix != 8) {
2132 if constexpr (FP::zero) {
2133 if constexpr (std::is_signed_v<T>) {
2137 if constexpr (FP::sign == f::int_plus_sign::plus) {
2139 }
else if constexpr (FP::sign == f::int_plus_sign::space) {
2144 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2146 if constexpr (Radix == 2) {
2147 *ptr++ = FP::prefix == f::int_prefix::lcase ? K(
'b') : K(
'B');
2148 }
else if constexpr (Radix == 16) {
2149 *ptr++ = FP::prefix == f::int_prefix::lcase ? K(
'x') : K(
'X');
2153 if constexpr (FP::width == unsigned(-1)) {
2154 if (width_ > all_len) {
2155 before = width_ - all_len;
2158 if (FP::width > all_len) {
2159 before = FP::width - all_len;
2163 ch_traits<K>::assign(ptr, before, K(
'0'));
2166 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2169 size_t before = 0, after = 0;
2170 if constexpr (FP::width == unsigned(-1)) {
2171 if (width_ > all_len) {
2172 if constexpr (FP::align == f::int_align::left) {
2173 after = width_ - all_len;
2174 }
else if constexpr (FP::align == f::int_align::center) {
2175 before = (width_ - all_len) / 2;
2176 after = width_ - all_len - before;
2178 before = width_ - all_len;
2182 if (FP::width > all_len) {
2183 if constexpr (FP::align == f::int_align::left) {
2184 after = FP::width - all_len;
2185 }
else if constexpr (FP::align == f::int_align::center) {
2186 before = (FP::width - all_len) / 2;
2187 after = FP::width - all_len - before;
2189 before = FP::width - all_len;
2194 ch_traits<K>::assign(ptr, before, K(FP::fill));
2197 if constexpr (std::is_signed_v<T>) {
2201 if constexpr (FP::sign == f::int_plus_sign::plus) {
2203 }
else if constexpr (FP::sign == f::int_plus_sign::space) {
2208 if constexpr (FP::prefix != f::int_prefix::none && (Radix == 2 || Radix == 8 || Radix == 16)) {
2210 if constexpr (Radix == 2) {
2211 *ptr++ = FP::prefix == f::int_prefix::lcase ? K(
'b') : K(
'B');
2212 }
else if constexpr (Radix == 16) {
2213 *ptr++ = FP::prefix == f::int_prefix::lcase ? K(
'x') : K(
'X');
2216 ch_traits<K>::copy(ptr, std::end(buf) - len, len);
2220 ch_traits<K>::assign(ptr, after, K(FP::fill));
2228template<FromIntNumber T,
unsigned Radix, f::FmtParamSet FP>
requires (Radix > 1 && Radix <= 36)
2229template<is_std_string_v S>
2230expr_integer_src<T, Radix, FP>::operator S()
const {
2231 using st =
typename S::value_type;
2232 using Al =
typename S::allocator_type;
2233 return to_std_string<st, Al>(expr_integer<st, T, Radix, FP>{value_, width_});
2236template<FromIntNumber T,
unsigned Radix, f::FmtParamSet FP>
requires (Radix > 1 && Radix <= 36)
2238expr_integer_src<T, Radix, FP>::operator S()
const {
2239 using st =
typename S::symb_type;
2240 return S{expr_integer<st, T, Radix, FP>{value_, width_}};
2243template<
typename K, FromIntNumber T,
unsigned Radix, f::FmtParamSet FP>
2244struct convert_to_strexpr<K, expr_integer_src<T, Radix, FP>> {
2245 using type = expr_integer<K, T, Radix, FP>;
2248template<
unsigned R,
typename T,
typename F>
2249struct flags_checker {
2251 using flags_t = f::to_fmt_t<F>;
2252 inline static constexpr unsigned Radix = R;
2255template<
typename T,
bool ArgW
idth>
2256concept good_int_flags =
2257 T::Radix > 1 && T::Radix <= 36
2260 && (T::flags_t::align == f::int_align::none || !T::flags_t::zero)
2263 && (std::is_signed_v<typename T::val_t> || T::flags_t::sign == f::int_plus_sign::none)
2266 && (T::flags_t::width == unsigned(-1)) == ArgWidth
2269 && (T::flags_t::prefix == f::int_prefix::none || T::Radix == 2 || T::Radix == 8 || T::Radix == 16);
2378template<
unsigned R, auto fp = f::df, FromIntNumber T>
requires good_int_flags<flags_checker<R, T,
decltype(fp)>,
false>
2380 using fmt_param = f::to_fmt_t<
decltype(fp)>;
2381 return expr_integer_src<T, R, fmt_param>{v};
2387template<
unsigned R, auto fp = f::df, FromIntNumber T>
requires good_int_flags<flags_checker<R, T,
decltype(fp)>,
true>
2389 using fmt_param = f::to_fmt_t<
decltype(fp)>;
2390 return expr_integer_src<T, R, fmt_param>{v, w};
2393template<
typename T,
unsigned R,
typename F>
requires good_int_flags<flags_checker<R, T, F>,
false>
2394auto operator / (T v,
const f::fmt_radix_info<R, F>&) {
2395 return expr_integer_src<T, R, f::to_fmt_t<F>>{v};
2400 using symb_type = K;
2401 mutable u8s buf[40];
2404 constexpr expr_real(
double d) : v(d) {}
2405 constexpr expr_real(
float d) : v(d) {}
2407 size_t length() const noexcept {
2408 auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
2409 l = ec != std::errc{} ? 0 : ptr - buf;
2412 K* place(K* ptr)
const noexcept {
2413 if constexpr (
sizeof(K) ==
sizeof(buf[0])) {
2414 ch_traits<K>::copy(ptr, (K*)buf, l);
2416 for (
size_t i = 0; i < l; i++) {
2434template<
typename K, std::
floating_po
int T>
2435struct convert_to_strexpr<K, T> {
2436 using type = expr_real<K>;
2450template<
typename K>
requires is_one_of_char_v<K>
2451inline constexpr expr_real<K>
e_num(
double t) {
2455template<FromIntNumber Val,
bool All,
bool Ucase,
bool Ox>
2456struct expr_hex_src {
2457 explicit constexpr expr_hex_src(Val v) : v_(v){}
2461template<
typename K, FromIntNumber Val,
bool All,
bool Ucase,
bool Ox>
2463 using symb_type = K;
2464 mutable need_sign<K, std::is_signed_v<Val>, Val> v_;
2465 mutable K buf_[
sizeof(Val) * 2]{};
2467 explicit constexpr expr_hex(Val v) : v_(v){}
2468 constexpr expr_hex(
const expr_hex_src<Val, All, Ucase, Ox>& v) : v_(v.v_){}
2470 constexpr size_t length() const noexcept {
2471 K *ptr = buf_ + std::size(buf_);
2474 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2478 *--ptr = digits_symbols<K, Ucase>[v_.val & 0xF];
2483 if constexpr (All) {
2484 if (
size_t need =
sizeof(Val) * 2 - l) {
2485 ch_traits<K>::assign(buf_, need, K(
'0'));
2487 l =
sizeof(Val) * 2;
2493 if constexpr (std::is_signed_v<Val>) {
2494 return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0);
2496 return l + (Ox ? 2 : 0);
2498 constexpr K* place(K* ptr)
const noexcept {
2499 if constexpr (std::is_signed_v<Val>) {
2508 if constexpr (All) {
2509 ch_traits<K>::copy(ptr, buf_,
sizeof(Val) * 2);
2510 return ptr +
sizeof(Val) * 2;
2512 ch_traits<K>::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val);
2513 return ptr + v_.val;
2579template<
unsigned Flags = 0, FromIntNumber T>
2581 return expr_hex_src<T, (Flags &
HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2584template<
char c = 0,
char...Chars>
2585constexpr unsigned parse_f16_flags() {
2586 if constexpr (c ==
'1') {
2588 }
else if constexpr (c ==
'2') {
2589 return HexFlags::Lcase | parse_f16_flags<Chars...>();
2590 }
else if constexpr (c ==
'3') {
2591 return HexFlags::No0x | parse_f16_flags<Chars...>();
2600template<FromIntNumber T,
unsigned Flags>
2601constexpr auto operator/(T v,
const f16flags<Flags>&) {
2602 return expr_hex_src<T, (Flags &
HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
2605inline namespace literals {
2606template<
char...Chars>
2607constexpr auto operator""_f16() {
2608 return f16flags<parse_f16_flags<Chars...>()>{};
2620template<
typename K,
typename Val,
bool All,
bool Ucase,
bool Ox>
2621struct convert_to_strexpr<K, expr_hex_src<Val, All, Ucase, Ox>> {
2622 using type = expr_hex<K, Val, All, Ucase, Ox>;
2626struct expr_pointer : expr_hex<K, uintptr_t, true, true, true> {
2627 constexpr expr_pointer(
const void* ptr) : expr_hex<K, uintptr_t, true, true, true>((uintptr_t)ptr){}
2638template<
typename K,
typename T>
2639struct convert_to_strexpr<K, const T*> {
2640 using type = expr_pointer<K>;
2643template<
typename K, StrExprForType<K> A,
bool Left>
2645 using symb_type = K;
2649 mutable size_t alen_{};
2650 constexpr expr_fill(K symbol,
size_t width,
const A& a) : symbol_(symbol), width_(width), a_(a){}
2652 constexpr size_t length() const noexcept {
2653 alen_ = a_.length();
2654 return std::max(alen_, width_);
2656 constexpr K* place(K* ptr)
const noexcept {
2657 if (alen_ >= width_) {
2658 return (K*)a_.place((
typename A::symb_type*)ptr);
2660 size_t w = width_ - alen_;
2661 if constexpr (Left) {
2662 ch_traits<K>::assign(ptr, w, symbol_);
2664 return (K*)a_.place((
typename A::symb_type*)ptr);
2666 ptr = (K*)a_.place((
typename A::symb_type*)ptr);
2667 ch_traits<K>::assign(ptr, w, symbol_);
2694template<StrExpr A,
typename K =
typename A::symb_type>
2695expr_fill<K, A, true>
e_fill_left(
const A& a,
size_t width, K symbol = K(
' ')) {
2696 return {symbol, width, a};
2720template<StrExpr A,
typename K =
typename A::symb_type>
2721expr_fill<K, A, false>
e_fill_right(
const A& a,
size_t width, K symbol = K(
' ')) {
2722 return {symbol, width, a};
2741template<
typename K,
typename T,
size_t I,
bool tail,
bool skip_empty>
2742struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
2743 using symb_type = K;
2744 using my_type = expr_join<K, T, I, tail, skip_empty>;
2748 constexpr expr_join(
const T& _s,
const K* _delim) : s(_s), delim(_delim){}
2750 constexpr size_t length() const noexcept {
2752 for (
const auto& t: s) {
2753 size_t len = t.length();
2754 if (len > 0 || !skip_empty) {
2755 if (I > 0 && l > 0) {
2761 return l + (tail && I > 0 && (l > 0 || (!skip_empty && std::size(s) > 0))? I : 0);
2763 constexpr K* place(K* ptr)
const noexcept {
2768 for (
const auto& t: s) {
2769 size_t copyLen = t.length();
2770 if ((copyLen > 0 || !skip_empty) && I > 0 && write != ptr) {
2771 ch_traits<K>::copy(write, delim, I);
2774 ch_traits<K>::copy(write, t.data(), copyLen);
2777 if (tail && I > 0 && (write != ptr || (!skip_empty && std::size(s) > 0))) {
2778 ch_traits<K>::copy(write, delim, I);
2798template<bool tail = false, bool skip_empty = false, typename L, typename K = typename const_lit<L>::symb_type,
size_t I = const_lit<L>::Count,
typename T>
2799inline constexpr auto e_join(
const T& s, L&& d) {
2800 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
2804concept is_const_pattern = N > 1 && N <= 17;
2806template<
typename K,
size_t I>
2808 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) *
sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
2812struct _ascii_mask<K, 0> {
2813 constexpr static const size_t value = 0;
2818 using uns = std::make_unsigned_t<K>;
2819 constexpr static const size_t WIDTH =
sizeof(size_t) /
sizeof(uns);
2820 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
2824constexpr inline bool isAsciiUpper(K k) {
2825 return k >=
'A' && k <=
'Z';
2829constexpr inline bool isAsciiLower(K k) {
2830 return k >=
'a' && k <=
'z';
2834constexpr inline K makeAsciiLower(K k) {
2835 return isAsciiUpper(k) ? k | 0x20 : k;
2839constexpr inline K makeAsciiUpper(K k) {
2840 return isAsciiLower(k) ? k & ~0x20 : k;
2843enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
2844template<TrimS
ides S,
typename K,
size_t N,
bool withSpaces = false>
2845struct trim_operator;
2848struct digits_selector {
2849 using wider_type = uint16_t;
2853struct digits_selector<2> {
2854 using wider_type = uint32_t;
2858struct digits_selector<4> {
2859 using wider_type = uint64_t;
2873template<
bool CanNegate,
bool CheckOverflow,
typename T>
2874struct result_type_selector {
2879struct result_type_selector<true, false, T> {
2880 using type = std::make_unsigned_t<T>;
2883template<
unsigned Base>
2884constexpr unsigned digit_width() {
2903template<
typename T,
unsigned Base>
2904constexpr unsigned max_overflow_digits = (
sizeof(T) * CHAR_BIT) / digit_width<Base>();
2907struct convert_result {
2914 inline static constexpr uint8_t NUMBERS[] = {
2915 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2916 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
2917 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
2918 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
2919 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
2920 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2921 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2922 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2923 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2924 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
2926 template<
typename K,
unsigned Base>
2927 static constexpr std::make_unsigned_t<K> toDigit(K s) {
2928 auto us =
static_cast<std::make_unsigned_t<K>
>(s);
2929 if constexpr (Base <= 10) {
2932 if constexpr (
sizeof(K) == 1) {
2935 return us < 256 ? NUMBERS[us] : us;
2940 template<
typename K, ToIntNumber T,
unsigned Base,
bool CheckOverflow>
2942 static constexpr convert_result<T> parse(
const K* start,
const K* current,
const K* end,
bool negate) {
2943 using u_type = std::make_unsigned_t<T>;
2944 #ifndef HAS_BUILTIN_OVERFLOW
2945 u_type maxMult = 0, maxAdd = 0;
2946 if constexpr (CheckOverflow) {
2947 maxMult = std::numeric_limits<u_type>::max() / Base;
2948 maxAdd = std::numeric_limits<u_type>::max() % Base;
2952 unsigned maxDigits = max_overflow_digits<u_type, Base>;
2954 const K* from = current;
2956 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
2958 if (no_need_check_o_f) {
2960 const u_type digit = toDigit<K, Base>(*current);
2961 if (digit >= Base) {
2964 number = number * Base + digit;
2965 if (++current == end) {
2971 for (;maxDigits; maxDigits--) {
2972 const u_type digit = toDigit<K, Base>(*current);
2973 if (digit >= Base) {
2976 number = number * Base + digit;
2983 const u_type digit = toDigit<K, Base>(*current);
2984 if (digit >= Base) {
2987 #ifdef HAS_BUILTIN_OVERFLOW
2988 if (__builtin_mul_overflow(number, Base, &number) ||
2989 __builtin_add_overflow(number, digit, &number)) {
2991 if (number < maxMult || (number == maxMult && number < maxAdd)) {
2992 number = number * Base + digit;
2996 while(++current < end) {
2997 if (toDigit<K, Base>(*current) >= Base) {
3003 if (++current == end) {
3011 if constexpr (std::is_signed_v<T>) {
3012 result = negate ? 0 - number : number;
3013 if constexpr (CheckOverflow) {
3015 if (number > (u_type)std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
3026 return {result, error, size_t(current - start)};
3033 template<
typename K, ToIntNumber T,
unsigned Base = 0,
bool CheckOverflow = true,
bool SkipWs = true,
bool AllowSign = true>
3034 requires(Base == unsigned(-1) || (Base < 37 && Base != 1))
3035 static constexpr convert_result<T> to_integer(const K* start,
size_t len) noexcept {
3036 const K *ptr = start, *end = ptr + len;
3037 bool negate =
false;
3038 if constexpr (SkipWs) {
3039 while (ptr < end && std::make_unsigned_t<K>(*ptr) <=
' ')
3043 if constexpr (std::is_signed_v<T>) {
3044 if constexpr (AllowSign) {
3049 }
else if (*ptr ==
'-') {
3061 }
else if constexpr (AllowSign) {
3070 if constexpr (Base == 0 || Base == unsigned(-1)) {
3074 if (*ptr ==
'x' || *ptr ==
'X') {
3075 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
3077 if constexpr (Base == unsigned(-1)) {
3078 if (*ptr ==
'b' || *ptr ==
'B') {
3079 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
3081 if (*ptr ==
'o' || *ptr ==
'O') {
3082 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
3085 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
3089 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
3091 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
3097template<
typename K,
typename Impl>
3098class null_terminated {
3106 constexpr const K* c_str()
const {
return static_cast<const Impl*
>(
this)->symbols(); }
3109template<
typename K,
typename Impl,
bool Mutable>
class buffer_pointers;
3119template<
typename K,
typename Impl>
3120class buffer_pointers<K, Impl, false> {
3121 constexpr const Impl& d()
const {
return *
static_cast<const Impl*
>(
this); }
3129 constexpr const K* data()
const {
return d().symbols(); }
3136 constexpr const K* begin()
const {
return d().symbols(); }
3143 constexpr const K* end()
const {
return d().symbols() + d().length(); }
3150 constexpr const K* cbegin()
const {
return d().symbols(); }
3157 constexpr const K* cend()
const {
return d().symbols() + d().length(); }
3160template<
typename K,
typename Impl>
3161class buffer_pointers<K, Impl, true> :
public buffer_pointers<K, Impl, false> {
3162 constexpr Impl& d() {
return *
static_cast<Impl*
>(
this); }
3163 using base = buffer_pointers<K, Impl, false>;
3171 constexpr const K* data()
const {
return base::data(); }
3178 constexpr const K* begin()
const {
return base::begin(); }
3185 constexpr const K* end()
const {
return base::end(); }
3192 constexpr const K* cbegin()
const {
return base::cbegin(); }
3199 constexpr const K* cend()
const {
return base::cend(); }
3206 constexpr K* data() {
return d().str(); }
3213 constexpr K* begin() {
return d().str(); }
3220 constexpr K* end() {
return d().str() + d().length(); }
3229template<
typename K,
typename StrSrc>
3231 using str_t = StrSrc;
3236 constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
3242 return text_.length() == str::npos;
3251 if (!text_.length()) {
3256 }
else if (text_.length() == str::npos) {
3257 return {
nullptr, 0};
3259 size_t pos = text_.find(delim_),
next = 0;
3260 if (pos == str::npos) {
3261 pos = text_.length();
3264 next = pos + delim_.length();
3266 str_t result{text_.str, pos};
3279inline constexpr from_end to_end{0};
3281template<
typename T,
typename K>
3282concept is_slice_find =
requires (
const T& t) {
3283 requires is_equal_str_type_v<K, symb_type_from_src_t<
decltype(t.pattern_)>>;
3284 { T::to_end_of_pattern }->std::same_as<
const bool&>;
3285 { T::offset_from_found }->std::same_as<
const bool&>;
3286 { T::find_reverse }->std::same_as<
const bool&>;
3287 { T::all_at_fail }->std::same_as<
const bool&>;
3288 { t.offset_ } -> std::convertible_to<size_t>;
3289 { t.skip_ } -> std::convertible_to<size_t>;
3290 { t.find(t.pattern_,
size_t(0)) } -> std::convertible_to<size_t>;
3294using slice_find_func = size_t(*)(T, T, size_t);
3296template<
typename Pattern,
bool ToEndOfPattern,
bool OffsetFromFound,
bool AllAtFail,
bool FindReverse, slice_find_func<Pattern> F>
3298 inline static constexpr const bool to_end_of_pattern = ToEndOfPattern;
3299 inline static constexpr const bool offset_from_found = OffsetFromFound;
3300 inline static constexpr const bool find_reverse = FindReverse;
3301 inline static constexpr const bool all_at_fail = AllAtFail;
3303 template<StrSource S>
3304 constexpr slice_find(S&& pattern,
size_t offset = 0,
size_t skip = 0) {
3305 pattern_ = std::forward<S>(pattern);
3310 constexpr size_t find(Pattern where,
size_t offset)
const {
3311 return F(where, pattern_, offset);
3318template<
typename T,
typename K>
3319concept first_index_in_str = std::is_same_v<T, from_end> || std::is_convertible_v<T, size_t> || is_slice_find<T, K>;
3321template<
typename T,
typename K>
3322concept last_index_in_str = first_index_in_str<T, K> || std::is_same_v<T, from_begin>;
3348template<
typename K,
typename StrRef,
typename Impl,
bool Mutable>
3349class str_src_algs :
public buffer_pointers<K, Impl, Mutable> {
3350 constexpr const Impl& d()
const noexcept {
3351 return *
static_cast<const Impl*
>(
this);
3353 constexpr size_t _len()
const noexcept {
3354 return d().length();
3356 constexpr const K* _str()
const noexcept {
3357 return d().symbols();
3359 constexpr bool _is_empty()
const noexcept {
3360 return d().is_empty();
3364 using symb_type = K;
3365 using str_piece = StrRef;
3366 using traits = ch_traits<K>;
3367 using uns_type = std::make_unsigned_t<K>;
3368 using my_type = Impl;
3369 using base = str_src_algs<K, StrRef, Impl, Mutable>;
3370 str_src_algs() =
default;
3384 constexpr K*
place(K* ptr)
const noexcept {
3385 size_t myLen = _len();
3386 traits::copy(ptr, _str(), myLen);
3400 size_t tlen = std::min(_len(), bufSize - 1);
3401 traits::copy(buffer, _str(), tlen);
3421 constexpr std::basic_string_view<D>
to_sv() const noexcept {
3422 return {(
const D*)_str(), _len()};
3431 constexpr operator std::basic_string_view<D, Traits>()
const {
3432 return {(
const D*)_str(), _len()};
3440 template<
typename D = K,
typename Traits = std::
char_traits<D>,
typename Allocator = std::allocator<D>>
requires is_equal_str_type_v<K, D>
3441 constexpr std::basic_string<D, Traits, Allocator>
to_string()
const {
3442 return {(
const D*)_str(), _len()};
3451 constexpr operator std::basic_string<D, Traits, Allocator>()
const {
3452 return {(
const D*)_str(), _len()};
3460 constexpr operator str_piece() const noexcept {
3461 return str_piece{_str(), _len()};
3469 constexpr str_piece
to_str() const noexcept {
3470 return {_str(), _len()};
3495 [[deprecated(
"Use 'slice' or [start, end] in C++23")]]
3496 constexpr str_piece
operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
3497 size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
3498 idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
3501 if (idxStart > idxEnd)
3503 return str_piece{_str() + idxStart, idxEnd - idxStart};
3573 template<first_index_in_str<K> S, last_index_in_str<K> E>
3574 constexpr str_piece
slice(S start, E end)
const noexcept {
3575 size_t idxStart, idxEnd, len = _len();
3576 if constexpr (is_slice_find<S, K>) {
3577 size_t offset = std::min(S::find_reverse ? len - start.offset_ : start.offset_, len), skip = start.skip_;
3579 size_t find = start.find(d(), offset);
3580 if (find == str::npos) {
3581 if constexpr (S::all_at_fail) {
3589 if constexpr (S::to_end_of_pattern) {
3590 idxStart = find + start.pattern_.length();
3597 if constexpr (!S::find_reverse) {
3598 offset += start.pattern_.length();
3601 }
else if constexpr (std::is_same_v<S, from_end>) {
3602 idxStart = start.idx_ >= len ? 0 : len - start.idx_;
3604 idxStart = std::min(
size_t(start), len);
3607 if constexpr (is_slice_find<E, K>) {
3608 size_t offset = std::min(E::offset_from_found ? idxStart + end.offset_ : E::find_reverse ? len - end.offset_ : end.offset_, len), skip = end.skip_;
3610 size_t find = end.find(d(), offset);
3611 if (find == str::npos) {
3612 if constexpr (E::all_at_fail) {
3620 if constexpr (E::to_end_of_pattern) {
3621 idxEnd = find + end.pattern_.length();
3628 if constexpr (!E::find_reverse) {
3629 offset += end.pattern_.length();
3632 }
else if constexpr (std::is_same_v<E, from_begin>) {
3633 idxEnd = std::max(idxStart, std::min(end.idx_, len));
3634 }
else if constexpr (std::is_same_v<E, from_end>) {
3635 idxEnd = std::max(idxStart, end.idx_ >= len ? 0 : len - end.idx_);
3638 idxEnd = len - idxStart <= e ? len : idxStart + e;
3641 return str_piece{_str() + idxStart, idxEnd - idxStart};
3644#if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202211L
3649 template<first_index_in_str<K> S, last_index_in_str<K> E>
3650 constexpr str_piece operator[](S start, E end)
const noexcept {
3651 return slice(start, end);
3658 template<
typename E>
requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3659 constexpr str_piece
prefix(E count)
const noexcept {
3660 return slice(0, count);
3666 template<
typename E>
requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3667 constexpr str_piece
tail(E count)
const noexcept {
3668 return slice(count, to_end);
3674 template<
typename E>
requires std::is_convertible_v<E, size_t> || is_slice_find<E, K>
3675 constexpr str_piece
suffix(E count)
const noexcept {
3676 if constexpr (std::is_convertible_v<E, size_t>) {
3677 return slice(from_end(
static_cast<size_t>(count)), to_end);
3679 return slice(count, to_end);
3692 [[deprecated(
"Use 'slice' or [start, len] in C++23")]]
3693 constexpr str_piece
mid(
size_t from,
size_t len = -1) const noexcept {
3694 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
3697 if (idxStart > idxEnd)
3699 return str_piece{_str() + idxStart, idxEnd - idxStart};
3715 constexpr str_piece
from_to(
size_t from,
size_t to)
const noexcept {
3716 return str_piece{_str() + from, to - from};
3728 [[deprecated(
"Use 'slice(0, begin_or_all(pattern, offset))' or '[0, begin_or_all(pattern, offset)]' in C++23, or 'slice(0, \"pattern\"_boa))'")]]
3729 constexpr str_piece
until(str_piece pattern,
size_t offset = 0) const noexcept {
3749 constexpr K
at(ptrdiff_t idx)
const {
3750 return _str()[idx >= 0 ? idx : _len() + idx];
3754 constexpr int compare(
const K* text,
size_t len)
const {
3755 size_t myLen = _len();
3756 int cmp = traits::compare(_str(), text, std::min(myLen, len));
3757 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
3768 return compare(o.symbols(), o.length());
3779 size_t myLen = _len(), idx = 0;
3780 const K* ptr = _str();
3781 for (; idx < myLen; idx++) {
3782 uns_type s1 = (uns_type)text[idx];
3786 uns_type s2 = (uns_type)ptr[idx];
3789 }
else if (s1 > s2) {
3793 return text[idx] == 0 ? 0 : -1;
3796 constexpr bool equal(
const K* text,
size_t len)
const noexcept {
3797 return len == _len() && traits::compare(_str(), text, len) == 0;
3807 constexpr bool equal(str_piece other)
const noexcept {
3808 return equal(other.symbols(), other.length());
3819 return equal(other._str(), other._len());
3828 return compare(other._str(), other._len()) <=> 0;
3836 template<typename T, size_t N = const_lit_for<K, T>::Count>
3838 return N - 1 == _len() && traits::compare(_str(), (
const K*)other, N - 1) == 0;
3846 template<typename T, size_t N = const_lit_for<K, T>::Count>
3848 size_t myLen = _len();
3849 int cmp = traits::compare(_str(), (
const K*)other, std::min(myLen, N - 1));
3850 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
3856 constexpr int compare_ia(
const K* text,
size_t len)
const noexcept {
3858 return _is_empty() ? 0 : 1;
3859 size_t myLen = _len(), checkLen = std::min(myLen, len);
3860 const uns_type *ptr1 =
reinterpret_cast<const uns_type*
>(_str()), *ptr2 =
reinterpret_cast<const uns_type*
>(text);
3861 while (checkLen--) {
3862 uns_type s1 = *ptr1++, s2 = *ptr2++;
3865 s1 = makeAsciiLower(s1);
3866 s2 = makeAsciiLower(s2);
3872 return myLen == len ? 0 : myLen > len ? 1 : -1;
3883 return compare_ia(text.symbols(), text.length());
3894 constexpr bool equal_ia(str_piece text)
const noexcept {
3895 size_t len = _len();
3896 if (len != text.length())
3898 const uns_type *ptr1 =
reinterpret_cast<const uns_type*
>(_str()), *ptr2 =
reinterpret_cast<const uns_type*
>(text.symbols());
3900 uns_type s1 = *ptr1++, s2 = *ptr2++;
3903 if (isAsciiUpper(s1)) {
3904 if (s1 == (s2 & ~0x20)) {
3907 }
else if (isAsciiLower(s1) && s1 == (s2 | 0x20)) {
3922 constexpr bool less_ia(str_piece text)
const noexcept {
3923 return compare_ia(text.symbols(), text.length()) < 0;
3926 constexpr size_t find(
const K* pattern,
size_t lenPattern,
size_t offset)
const noexcept {
3927 size_t lenText = _len();
3930 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
3933 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
3935 for (
const K* fnd = text + offset;; ++fnd) {
3936 fnd = traits::find(fnd, last - fnd, first);
3939 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
3940 return static_cast<size_t>(fnd - text);
3953 constexpr size_t find(str_piece pattern,
size_t offset = 0) const noexcept {
3954 return find(pattern.symbols(), pattern.length(), offset);
3972 template<
typename Exc,
typename ... Args>
requires std::is_constructible_v<Exc, Args...>
3973 constexpr size_t find_or_throw(str_piece pattern,
size_t offset = 0, Args&& ... args)
const {
3974 if (
auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
3977 throw Exc(std::forward<Args>(args)...);
3989 constexpr size_t find_end(str_piece pattern,
size_t offset = 0) const noexcept {
3990 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
3991 return fnd == str::npos ? fnd : fnd + pattern.length();
4003 constexpr size_t find_or_all(str_piece pattern,
size_t offset = 0) const noexcept {
4004 auto fnd = find(pattern.symbols(), pattern.length(), offset);
4005 return fnd == str::npos ? _len() : fnd;
4018 auto fnd = find(pattern.symbols(), pattern.length(), offset);
4019 return fnd == str::npos ? _len() : fnd + pattern.length();
4022 constexpr size_t find_last(
const K* pattern,
size_t lenPattern,
size_t offset)
const noexcept {
4023 if (lenPattern == 1)
4024 return find_last(pattern[0], offset);
4025 size_t lenText = std::min(_len(), offset);
4028 if (!lenPattern || lenPattern > lenText)
4032 const K *text = _str() + lenPattern, last = pattern[lenPattern];
4033 lenText -= lenPattern;
4035 if (text[--lenText] == last) {
4036 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
4053 constexpr size_t find_last(str_piece pattern,
size_t offset = -1) const noexcept {
4054 return find_last(pattern.symbols(), pattern.length(), offset);
4067 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
4068 return fnd == str::npos ? fnd : fnd + pattern.length();
4081 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
4082 return fnd == str::npos ? _len() : fnd;
4095 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
4096 return fnd == str::npos ? _len() : fnd + pattern.length();
4108 constexpr bool contains(str_piece pattern,
size_t offset = 0) const noexcept {
4109 return find(pattern, offset) != str::npos;
4121 constexpr size_t find(K s,
size_t offset = 0) const noexcept {
4122 size_t len = _len();
4124 const K *
str = _str(), *fnd = traits::find(
str + offset, len - offset, s);
4126 return static_cast<size_t>(fnd -
str);
4141 size_t len = _len();
4143 const K *
str = _str(), *fnd = traits::find(
str + offset, len - offset, s);
4145 return static_cast<size_t>(fnd -
str);
4150 static const K* find_first_by_case(
const K* from,
const K* last, K symbol, K symbolInOtherCase) {
4151 while (from < last) {
4153 if (f == symbol || f == symbolInOtherCase) {
4171 constexpr size_t find_ia(str_piece pattern,
size_t offset = 0) const noexcept {
4172 size_t lenPattern = pattern.length();
4173 size_t lenText = _len();
4176 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
4178 const K* patternStr = pattern.symbols();
4180 const K *text = _str(), *last = text + lenText - lenPattern, first = patternStr[0];
4183 bool firstHasCase = isAsciiLower(first);
4185 firstInOtherCase = first & ~0x20;
4186 }
else if (isAsciiUpper(first)) {
4187 firstHasCase =
true;
4188 firstInOtherCase = first | 0x20;
4191 auto equal = [&](
const K* from) {
4192 for (
size_t idx = 0; idx < lenPattern; idx++) {
4193 K s1 = from[idx], s2 = patternStr[idx];
4196 if (isAsciiUpper(s1)) {
4197 if (s1 == (s2 & ~0x20)) {
4200 }
else if (isAsciiLower(s1) && s1 == (s2 | 0x20)) {
4208 for (
const K* fnd = text + offset;;) {
4210 ? find_first_by_case(fnd, last, first, firstInOtherCase)
4211 : traits::find(fnd, last - fnd, first);
4215 return fnd - text - 1;
4232 constexpr size_t find_ia_lower(str_piece pattern_lower,
size_t offset = 0) const noexcept {
4233 size_t lenPattern = pattern_lower.length();
4234 size_t lenText = _len();
4237 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
4239 const K* patternStr = pattern_lower.symbols();
4241 const K *text = _str(), *last = text + lenText - lenPattern, first = patternStr[0];
4244 bool firstHasCase = isAsciiLower(first);
4246 firstInOtherCase = first & ~0x20;
4249 auto equal = [&](
const K* from) {
4250 for (
size_t idx = 0; idx < lenPattern; idx++) {
4251 K s1 = from[idx], s2 = patternStr[idx];
4252 if (!(s1 == s2 || (isAsciiUpper(s1) && (s1 | 0x20) == s2))) {
4259 for (
const K* fnd = text + offset;;) {
4261 ? find_first_by_case(fnd, last, first, firstInOtherCase)
4262 : traits::find(fnd, last - fnd, first);
4266 return fnd - text - 1;
4271 template<
typename Op>
4272 constexpr void for_all_founded(
const Op& op,
const K* pattern,
size_t patternLen,
size_t offset,
size_t maxCount)
const {
4275 while (maxCount-- > 0) {
4276 size_t fnd = find(pattern, patternLen, offset);
4277 if (fnd == str::npos)
4280 offset = fnd + patternLen;
4295 template<
typename Op>
4296 constexpr void for_all_founded(
const Op& op, str_piece pattern,
size_t offset = 0,
size_t maxCount = 0)
const {
4297 for_all_founded(op, pattern.symbols(), pattern.length(), offset, maxCount);
4300 template<
typename To = std::vector<
size_t>>
4301 constexpr To find_all(
const K* pattern,
size_t patternLen,
size_t offset,
size_t maxCount)
const {
4303 for_all_founded([&](
auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
4318 template<
typename To = std::vector<
size_t>>
4319 constexpr To
find_all(str_piece pattern,
size_t offset = 0,
size_t maxCount = 0)
const {
4320 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
4322 template<
typename To = std::vector<
size_t>>
4323 constexpr void find_all_to(To& to,
const K* pattern,
size_t len,
size_t offset = 0,
size_t maxCount = 0)
const {
4324 return for_all_founded([&](
size_t pos) {
4325 to.emplace_back(pos);
4326 }, pattern, len, offset, maxCount);
4338 constexpr size_t find_last(K s,
size_t offset = -1) const noexcept {
4339 size_t len = std::min(_len(), offset);
4340 const K *text = _str();
4342 if (text[--len] == s)
4357 constexpr size_t find_first_of(str_piece pattern,
size_t offset = 0) const noexcept {
4358 return std::basic_string_view<K>{_str(), _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4370 constexpr std::pair<size_t, size_t>
find_first_of_idx(str_piece pattern,
size_t offset = 0) const noexcept {
4371 const K* text = _str();
4372 size_t fnd = std::basic_string_view<K>{text, _len()}.find_first_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4373 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4386 return std::basic_string_view<K>{_str(), _len()}.find_first_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4398 constexpr size_t find_last_of(str_piece pattern,
size_t offset = str::npos)
const noexcept {
4399 return std::basic_string_view<K>{_str(), _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4411 constexpr std::pair<size_t, size_t>
find_last_of_idx(str_piece pattern,
size_t offset = str::npos)
const noexcept {
4412 const K* text = _str();
4413 size_t fnd = std::basic_string_view<K>{text, _len()}.find_last_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4414 return {fnd, fnd == std::basic_string<K>::npos ? fnd : pattern.find(text[fnd]) };
4426 constexpr size_t find_last_not_of(str_piece pattern,
size_t offset = str::npos)
const noexcept {
4427 return std::basic_string_view<K>{_str(), _len()}.find_last_not_of(std::basic_string_view<K>{pattern.str, pattern.len}, offset);
4439 [[deprecated(
"Use substring(start, end)")]]
4440 constexpr my_type
substr(ptrdiff_t from, ptrdiff_t len = 0)
const {
4441 return my_type{d()(from, len)};
4451 template<
typename T, first_index_in_str<K> S, last_index_in_str<K> E>
requires std::is_constructible_v<T, str_piece>
4453 str_piece res =
slice(start, end);
4454 if constexpr (std::is_constructible_v<T, const my_type&> && !std::is_same_v<T, str_piece>) {
4455 if (res.is_same(*
this)) {
4465 template<first_index_in_str<K> S, last_index_in_str<K> E>
4466 constexpr my_type
substring(S start, E end)
const noexcept {
4479 [[deprecated(
"Use slice_to(start, end)")]]
4480 constexpr my_type
str_mid(
size_t from,
size_t len = -1)
const {
4481 return my_type{d().mid(from, len)};
4511 template<ToIntNumber T,
bool CheckOverflow = true,
unsigned Base = 0,
bool SkipWs = true,
bool AllowSign = true>
4513 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4544 template<ToIntNumber T,
bool CheckOverflow = true,
unsigned Base = 0,
bool SkipWs = true,
bool AllowSign = true>
4545 constexpr convert_result<T>
to_int() const noexcept {
4546 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
4554 template<
bool SkipWS = true,
bool AllowPlus = true>
4555 requires(
sizeof(K) == 1 &&
4556 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4558 size_t len = _len();
4559 const K* ptr = _str();
4560 if constexpr (SkipWS) {
4561 while (len && uns_type(*ptr) <=
' ') {
4566 if constexpr (AllowPlus) {
4567 if (len && *ptr == K(
'+')) {
4575 if constexpr (
requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4577 if (std::from_chars((
const K*)ptr, (
const K*)ptr + len, d).ec == std::errc{}) {
4589 template<
bool SkipWS = true>
4590 requires(
sizeof(K) == 1 &&
4591 requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); })
4593 size_t len = _len();
4594 const K* ptr = _str();
4595 if constexpr (SkipWS) {
4596 while (len && uns_type(*ptr) <=
' ') {
4602 if constexpr (
requires { std::from_chars(std::declval<K*>(), std::declval<K*>(), std::declval<double&>()); }) {
4604 if (std::from_chars((
const K*)ptr, (
const K*)ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
4619 template<ToIntNumber T>
4624 template<
typename T,
typename Op>
4625 constexpr T splitf(
const K* delimiter,
size_t lendelimiter,
const Op& beforeFunc,
size_t offset)
const {
4626 size_t mylen = _len();
4627 std::conditional_t<std::is_same_v<T, void>, char, T> results;
4628 str_piece me{_str(), mylen};
4629 for (
size_t i = 0;; i++) {
4630 size_t beginOfDelim = find(delimiter, lendelimiter, offset);
4631 if (beginOfDelim == str::npos) {
4632 str_piece last{me.symbols() + offset, me.length() - offset};
4633 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4636 if constexpr (
requires { results.emplace_back(last); }) {
4637 if (last.is_same(me)) {
4640 results.emplace_back(d());
4642 results.emplace_back(last);
4644 }
else if constexpr (
requires { results.push_back(last); }) {
4645 if (last.is_same(me)) {
4648 results.push_back(d());
4650 results.push_back(last);
4652 }
else if constexpr (
requires {results[i] = last;} &&
requires{std::size(results);}) {
4653 if (i < std::size(results)) {
4654 if (last.is_same(me)) {
4664 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
4665 if constexpr (std::is_invocable_v<Op, str_piece&>) {
4668 if constexpr (
requires { results.emplace_back(piece); }) {
4669 results.emplace_back(piece);
4670 }
else if constexpr (
requires { results.push_back(piece); }) {
4671 results.push_back(piece);
4672 }
else if constexpr (
requires { results[i] = piece; } &&
requires{std::size(results);}) {
4673 if (i < std::size(results)) {
4675 if (i == results.size() - 1) {
4680 offset = beginOfDelim + lendelimiter;
4682 if constexpr (!std::is_same_v<T, void>) {
4716 template<
typename T,
typename Op>
4717 constexpr T
splitf(str_piece delimiter,
const Op& beforeFunc,
size_t offset = 0)
const {
4718 return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
4732 template<
typename T>
4733 constexpr T
split(str_piece delimiter,
size_t offset = 0)
const {
4734 return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
4739 constexpr bool starts_with(
const K* prefix,
size_t l)
const noexcept {
4740 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
4758 return _len() >
prefix.length() &&
4760 trim_operator<TrimSides::TrimLeft, K, size_t(-1),
true>{}.isTrim(_str()[
prefix.length()]);
4771 return _len() >
prefix.length() &&
4773 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[
prefix.length()]);
4775 template<typename T, size_t N = const_lit_for<K, T>::Count,
StrType<K> From>
requires is_const_pattern<N>
4776 constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol)
const noexcept {
4777 return _len() >= N &&
4778 starts_with(prefix) &&
4779 trim_operator<TrimSides::TrimLeft, K, N - 1,
false>{next_symbol}.isTrim(_str()[prefix.length()]);
4782 constexpr bool starts_with_ia(
const K* prefix,
size_t len)
const noexcept {
4783 size_t myLen = _len();
4787 const K* ptr1 = _str();
4789 K s1 = *ptr1++, s2 = *prefix++;
4792 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4804 return starts_with_ia(
prefix.symbols(),
prefix.length());
4813 return _len() >
prefix.length() &&
4814 starts_with_ia(
prefix) &&
4815 trim_operator<TrimSides::TrimLeft, K, size_t(-1),
true>{}.isTrim(_str()[
prefix.length()]);
4826 return _len() >
prefix.length() &&
4827 starts_with_ia(
prefix) &&
4828 trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[
prefix.length()]);
4830 template<typename T, size_t N = const_lit_for<K, T>::Count,
StrType<K> From>
requires is_const_pattern<N>
4831 constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol)
const noexcept {
4832 return _len() >= N &&
4833 starts_with_ia(prefix) &&
4834 trim_operator<TrimSides::TrimLeft, K, N - 1,
false>{next_symbol}.isTrim(_str()[prefix.length()]);
4839 constexpr bool prefix_in(
const K* text,
size_t len)
const noexcept {
4840 size_t myLen = _len();
4843 return !myLen || 0 == traits::compare(text, _str(), myLen);
4852 return prefix_in(text.symbols(), text.length());
4856 constexpr bool ends_with(
const K* suffix,
size_t len)
const noexcept {
4857 size_t myLen = _len();
4858 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
4871 constexpr bool ends_with_ia(
const K* suffix,
size_t len)
const noexcept {
4872 size_t myLen = _len();
4876 const K* ptr1 = _str() + myLen - len;
4878 K s1 = *ptr1++, s2 = *suffix++;
4881 if (makeAsciiLower(s1) != makeAsciiLower(s2))
4893 return ends_with_ia(
suffix.symbols(),
suffix.length());
4902 if (std::is_constant_evaluated()) {
4903 for (
size_t idx = 0; idx < _len(); idx++) {
4904 if (uns_type(_str()[idx]) > 127) {
4910 const int sl = ascii_mask<K>::WIDTH;
4911 const size_t mask = ascii_mask<K>::VALUE;
4912 size_t len = _len();
4913 const uns_type* ptr =
reinterpret_cast<const uns_type*
>(_str());
4914 if constexpr (sl > 1) {
4915 const size_t roundMask =
sizeof(size_t) - 1;
4916 while (len >= sl && (
reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
4922 if (*
reinterpret_cast<const size_t*
>(ptr) & mask)
4942 template<
typename R = my_type>
4944 return R::upperred_only_ascii_from(d());
4954 template<
typename R = my_type>
4956 return R::lowered_only_ascii_from(d());
4974 template<
typename R = my_type>
4975 R
replaced(str_piece pattern, str_piece repl,
size_t offset = 0,
size_t maxCount = 0)
const {
4976 return R::replaced_from(d(), pattern, repl, offset, maxCount);
4990 template<
typename R = str_piece>
4992 if (starts_with(
prefix)) {
5009 template<
typename R = str_piece>
5011 if (starts_with_ia(
prefix)) {
5028 template<
typename R = str_piece>
5047 template<
typename R = str_piece>
5049 if (ends_with_ia(
suffix)) {
5055 template<StrType<K> From>
5056 constexpr static my_type make_trim_op(
const From& from,
const auto& opTrim) {
5057 str_piece sfrom = from, newPos = opTrim(sfrom);
5058 if (newPos.is_same(sfrom)) {
5062 return my_type{newPos};
5064 template<TrimS
ides S, StrType<K> From>
5065 constexpr static my_type trim_static(
const From& from) {
5066 return make_trim_op(from, trim_operator<S, K,
static_cast<size_t>(-1),
true>{});
5069 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
5070 requires is_const_pattern<N>
5071 constexpr static my_type trim_static(
const From& from, T&& pattern) {
5072 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
5075 template<TrimS
ides S,
bool withSpaces, StrType<K> From>
5076 constexpr static my_type trim_static(
const From& from, str_piece pattern) {
5077 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
5087 template<
typename R = str_piece>
5089 return R::template trim_static<TrimSides::TrimAll>(d());
5099 template<
typename R = str_piece>
5101 return R::template trim_static<TrimSides::TrimLeft>(d());
5111 template<
typename R = str_piece>
5113 return R::template trim_static<TrimSides::TrimRight>(d());
5125 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5126 requires is_const_pattern<N>
5128 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
5140 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5141 requires is_const_pattern<N>
5143 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
5155 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5156 requires is_const_pattern<N>
5158 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
5177 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5178 requires is_const_pattern<N>
5180 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
5196 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5197 requires is_const_pattern<N>
5199 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5215 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
5216 requires is_const_pattern<N>
5218 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5233 template<
typename R = str_piece>
5235 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
5247 template<
typename R = str_piece>
5249 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
5261 template<
typename R = str_piece>
5263 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
5279 template<
typename R = str_piece>
5281 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
5297 template<
typename R = str_piece>
5299 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
5315 template<
typename R = str_piece>
5317 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
5331 template<
typename R = str_piece>
5333 str_piece res = *
this;
5334 while(res.starts_with(
prefix)) {
5335 res.remove_prefix(
prefix.length());
5336 if (--max_count == 0) {
5354 template<
typename R = str_piece>
5356 str_piece res = *
this;
5357 while(res.starts_with_ia(
prefix)) {
5358 res.remove_prefix(
prefix.length());
5359 if (--max_count == 0) {
5377 template<
typename R = str_piece>
5379 str_piece res = *
this;
5380 while(res.ends_with(
suffix)) {
5381 res.remove_suffix(
suffix.length());
5397 template<
typename R = str_piece>
5399 str_piece res = *
this;
5400 while(res.ends_with_ia(
suffix)) {
5401 res.remove_suffix(
suffix.length());
5420template<
size_t N>
requires (N > 1)
5421struct find_all_container {
5422 static constexpr size_t max_capacity = N;
5423 size_t positions_[N];
5426 constexpr void emplace_back(
size_t pos) {
5427 positions_[added_++] = pos;
5450struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
5451 using symb_type = K;
5452 using my_type = str_src<K>;
5454 const symb_type* str;
5457 str_src() =
default;
5462 template<typename T, size_t N = const_lit_for<K, T>::Count>
5463 constexpr str_src(T&& v) noexcept : str((
const K*)v), len(N - 1) {}
5469 constexpr str_src(
const K* p,
size_t l) noexcept : str(p), len(l) {}
5471 template<StrType<K> T>
5472 constexpr str_src(T&& t) :
str(t.symbols()), len(t.length()){}
5478 template<
typename A>
5479 constexpr str_src(
const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
5484 constexpr str_src(
const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
5497 constexpr const symb_type*
symbols() const noexcept {
5513 constexpr bool is_same(str_src<K> other)
const noexcept {
5514 return str == other.str && len == other.len;
5523 return str >= other.str && str + len <= other.str + other.len;
5586struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
5587 using symb_type = K;
5588 using my_type = str_src_nt<K>;
5589 using base = str_src<K>;
5591 constexpr static const K empty_string[1] = {0};
5593 str_src_nt() =
default;
5610 template<
typename T>
requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
5613 base::str = base::len ? p : empty_string;
5619 template<typename T, size_t N = const_lit_for<K, T>::Count>
5620 constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
5626 constexpr str_src_nt(
const K* p,
size_t l) noexcept : base(p, l) {}
5628 template<StrType<K> T>
5631 base::len = t.length();
5637 template<
typename A>
5638 constexpr str_src_nt(
const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
5640 static const my_type empty_str;
5650 if (from > base::len) {
5653 return {base::str + from, base::len - from};
5658inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
5659template<
typename K>
struct simple_str_selector;
5661inline namespace literals {
5721template<
char...Chars>
5722SS_CONSTEVAL
auto operator""_fmt() {
5723 return f::skip_0x<Chars...>();
5728#ifndef IN_FULL_SIMSTR
5731using simple_str = str_src<K>;
5734struct simple_str_selector {
5735 using type = simple_str<K>;
5739using simple_str_nt = str_src_nt<K>;
5742using Splitter = SplitterBase<K, str_src<K>>;
5744using ssa = str_src<u8s>;
5745using ssb = str_src<ubs>;
5746using ssw = str_src<wchar_t>;
5747using ssu = str_src<u16s>;
5748using ssuu = str_src<u32s>;
5749using stra = str_src_nt<u8s>;
5750using strb = str_src_nt<ubs>;
5751using strw = str_src_nt<wchar_t>;
5752using stru = str_src_nt<u16s>;
5753using struu = str_src_nt<u32s>;
5756consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
5757 if constexpr (std::is_same_v<K, u8s>)
5759 if constexpr (std::is_same_v<K, ubs>)
5761 if constexpr (std::is_same_v<K, uws>)
5763 if constexpr (std::is_same_v<K, u16s>)
5765 if constexpr (std::is_same_v<K, u32s>)
5769#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
5771inline namespace literals {
5845constexpr size_t slice_simple_find_func(str_src<K> str, str_src<K> pattern,
size_t offset){
return str.find(pattern, offset);}
5847constexpr size_t slice_simple_find_rev_func(str_src<K> str, str_src<K> pattern,
size_t offset){
return str.find_last(pattern, offset);}
5849template<
typename K,
bool ToEndOfPattern,
bool OffsetFromFound,
bool AllAtFail, auto F = slice_simple_find_func<K>>
5850struct slice_simple_find : slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, false, F> {
5851 using slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail,
false, F>::slice_find;
5855struct begin_or_all : slice_simple_find<K, false, true, true> {
5856 using base = slice_simple_find<K, false, true, true>;
5860struct end_or_all : slice_simple_find<K, true, true, true> {
5861 using base = slice_simple_find<K, true, true, true>;
5866struct begin_or_empty : slice_simple_find<K, false, true, false> {
5867 using base = slice_simple_find<K, false, true, false>;
5871struct end_or_empty : slice_simple_find<K, true, true, false> {
5872 using base = slice_simple_find<K, true, true, false>;
5876template<
typename K,
bool ToEndOfPattern,
bool OffsetFromFound,
bool AllAtFail, auto F = slice_simple_find_rev_func<K>>
5877struct slice_simple_find_rev : slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail, true, F> {
5878 using slice_find<str_src<K>, ToEndOfPattern, OffsetFromFound, AllAtFail,
true, F>::slice_find;
5882struct begin_last_or_all : slice_simple_find_rev<K, false, false, true> {
5883 using base = slice_simple_find_rev<K, false, false, true>;
5888struct end_last_or_all : slice_simple_find_rev<K, true, false, true> {
5889 using base = slice_simple_find_rev<K, true, false, true>;
5894struct begin_last_or_empty : slice_simple_find_rev<K, false, false, false> {
5895 using base = slice_simple_find_rev<K, false, false, false>;
5900struct end_last_or_empty : slice_simple_find_rev<K, true, false, false> {
5901 using base = slice_simple_find_rev<K, true, false, false>;
5905#define CTAD_SLICE_FIND(name) template<StrSource S> name(S&&) -> name<symb_type_from_src_t<S>>;\
5906 template<StrSource S> name(S&&, size_t) -> name<symb_type_from_src_t<S>>;\
5907 template<StrSource S> name(S&&, size_t, size_t) -> name<symb_type_from_src_t<S>>
5909CTAD_SLICE_FIND(begin_or_all);
5910CTAD_SLICE_FIND(end_or_all);
5911CTAD_SLICE_FIND(begin_or_empty);
5912CTAD_SLICE_FIND(end_or_empty);
5913CTAD_SLICE_FIND(begin_last_or_all);
5914CTAD_SLICE_FIND(end_last_or_all);
5915CTAD_SLICE_FIND(begin_last_or_empty);
5916CTAD_SLICE_FIND(end_last_or_empty);
5918inline namespace literals {
5920#define FIND_LITERAL(name, type, suffix) SS_CONSTEVAL name<type> operator ""_##suffix(const type* ptr, size_t l) {return str_src<type>{ptr, l};}
5921#define FIND_LITERALS(name, suffix) \
5922 FIND_LITERAL(name, u8s, suffix) \
5923 FIND_LITERAL(name, ubs, suffix) \
5924 FIND_LITERAL(name, u16s, suffix) \
5925 FIND_LITERAL(name, u32s, suffix) \
5926 FIND_LITERAL(name, wchar_t, suffix)
5928FIND_LITERALS(begin_or_all, boa)
5929FIND_LITERALS(end_or_all, eoa)
5930FIND_LITERALS(begin_or_empty, boe)
5931FIND_LITERALS(end_or_empty, eoe)
5932FIND_LITERALS(begin_last_or_all, bloa)
5933FIND_LITERALS(end_last_or_all, eloa)
5934FIND_LITERALS(begin_last_or_empty, bloe)
5935FIND_LITERALS(end_last_or_empty, eloe)
5939template<
typename K,
bool withSpaces>
5940struct CheckSpaceTrim {
5941 constexpr bool is_trim_spaces(K s)
const {
5942 return s ==
' ' || (s >= 9 && s <= 13);
5946struct CheckSpaceTrim<K, false> {
5947 constexpr bool is_trim_spaces(K)
const {
5953struct CheckSymbolsTrim {
5955 constexpr bool is_trim_symbols(K s)
const {
5956 return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) !=
nullptr;
5960template<
typename K,
size_t N>
5961struct CheckConstSymbolsTrim {
5962 const const_lit_to_array<K, N> symbols;
5964 template<typename T, size_t M = const_lit_for<K, T>::Count>
requires (M == N + 1)
5965 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
5967 constexpr bool is_trim_symbols(K s)
const noexcept {
5968 return symbols.contain(s);
5973struct CheckConstSymbolsTrim<K, 0> {
5974 constexpr bool is_trim_symbols(K)
const {
5979template<
typename K,
size_t N>
5980struct SymbSelector {
5981 using type = CheckConstSymbolsTrim<K, N>;
5985struct SymbSelector<K, 0> {
5986 using type = CheckSymbolsTrim<K>;
5990struct SymbSelector<K, static_cast<size_t>(-1)> {
5991 using type = CheckConstSymbolsTrim<K, 0>;
5994template<TrimS
ides S,
typename K,
size_t N,
bool withSpaces>
5995struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
5996 constexpr bool isTrim(K s)
const {
5997 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
5999 constexpr str_src<K> operator()(str_src<K> from)
const {
6000 if constexpr ((S & TrimSides::TrimLeft) != 0) {
6002 if (isTrim(*from.str)) {
6009 if constexpr ((S & TrimSides::TrimRight) != 0) {
6010 const K* back = from.str + from.len - 1;
6012 if (isTrim(*back)) {
6023template<TrimS
ides S,
typename K>
6024using SimpleTrim = trim_operator<S, K, size_t(-1),
true>;
6026template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
6027 requires is_const_pattern<N>
6028constexpr inline auto trimOp(T&& pattern) {
6029 return trim_operator<S, K, N - 1, withSpaces>{pattern};
6032template<TrimS
ides S = TrimS
ides::TrimAll,
bool withSpaces = false,
typename K>
6033constexpr inline auto trimOp(str_src<K> pattern) {
6034 return trim_operator<S, K, 0, withSpaces>{pattern};
6037static constexpr size_t FIND_CACHE_SIZE = 16;
6039template<
typename K,
size_t N,
size_t L>
6040struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
6041 using symb_type = K;
6042 using my_type = expr_replaces<K, N, L>;
6044 const K(&pattern)[N + 1];
6045 const K(&repl)[L + 1];
6046 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6047 mutable size_t last_;
6049 constexpr expr_replaces(str_src<K> w,
const K(&p)[N + 1],
const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
6051 constexpr size_t length()
const {
6052 size_t l = what.length();
6053 if constexpr (N == L) {
6056 what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
6057 if (matches_.added_) {
6058 last_ = matches_.positions_[matches_.added_ - 1] + N;
6059 l += int(L - N) * matches_.added_;
6061 if (matches_.added_ == FIND_CACHE_SIZE) {
6063 size_t next = what.find(pattern, N, last_);
6064 if (next == str::npos) {
6073 matches_.added_ = -1;
6077 constexpr K* place(K* ptr)
const noexcept {
6078 if constexpr (N == L) {
6079 const K* from = what.symbols();
6080 for (
size_t start = 0; start < what.length();) {
6081 size_t next = what.find(pattern, N, start);
6082 if (next == str::npos) {
6083 next = what.length();
6085 size_t delta = next - start;
6086 ch_traits<K>::copy(ptr, from + start, delta);
6088 ch_traits<K>::copy(ptr, repl, L);
6094 if (matches_.added_ == 0) {
6095 return what.place(ptr);
6096 }
else if (matches_.added_ ==
size_t(-1)) {
6100 const K* from = what.symbols();
6101 for (
size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
6102 ch_traits<K>::copy(ptr, from + start, offset - start);
6103 ptr += offset - start;
6104 ch_traits<K>::copy(ptr, repl, L);
6107 if (start >= last_) {
6108 size_t tail = what.length() - last_;
6109 ch_traits<K>::copy(ptr, from + last_, tail);
6113 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
6133template<StrSource A,
typename K = symb_type_from_src_t<A>,
typename T,
size_t N = const_lit_for<K, T>::Count,
typename X,
size_t L = const_lit_for<K, X>::Count>
6136 return expr_replaces<K, N - 1, L - 1>{std::forward<A>(w), p, r};
6148 using symb_type = K;
6153 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6154 mutable size_t last_;
6167 constexpr size_t length()
const {
6169 if (!plen || plen == rlen) {
6172 what.find_all_to(matches_, pattern.
symbols(), plen, 0, FIND_CACHE_SIZE);
6173 if (matches_.added_) {
6174 last_ = matches_.positions_[matches_.added_ - 1] + plen;
6175 l += int(rlen - plen) * matches_.added_;
6177 if (matches_.added_ == FIND_CACHE_SIZE) {
6179 size_t next = what.find(pattern.
symbols(), plen, last_);
6180 if (next == str::npos) {
6183 last_ = next + plen;
6189 matches_.added_ = -1;
6193 constexpr K* place(K* ptr)
const noexcept {
6196 const K* from = what.
symbols();
6197 for (
size_t start = 0; start < what.
length();) {
6198 size_t next = what.find(pattern, start);
6199 if (next == str::npos) {
6202 size_t delta = next - start;
6203 ch_traits<K>::copy(ptr, from + start, delta);
6205 ch_traits<K>::copy(ptr, repl.
symbols(), rlen);
6207 start = next + plen;
6211 if (matches_.added_ == 0) {
6212 return what.
place(ptr);
6213 }
else if (matches_.added_ == -1) {
6217 const K* from = what.
symbols();
6218 for (
size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
6219 ch_traits<K>::copy(ptr, from + start, offset - start);
6220 ptr += offset - start;
6221 ch_traits<K>::copy(ptr, repl.
symbols(), rlen);
6223 start = offset + plen;
6224 if (start >= last_) {
6225 size_t tail = what.
length() - start;
6226 ch_traits<K>::copy(ptr, from + start, tail);
6230 offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.
symbols(), plen, start);
6252template<
typename K, StrExprForType<K> E>
6254 using symb_type = K;
6258 mutable size_t replLen;
6259 mutable find_all_container<FIND_CACHE_SIZE> matches_;
6260 mutable size_t last_;
6274 constexpr size_t length()
const {
6279 matches_.positions_[0] = what.find(pattern);
6280 if (matches_.positions_[0] == -1) {
6284 matches_.added_ = 1;
6286 replLen = expr.length();
6287 if (replLen == plen) {
6291 what.find_all_to(matches_, pattern.
symbols(), plen, matches_.positions_[0] + plen, FIND_CACHE_SIZE - 1);
6293 last_ = matches_.positions_[matches_.added_ - 1] + plen;
6294 l += int(replLen - plen) * matches_.added_;
6296 if (matches_.added_ == FIND_CACHE_SIZE) {
6298 size_t next = what.find(pattern.
symbols(), plen, last_);
6299 if (next == str::npos) {
6302 last_ = next + plen;
6303 l += replLen - plen;
6307 matches_.added_ = -1;
6311 constexpr K* place(K* ptr)
const noexcept {
6312 if (matches_.added_ == 0) {
6314 return what.
place(ptr);
6315 }
else if (matches_.added_ == -1) {
6319 size_t plen = pattern.
length();
6320 const K* from = what.
symbols();
6321 ch_traits<K>::copy(ptr, from, matches_.positions_[0]);
6322 ptr += matches_.positions_[0];
6323 const K* repl = ptr;
6324 expr.
place((
typename E::symb_type*)ptr);
6326 size_t start = matches_.positions_[0] + plen;
6328 if (plen == replLen) {
6330 size_t next = what.find(pattern, start);
6331 if (next == str::npos) {
6334 size_t delta = next - start;
6335 ch_traits<K>::copy(ptr, from + start, delta);
6337 ch_traits<K>::copy(ptr, repl, replLen);
6339 start = next + plen;
6342 for (
size_t idx = 1;;) {
6343 if (start >= last_) {
6346 size_t next = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, start);
6347 size_t delta = next - start;
6348 ch_traits<K>::copy(ptr, from + start, delta);
6350 ch_traits<K>::copy(ptr, repl, replLen);
6352 start = next + plen;
6355 size_t tail = what.
length() - start;
6356 ch_traits<K>::copy(ptr, from + start, tail);
6374template<StrSource A,
typename K = symb_type_from_src_t<A>,
typename T,
typename X>
6375 requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, X> && (!is_const_lit_v<T> || !is_const_lit_v<X>))
6395template<StrSource A,
typename K = symb_type_from_src_t<A>,
typename T, StrExprForType<K> E>
6396 requires std::is_constructible_v<str_src<K>, T>
6397constexpr auto e_repl(A&& w, T&& p,
const E& expr) {
6402template<
bool UseVectorForReplace>
6403struct replace_search_result_store {
6405 std::pair<size_t, size_t> replaces_[16];
6409struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6413template<
typename K,
size_t N,
bool UseVectorForReplace>
6414struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
6415 using symb_type = K;
6416 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6417 const K pattern_[N];
6418 const str_src<K> source_;
6419 const str_src<K> replaces_[N];
6421 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6423 [[_no_unique_address]]
6424 std::array<uu8s, N >= BIT_SEARCH_TRESHHOLD ? (
sizeof(K) == 1 ? 32 : 64) : 0> bit_mask_{};
6426 template<
typename ... Repl>
requires (
sizeof...(Repl) == N * 2)
6427 constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6429 size_t length()
const {
6430 size_t l = source_.length();
6431 auto [fnd, num] = find_first_of(source_.str, source_.len);
6432 if (fnd == str::npos) {
6435 l += replaces_[num].len - 1;
6436 if constexpr (UseVectorForReplace) {
6437 search_results_.reserve((l >> 4) + 8);
6438 search_results_.emplace_back(fnd, num);
6439 for (
size_t start = fnd + 1;;) {
6440 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6441 if (fnd == str::npos) {
6444 search_results_.emplace_back(fnd, idx);
6446 l += replaces_[idx].len - 1;
6449 const size_t max_store = std::size(search_results_.replaces_);
6450 search_results_.replaces_[0] = {fnd, num};
6451 search_results_.count_++;
6452 for (
size_t start = fnd + 1;;) {
6453 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6454 if (found == str::npos) {
6457 if (search_results_.count_ < max_store) {
6458 search_results_.replaces_[search_results_.count_] = {found, idx};
6460 l += replaces_[idx].len - 1;
6461 search_results_.count_++;
6467 K* place(K* ptr)
const noexcept {
6469 const K* text = source_.str;
6470 if constexpr (UseVectorForReplace) {
6471 for (
const auto& [pos, num] : search_results_) {
6472 size_t delta = pos - start;
6473 ch_traits<K>::copy(ptr, text + start, delta);
6475 ptr = replaces_[num].place(ptr);
6479 const size_t max_store = std::size(search_results_.replaces_);
6480 size_t founded = search_results_.count_;
6481 for (
size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6482 const auto [pos, num] = search_results_.replaces_[idx];
6483 size_t delta = pos - start;
6484 ch_traits<K>::copy(ptr, text + start, delta);
6486 ptr = replaces_[num].place(ptr);
6489 if (founded > max_store) {
6490 founded -= max_store;
6492 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6493 size_t delta = fnd - start;
6494 ch_traits<K>::copy(ptr, text + start, delta);
6496 ptr = replaces_[idx].place(ptr);
6501 size_t tail = source_.len - start;
6502 ch_traits<K>::copy(ptr, text + start, tail);
6507 template<
typename ... Repl>
6508 constexpr expr_replace_const_symbols(
int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
6509 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6511 template<
typename ... Repl>
requires (
sizeof...(Repl) == N)
6512 constexpr expr_replace_const_symbols(
int, str_src<K> source, Repl&&... repl) :
6513 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6515 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6516 for (
size_t idx = 0; idx < N; idx++) {
6517 uu8s s =
static_cast<uu8s
>(pattern_[idx]);
6518 if constexpr (
sizeof(K) == 1) {
6519 bit_mask_[s >> 3] |= 1 << (s & 7);
6521 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6522 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6524 bit_mask_[s >> 3] |= 1 << (s & 7);
6531 template<
size_t Idx>
6532 size_t index_of(K s)
const {
6533 if constexpr (Idx < N) {
6534 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6538 bool is_in_mask(uu8s s)
const {
6539 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6541 bool is_in_mask2(uu8s s)
const {
6542 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6544 bool is_in_pattern(K s,
size_t& idx)
const {
6545 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6546 if constexpr (
sizeof(K) == 1) {
6547 if (is_in_mask(s)) {
6548 idx = index_of<0>(s);
6552 if (std::make_unsigned_t<const K>(s) > 255) {
6553 if (is_in_mask2(s)) {
6554 return (idx = index_of<0>(s)) != -1;
6557 if (is_in_mask(s)) {
6558 idx = index_of<0>(s);
6566 std::pair<size_t, size_t> find_first_of(
const K* text,
size_t len,
size_t offset = 0)
const {
6567 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6569 while (offset < len) {
6570 if (is_in_pattern(text[offset], idx)) {
6571 return {offset, idx};
6576 while (offset < len) {
6577 if (
size_t idx = index_of<0>(text[offset]); idx != -1) {
6578 return {offset, idx};
6626template<
bool UseVector = false, StrSource A,
typename K = symb_type_from_src_t<A>,
typename ... Repl>
6627 requires (
sizeof...(Repl) % 2 == 0)
6629 return expr_replace_const_symbols<K,
sizeof...(Repl) / 2, UseVector>(std::forward<A>(src), std::forward<Repl>(other)...);
6671template<
typename K,
bool UseVectorForReplace = false>
6673 using symb_type = K;
6674 using str_t =
typename simple_str_selector<K>::type;
6675 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6678 const std::vector<std::pair<K, str_t>>& replaces_;
6680 std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
6682 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6684 uu8s bit_mask_[
sizeof(K) == 1 ? 32 : 64]{};
6713 : source_(source), replaces_(repl)
6715 size_t pattern_len = replaces_.size();
6716 pattern_.resize(pattern_len);
6717 K* pattern = pattern_.data();
6719 for (
size_t idx = 0; idx < replaces_.size(); idx++) {
6720 *pattern++ = replaces_[idx].first;
6723 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6724 for (
size_t idx = 0; idx < pattern_len; idx++) {
6725 uu8s s =
static_cast<uu8s
>(pattern_[idx]);
6726 if constexpr (
sizeof(K) == 1) {
6727 bit_mask_[s >> 3] |= (1 << (s & 7));
6729 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6730 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6732 bit_mask_[s >> 3] |= (1 << (s & 7));
6739 size_t length()
const {
6740 size_t l = source_.
length();
6741 auto [fnd, num] = find_first_of(source_.str, source_.len);
6742 if (fnd == str::npos) {
6745 l += replaces_[num].second.len - 1;
6746 if constexpr (UseVectorForReplace) {
6747 search_results_.reserve((l >> 4) + 8);
6748 search_results_.emplace_back(fnd, num);
6749 for (
size_t start = fnd + 1;;) {
6750 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6751 if (fnd == str::npos) {
6754 search_results_.emplace_back(fnd, idx);
6756 l += replaces_[idx].second.len - 1;
6759 const size_t max_store = std::size(search_results_.replaces_);
6760 search_results_.replaces_[0] = {fnd, num};
6761 search_results_.count_++;
6762 for (
size_t start = fnd + 1;;) {
6763 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6764 if (found == str::npos) {
6767 if (search_results_.count_ < max_store) {
6768 search_results_.replaces_[search_results_.count_] = {found, idx};
6770 l += replaces_[idx].second.len - 1;
6771 search_results_.count_++;
6777 K* place(K* ptr)
const noexcept {
6779 const K* text = source_.str;
6780 if constexpr (UseVectorForReplace) {
6781 for (
const auto& [pos, num] : search_results_) {
6782 size_t delta = pos - start;
6783 ch_traits<K>::copy(ptr, text + start, delta);
6785 ptr = replaces_[num].second.place(ptr);
6789 const size_t max_store = std::size(search_results_.replaces_);
6790 size_t founded = search_results_.count_;
6791 for (
size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6792 const auto [pos, num] = search_results_.replaces_[idx];
6793 size_t delta = pos - start;
6794 ch_traits<K>::copy(ptr, text + start, delta);
6796 ptr = replaces_[num].second.place(ptr);
6799 if (founded > max_store) {
6800 founded -= max_store;
6802 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6803 size_t delta = fnd - start;
6804 ch_traits<K>::copy(ptr, text + start, delta);
6806 ptr = replaces_[idx].second.place(ptr);
6811 size_t tail = source_.len - start;
6812 ch_traits<K>::copy(ptr, text + start, tail);
6817 size_t index_of(K s)
const {
6818 return pattern_.find(s);
6821 bool is_in_mask(uu8s s)
const {
6822 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6824 bool is_in_mask2(uu8s s)
const {
6825 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6828 bool is_in_pattern(K s,
size_t& idx)
const {
6829 if constexpr (
sizeof(K) == 1) {
6830 if (is_in_mask(s)) {
6835 if (std::make_unsigned_t<const K>(s) > 255) {
6836 if (is_in_mask2(s)) {
6837 return (idx = index_of(s)) != -1;
6840 if (is_in_mask(s)) {
6849 std::pair<size_t, size_t> find_first_of(
const K* text,
size_t len,
size_t offset = 0)
const {
6850 size_t pl = pattern_.length();
6851 if (pl >= BIT_SEARCH_TRESHHOLD) {
6853 while (offset < len) {
6854 if (is_in_pattern(text[offset], idx)) {
6855 return {offset, idx};
6860 while (offset < len) {
6861 if (
size_t idx = index_of(text[offset]); idx != -1) {
6862 return {offset, idx};
6871template<
typename K, StrExprForType<K> T>
6874 force_copy(
const T& t) : t_(t){}
6877template<
typename K,
typename T>
6878struct symb_type_from_src<force_copy<K, T>> {
6884force_copy(T&&) -> force_copy<typename T::symb_type, T>;
6886template<
typename K,
typename T>
6887constexpr auto to_subst(T&& t) {
6888 return to_strexpr<K>(std::forward<T>(t));
6891template<
typename K,
typename T,
size_t N>
6892constexpr auto to_subst(
const T(&t)[N]) {
6893 return expr_literal<T, N - 1>{t};
6896template<
typename K, StrExprForType<K> T>
6897constexpr decltype(
auto) to_subst(T&& t) {
6898 return std::forward<T>(t);
6901template<
typename K,
typename T>
6902constexpr T to_subst(
const force_copy<K, T>& t) {
6906template<
typename K,
typename T>
6907constexpr T to_subst(force_copy<K, T>&& t) {
6911template<
typename K,
typename T>
6912constexpr T to_subst(force_copy<K, T>& t) {
6916template<
typename K,
typename T>
6917using to_str_exp_t =
decltype(to_subst<K>(std::declval<T>()));
6926template<
typename K,
typename G,
typename Arg,
typename...Args>
6928 using symb_type = K;
6929 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
6930 using arg_t = to_str_exp_t<K, Arg>;
6931 to_str_exp_t<K, G> glue_;
6934 mutable size_t glue_len_;
6968 : glue_(to_subst<K>(
std::forward<G>(glue)))
6969 , arg_(to_subst<K>(
std::forward<Arg>(arg)))
6970 , args_(to_subst<K>(
std::forward<Args>(args))...) {}
6972 constexpr size_t length() const noexcept {
6973 return [
this]<
size_t...Indexes>(std::index_sequence<Indexes...>) {
6974 glue_len_ = glue_.length();
6975 size_t l = arg_.length() + glue_len_ *
sizeof...(Args);
6976 ((l += std::get<Indexes>(args_).length()),...);
6978 }(std::make_index_sequence<
sizeof...(Args)>());
6980 constexpr K* place(K* ptr)
const noexcept {
6981 return [
this]<
size_t...Indexes>(K* ptr, std::index_sequence<Indexes...>) {
6982 ptr = (K*)arg_.place((
typename std::remove_cvref_t<arg_t>::symb_type*)ptr);
6983 const K* glueStart = ptr;
6984 ptr = glue_.place(ptr);
6987 ptr = (K*)std::get<Indexes>(args_).place((
typename std::remove_cvref_t<std::tuple_element_t<Indexes, store_t>>::symb_type*)ptr),
6988 glue_len_ > 0 && Indexes <
sizeof...(Args) - 1 ? (ch_traits<K>::copy(ptr, glueStart, glue_len_), ptr += glue_len_) :
nullptr
6992 }(ptr, std::make_index_sequence<
sizeof...(Args)>());
6996template<
typename T,
typename ... Args>
requires (
sizeof...(Args) > 1)
6997e_concat(T&&, Args&&...) -> e_concat<symb_type_from_src_t<T>, T, Args...>;
6999struct parse_subst_string_error {
7000 parse_subst_string_error(
const char*){}
7005template<
typename K,
size_t NParams>
7006constexpr size_t parse_pattern_string(str_src<K> pattern,
auto&& add_part,
auto&& add_param) {
7007 char used_args[NParams] = {0};
7008 const K* first = pattern.begin(), *last = pattern.end(), *start = first;
7011 auto find = [](
const K* from,
const K* last, K s) {
7012 while (from != last) {
7020 size_t idx_in_params = 0;
7022 while (first != last) {
7023 const K* open_pos = first;
7024 if (*first !=
'{') {
7025 open_pos = find(first, last,
'{');
7028 const K* close_pos = find(first, open_pos,
'}');
7029 if (close_pos == open_pos) {
7030 unsigned len = open_pos - first;
7031 add_part(first - start, len);
7036 if (close_pos == open_pos || *close_pos !=
'}') {
7037 throw parse_subst_string_error{
"unescaped }"};
7039 unsigned len = close_pos - first;
7040 add_part(first - start, len);
7042 first = ++close_pos;
7044 if (open_pos == last) {
7048 if (++open_pos == last) {
7049 throw parse_subst_string_error{
"unescaped {"};
7051 if (*open_pos ==
'}') {
7052 if (idx_in_params == -1) {
7053 throw parse_subst_string_error{
"already used param ids"};
7055 if (idx_in_params == NParams) {
7056 throw parse_subst_string_error{
"too many params"};
7058 used_args[idx_in_params] = 1;
7059 add_param(idx_in_params++);
7060 first = open_pos + 1;
7061 }
else if (*open_pos ==
'{') {
7062 add_part(open_pos - start, 1);
7064 first = open_pos + 1;
7066 if (idx_in_params != 0 && idx_in_params != -1) {
7067 throw parse_subst_string_error{
"already used non id params"};
7070 const K* end = find(open_pos, last,
'}');
7072 throw parse_subst_string_error{
"not found }"};
7074 auto [p, err, _] = str_src<K>(open_pos, end - open_pos).template to_int<unsigned, true, 10, false, false>();
7075 if (err != IntConvertResult::Success || p < 1 || p > NParams) {
7076 throw parse_subst_string_error{
"bad param id"};
7083 for (
auto c : used_args) {
7085 throw parse_subst_string_error{
"unused param"};
7094 unsigned is_param: 1;
7096 portion() =
default;
7098 constexpr void set_param(
unsigned param) {
7099 if (param >= (1 << 16)) {
7100 throw parse_subst_string_error{
"the parameter id is too large"};
7105 constexpr void set_part(
unsigned from,
unsigned l) {
7106 if (from >= (1 << 16) || l >= (1 << 15)) {
7107 throw parse_subst_string_error{
"the string part is too large"};
7115template<
typename K,
size_t PtLen,
size_t NParams>
7116struct subst_params {
7117 const K(&source_)[PtLen];
7118 unsigned all_len_{};
7122 portion portions_[1 + ((PtLen - 2) * 2 / 3)]{};
7124 consteval subst_params(
const K(&pattern)[PtLen]) : source_(pattern) {
7125 all_len_ = parse_pattern_string<K, NParams>(pattern,
7126 [
this](
unsigned from,
unsigned len) {
7127 portions_[actual_++].set_part(from, len);
7128 }, [
this](
unsigned param) {
7129 portions_[actual_++].set_param(param);
7141template<
typename K,
size_t PtLen,
typename ... Args>
7143 inline static constexpr size_t NParams =
sizeof...(Args);
7144 using symb_type = K;
7145 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
7147 const details::subst_params<K, PtLen, NParams>& subst_;
7189 constexpr e_subst(
const details::subst_params<K, PtLen, NParams>& subst, Args&&...args)
7191 , args_(to_subst<K>(
std::forward<Args>(args))...){}
7193 constexpr size_t length() const noexcept {
7194 return [
this]<
size_t...Indexes>(std::index_sequence<Indexes...>) {
7196 size_t expr_length_[NParams] = {};
7197 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
7198 size_t l = subst_.all_len_;
7199 for (idx = 0; idx < subst_.actual_; idx++) {
7200 if (subst_.portions_[idx].is_param) {
7201 l += expr_length_[subst_.portions_[idx].start];
7205 }(std::make_index_sequence<
sizeof...(Args)>());
7207 template<
size_t Idx>
7208 constexpr K* place_idx(K* ptr,
size_t idx)
const noexcept {
7210 return (K*)std::get<Idx>(args_).place((
typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
7212 if constexpr (Idx < NParams - 1) {
7213 return place_idx<Idx + 1>(ptr, idx);
7217 constexpr K* place(K* ptr)
const noexcept {
7218 for (
size_t idx = 0; idx < subst_.actual_; idx++) {
7219 if (subst_.portions_[idx].is_param) {
7220 ptr = place_idx<0>(ptr, subst_.portions_[idx].start);
7222 ch_traits<K>::copy(ptr, subst_.source_ + subst_.portions_[idx].start, subst_.portions_[idx].len);
7223 ptr += subst_.portions_[idx].len;
7231template<
typename K,
size_t N,
typename...Args>
requires (
sizeof...(Args) > 0)
7232e_subst(
const K(&)[N], Args&&...) -> e_subst<K, N, Args...>;
7239template<
typename K,
typename ... Args>
7241 inline static constexpr size_t Nparams =
sizeof...(Args);
7242 using symb_type = K;
7243 using store_t = std::tuple<to_str_exp_t<K, Args>...>;
7245 details::portion portions_[Nparams * 3];
7246 std::vector<details::portion> more_portions_;
7290 , args_(to_subst<K>(
std::forward<Args>(args))...) {
7292 all_len_ = details::parse_pattern_string<K, Nparams>(pattern_, [
this](
unsigned from,
unsigned len) {
7293 if (actual_ < std::size(portions_)) {
7294 portions_[actual_++].set_part(from, len);
7296 if (actual_ == std::size(portions_)) {
7297 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
7299 more_portions_.emplace_back().set_part(from, len);
7301 }, [
this](
unsigned param) {
7302 if (actual_ < std::size(portions_)) {
7303 portions_[actual_++].set_param(param);
7305 if (actual_ == std::size(portions_)) {
7306 more_portions_.reserve((pattern_.len - 1) * 2 / 3 - std::size(portions_));
7308 more_portions_.emplace_back().set_param(param);
7313 constexpr size_t length() const noexcept {
7314 return [
this]<
size_t...Indexes>(std::index_sequence<Indexes...>) {
7316 size_t expr_length_[Nparams] = {};
7317 ((expr_length_[idx++] = std::get<Indexes>(args_).length()),...);
7318 size_t l = all_len_;
7319 for (idx = 0; idx < actual_; idx++) {
7320 if (portions_[idx].is_param) {
7321 l += expr_length_[portions_[idx].start];
7324 for (
const auto& p : more_portions_) {
7326 l += expr_length_[p.start];
7330 }(std::make_index_sequence<
sizeof...(Args)>());
7332 template<
size_t Idx>
7333 constexpr K* place_idx(K* ptr,
size_t idx)
const noexcept {
7335 return (K*)std::get<Idx>(args_).place((
typename std::remove_cvref_t<std::tuple_element_t<Idx, store_t>>::symb_type*)ptr);
7337 if constexpr (Idx < Nparams - 1) {
7338 return place_idx<Idx + 1>(ptr, idx);
7342 constexpr K* place(K* ptr)
const noexcept {
7343 for (
size_t idx = 0; idx < actual_; idx++) {
7344 if (portions_[idx].is_param) {
7345 ptr = place_idx<0>(ptr, portions_[idx].start);
7347 ch_traits<K>::copy(ptr, pattern_.
symbols() + portions_[idx].start, portions_[idx].len);
7348 ptr += portions_[idx].len;
7351 for (
const auto& p : more_portions_) {
7353 ptr = place_idx<0>(ptr, p.start);
7355 const K* from = pattern_.
symbols() + p.start;
7356 for (
size_t idx = p.len; idx--;) {
7367template<StrSourceNoLiteral T,
typename...Args>
requires (
sizeof...(Args) > 0)
7368e_vsubst(T&&, Args&&...) -> e_vsubst<symb_type_from_src_t<T>, Args...>;
7370template<is_one_of_
char_v K,
bool upper>
7371struct expr_change_case_ascii : expr_to_std_string<expr_change_case_ascii<K, upper>>{
7372 using symb_type = K;
7376 template<StrSource S>
7377 expr_change_case_ascii(S&& s) : src_(std::forward<S>(s)){}
7379 constexpr size_t length() const noexcept {
7380 return src_.length();
7382 constexpr K* place(K* ptr)
const noexcept {
7383 const K* read = src_.str;
7384 for (
size_t l = src_.len; l--;) {
7385 if constexpr (upper) {
7386 *ptr++ = makeAsciiUpper(*read++);
7388 *ptr++ = makeAsciiLower(*read++);
7409template<is_one_of_
char_v K>
7411 using base = expr_change_case_ascii<K, true>;
7415template<StrSource S>
7432template<is_one_of_
char_v K>
7434 using base = expr_change_case_ascii<K, false>;
7438template<StrSource S>
7479template<
typename K,
typename A, StrExprForType<K> E>
7480std::basic_string<K, std::char_traits<K>, A>&
change(std::basic_string<K, std::char_traits<K>, A>&
str,
size_t from,
size_t count,
const E& expr) {
7481 size_t expr_length = expr.length();
7483 str.erase(from, count);
7486 size_t str_length =
str.length();
7487 if (from > str_length) {
7490 if (count > str_length || from + count > str_length) {
7491 count = str_length - from;
7493 size_t new_length = str_length - count + expr_length;
7494 size_t tail_length = str_length - count - from;
7496 if (new_length <= str_length) {
7497 K* data =
str.data();
7498 expr.place((
typename E::symb_type*)data + from);
7499 if (expr_length < count) {
7501 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7503 str.resize(new_length);
7506 auto fill = [&](K* data, size_t) ->
size_t {
7508 std::char_traits<K>::move(data + from + expr_length, data + from + count, tail_length);
7510 expr.place((
typename E::symb_type*)data + from);
7513 if constexpr (
requires{
str.resize_and_overwrite(new_length, fill);}) {
7514 str.resize_and_overwrite(new_length, fill);
7515 }
else if constexpr (
requires{
str._Resize_and_overwrite(new_length, fill);}) {
7516 str._Resize_and_overwrite(new_length, fill);
7518 str.resize(new_length);
7519 fill(
str.data(), 0);
7552template<
typename K,
typename A, StrExprForType<K> E>
7553std::basic_string<K, std::char_traits<K>, A>&
append(std::basic_string<K, std::char_traits<K>, A>&
str,
const E& expr) {
7585template<
typename K,
typename A, StrExprForType<K> E>
7586std::basic_string<K, std::char_traits<K>, A>&
prepend(std::basic_string<K, std::char_traits<K>, A>&
str,
const E& expr) {
7620template<
typename K,
typename A, StrExprForType<K> E>
7621std::basic_string<K, std::char_traits<K>, A>&
insert(std::basic_string<K, std::char_traits<K>, A>&
str,
size_t from,
const E& expr) {
7649template<
typename K,
typename A, StrExprForType<K> E>
7650std::basic_string<K, std::char_traits<K>, A>&
overwrite(std::basic_string<K, std::char_traits<K>, A>&
str,
const E& expr) {
7651 if (
size_t expr_length = expr.length()) {
7652 if (expr_length <=
str.length()) {
7653 K* data =
str.data();
7654 expr.place((
typename E::symb_type*)data);
7655 str.resize(expr_length);
7657 auto fill = [&](K* data, size_t) ->
size_t {
7658 expr.place((
typename E::symb_type*)data);
7661 if constexpr (
requires{
str.resize_and_overwrite(expr_length, fill);}) {
7662 str.resize_and_overwrite(expr_length, fill);
7663 }
else if constexpr (
requires{
str._Resize_and_overwrite(expr_length, fill);}) {
7664 str._Resize_and_overwrite(expr_length, fill);
7666 str.resize(expr_length);
7667 expr.place((
typename E::symb_type*)
str.data());
7678template<
typename K,
typename A,
typename E>
7679struct replace_grow_helper {
7680 using my_type = std::basic_string<K, std::char_traits<K>, A>;
7682 replace_grow_helper(my_type& src,
str_src<K> p,
const K* r,
size_t rl,
size_t mc,
size_t d,
const E& e)
7683 :
str(src), source(src), pattern(p), repl(const_cast<K*>(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {}
7686 const str_src<K> source;
7687 const str_src<K> pattern;
7689 const size_t replLen;
7696 K* reserve_for_copy{};
7697 size_t end_of_piece{};
7698 size_t total_length{};
7700 std::optional<my_type> dst;
7703 size_t found[16] = {offset};
7706 offset += pattern.len;
7709 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
7710 found[idx] = source.find(pattern, offset);
7711 if (found[idx] == npos) {
7714 offset = found[idx] + pattern.len;
7717 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
7722 if (!reserve_for_copy) {
7725 end_of_piece = source.length();
7726 total_length = end_of_piece + all_delta;
7728 if (total_length <= str.capacity()) {
7733 dst_str = &dst.emplace();
7735 auto fill = [
this](K* p, size_t) ->
size_t {
7736 reserve_for_copy = p;
7737 return total_length;
7739 if constexpr (
requires{dst_str->_Resize_and_overwrite(total_length, fill);}) {
7740 dst_str->_Resize_and_overwrite(total_length, fill);
7741 }
else if constexpr (
requires{dst_str->resize_and_overwrite(total_length, fill);}) {
7742 dst_str->resize_and_overwrite(total_length, fill);
7744 dst_str->resize(total_length);
7745 reserve_for_copy = dst_str->data();
7748 const K* src_start = str.c_str();
7750 size_t pos = found[idx] + pattern.len;
7751 size_t lenOfPiece = end_of_piece - pos;
7752 ch_traits<K>::move(reserve_for_copy + pos + all_delta, src_start + pos, lenOfPiece);
7753 if constexpr (std::is_same_v<E, int>) {
7754 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7757 repl = reserve_for_copy + pos + all_delta - replLen;
7760 ch_traits<K>::copy(reserve_for_copy + pos + all_delta - replLen, repl, replLen);
7764 end_of_piece = found[idx];
7766 if (!all_delta && reserve_for_copy != src_start) {
7767 ch_traits<K>::copy(reserve_for_copy, src_start, found[0]);
7768 str = std::move(*dst);
7809template<
typename K,
typename A, StrExprForType<K> E,
typename T>
7810requires (std::is_constructible_v<str_src<K>, T>)
7811std::basic_string<K, std::char_traits<K>, A>&
replace(std::basic_string<K, std::char_traits<K>, A>&
str, T&& pattern,
const E& repl,
size_t offset = 0,
size_t max_count = -1) {
7816 str_src<K> spattern{std::forward<T>(pattern)};
7817 offset = src.find(pattern, offset);
7818 if (offset == npos) {
7821 size_t replLen = repl.length();
7823 if (spattern.len == replLen) {
7826 K* ptr =
str.data();
7827 replStart = ptr + offset;
7828 repl.place(replStart);
7830 while (--max_count) {
7831 offset = src.find(spattern, offset + replLen);
7834 ch_traits<K>::copy(ptr + offset, replStart, replLen);
7836 }
else if (spattern.len > replLen) {
7839 K* ptr =
str.data();
7840 replStart = ptr + offset;
7841 repl.place(replStart);
7842 size_t posWrite = offset + replLen;
7843 offset += spattern.len;
7845 while (--max_count) {
7846 size_t idx = src.find(spattern, offset);
7849 size_t lenOfPiece = idx - offset;
7850 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7851 posWrite += lenOfPiece;
7852 ch_traits<K>::copy(ptr + posWrite, replStart, replLen);
7853 posWrite += replLen;
7854 offset = idx + spattern.len;
7856 size_t tailLen = src.len - offset;
7857 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7858 str.resize(posWrite + tailLen);
7860 details::replace_grow_helper<K, A, E>(
str, spattern,
nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset);
7885template<
typename K,
typename A>
7886std::basic_string<K, std::char_traits<K>, A>&
replace(std::basic_string<K, std::char_traits<K>, A>&
str,
str_src<K> pattern,
str_src<K> repl,
size_t offset = 0,
size_t max_count = -1) {
7891 offset = src.find(pattern, offset);
7892 if (offset == npos) {
7895 if (pattern.len == repl.len) {
7898 K* ptr =
str.data();
7899 while (max_count--) {
7900 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7901 offset = src.find(pattern, offset + repl.len);
7905 }
else if (pattern.len > repl.len) {
7908 K* ptr =
str.data();
7909 ch_traits<K>::copy(ptr + offset, repl.str, repl.len);
7910 size_t posWrite = offset + repl.len;
7911 offset += pattern.len;
7913 while (--max_count) {
7914 size_t idx = src.find(pattern, offset);
7917 size_t lenOfPiece = idx - offset;
7918 ch_traits<K>::move(ptr + posWrite, ptr + offset, lenOfPiece);
7919 posWrite += lenOfPiece;
7920 ch_traits<K>::copy(ptr + posWrite, repl.str, repl.len);
7921 posWrite += repl.len;
7922 offset = idx + pattern.len;
7924 size_t tailLen = src.len - offset;
7925 ch_traits<K>::move(ptr + posWrite, ptr + offset, tailLen);
7926 str.resize(posWrite + tailLen);
7928 details::replace_grow_helper<K, A, int>(
str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset);
7933template<
typename K,
typename A,
typename T,
typename M>
7934requires (std::is_constructible_v<str_src<K>, T> && std::is_constructible_v<str_src<K>, M>)
7935std::basic_string<K, std::char_traits<K>, A>& replace(std::basic_string<K, std::char_traits<K>, A>&
str, T&& pattern, M&& repl,
size_t offset = 0,
size_t max_count = -1) {
7936 return replace(
str,
str_src<K>{std::forward<T>(pattern)}, str_src<K>{std::forward<M>(repl)}, offset, max_count);
7951template<
typename K,
typename A>
7952std::basic_string<K, std::char_traits<K>, A>&
make_ascii_upper(std::basic_string<K, std::char_traits<K>, A>&
str,
size_t from = 0,
size_t to = -1) {
7953 for (K *ptr =
str.data() + std::min(from,
str.length()), *end =
str.data() + std::min(to,
str.length()); ptr < end; ptr++) {
7955 if (isAsciiLower(s)) {
7974template<
typename K,
typename A>
7975std::basic_string<K, std::char_traits<K>, A>&
make_ascii_lower(std::basic_string<K, std::char_traits<K>, A>&
str,
size_t from = 0,
size_t to = -1) {
7976 for (K *ptr =
str.data() + std::min(from,
str.length()), *end =
str.data() + std::min(to,
str.length()); ptr < end; ptr++) {
7978 if (isAsciiUpper(s)) {
8013template<simstr::StdStrSource T>
8045template<
typename K,
typename A, simstr::StrExprForType<K> E>
8046std::basic_string<K, std::char_traits<K>, A>& operator |=(std::basic_string<K, std::char_traits<K>, A>& str,
const E& expr) {
8077template<
typename K,
typename A, simstr::StrExprForType<K> E>
8078std::basic_string<K, std::char_traits<K>, A>& operator ^=(std::basic_string<K, std::char_traits<K>, A>& str,
const E& expr) {
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3230
constexpr bool is_done() const
Find out if substrings are running out.
Definition strexpr.h:3241
constexpr str_t next()
Get the next substring.
Definition strexpr.h:3250
constexpr R trimmed_right_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:5316
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Find the last occurrence of a character in this string.
Definition strexpr.h:4338
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition strexpr.h:3882
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition strexpr.h:3767
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition strexpr.h:4121
constexpr std::optional< R > strip_suffix(str_piece suffix) const
Get a string without a suffix if it ends with one.
Definition strexpr.h:5029
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition strexpr.h:4866
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition strexpr.h:3818
constexpr std::basic_string_view< D > to_sv() const noexcept
Convert to std::basic_string_view.
Definition strexpr.h:3421
constexpr T slice_to(S start, E end) const noexcept
Get the slice result and place it into an object of the specified type.
Definition strexpr.h:4452
constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by a whitespace...
Definition strexpr.h:4812
constexpr R trimmed_right() const
Get a string with whitespace removed on the right.
Definition strexpr.h:5112
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix,...
Definition strexpr.h:5355
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the end of the first occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:4017
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition strexpr.h:4748
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition strexpr.h:3807
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition strexpr.h:3399
constexpr str_piece until(str_piece pattern, size_t offset=0) const noexcept
Get the substring str_src from the beginning to the first found occurrence of the specified substring...
Definition strexpr.h:3729
constexpr R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition strexpr.h:5234
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4512
constexpr my_type substring(S start, E end) const noexcept
Wrapper around slice_to<my_type>
Definition strexpr.h:4466
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition strexpr.h:3922
constexpr T split(str_piece delimiter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition strexpr.h:4733
constexpr R trimmed_right_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:5217
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition strexpr.h:3693
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Get a substring. Works similarly to operator(), only the result is the same type as the method applie...
Definition strexpr.h:4440
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition strexpr.h:4943
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3469
constexpr bool starts_with_and_ws(str_piece prefix) const noexcept
Check if a string begins with the specified substring followed by a whitespace ASCII character.
Definition strexpr.h:4757
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Get the substring str_src from position from to position to (not including it).
Definition strexpr.h:3715
constexpr R trimmed_left_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:5298
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Get a copy of the string with occurrences of substrings replaced.
Definition strexpr.h:4975
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition strexpr.h:3778
constexpr size_t find_ia(str_piece pattern, size_t offset=0) const noexcept
Find the start of the first occurrence of a substring within this string, ignoring ASCII case.
Definition strexpr.h:4171
constexpr R trimmed_left() const
Get a string with whitespace removed on the left.
Definition strexpr.h:5100
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Whether the string ends with the specified substring in a case-insensitive ASCII character.
Definition strexpr.h:4892
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character not from the given character set.
Definition strexpr.h:4385
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:4357
constexpr R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition strexpr.h:5262
constexpr R trimmed_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:5179
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string or the end of the string.
Definition strexpr.h:4080
constexpr R trimmed_prefix(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:5332
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Whether the string begins with the given substring in a case-insensitive ASCII character.
Definition strexpr.h:4803
constexpr str_piece slice(S start, E end) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3574
constexpr R trimmed_left_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition strexpr.h:5198
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string.
Definition strexpr.h:4053
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:4398
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Find a character in this string or the end of a string.
Definition strexpr.h:4140
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character not from the given character set.
Definition strexpr.h:4426
std::optional< double > to_double() const noexcept
Convert string to double.
Definition strexpr.h:4557
constexpr size_t find_ia_lower(str_piece pattern_lower, size_t offset=0) const noexcept
Find the start of the first occurrence of a substring within this string, ignoring ASCII case,...
Definition strexpr.h:4232
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Find the end of the occurrence of a substring in this string.
Definition strexpr.h:3989
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition strexpr.h:4592
constexpr std::optional< R > strip_prefix(str_piece prefix) const
Get a string without a prefix if it starts with one.
Definition strexpr.h:4991
constexpr bool operator!() const noexcept
Check for emptiness.
Definition strexpr.h:3736
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition strexpr.h:5088
constexpr size_t size() const
The size of the string in characters.
Definition strexpr.h:3410
constexpr To find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Find all occurrences of a substring in this string.
Definition strexpr.h:4319
constexpr str_piece prefix(E count) const noexcept
Wraps slice(0, count). It also allows you to set the boundary using a search method.
Definition strexpr.h:3659
constexpr my_type str_mid(size_t from, size_t len=-1) const
Get part of a string with an object of the same type to which the method is applied,...
Definition strexpr.h:4480
constexpr str_piece tail(E count) const noexcept
Wraps slice(count, to_end). It also allows you to set the boundary using a search method.
Definition strexpr.h:3667
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string.
Definition strexpr.h:4066
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition strexpr.h:4370
constexpr str_piece suffix(E count) const noexcept
Wraps slice(from_end(count), to_end). It also allows you to set the boundary using a search method.
Definition strexpr.h:3675
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition strexpr.h:4899
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string.
Definition strexpr.h:3953
constexpr SplitterBase< K, str_piece > splitter(str_piece delimiter) const
Retrieve a Splitter object by the given splitter, which allows sequential get substrings using the ne...
Definition strexpr.h:5415
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition strexpr.h:4411
constexpr R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition strexpr.h:5142
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string or the end of the string.
Definition strexpr.h:4003
constexpr R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition strexpr.h:5248
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition strexpr.h:3837
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition strexpr.h:3827
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition strexpr.h:4108
constexpr R trimmed_suffix_ia(str_piece suffix) const
Returns a string with the end of the string removed if it ends with the specified suffix,...
Definition strexpr.h:5398
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3496
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition strexpr.h:3749
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition strexpr.h:4955
constexpr R trimmed_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition strexpr.h:5280
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition strexpr.h:3847
constexpr std::basic_string< D, Traits, Allocator > to_string() const
Convert to std::basic_string.
Definition strexpr.h:3441
constexpr R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition strexpr.h:5157
constexpr void for_all_founded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Call a functor on all found occurrences of a substring in this string.
Definition strexpr.h:4296
constexpr T splitf(str_piece delimiter, const Op &beforeFunc, size_t offset=0) const
Split a string into parts at a given delimiter, possibly applying a functor to each substring.
Definition strexpr.h:4717
constexpr R trimmed(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left and right.
Definition strexpr.h:5127
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition strexpr.h:4851
constexpr std::optional< R > strip_prefix_ia(str_piece prefix) const
Get a string without a prefix if it starts with it, insensitive to ASCII characters.
Definition strexpr.h:5010
constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified case-insensitive ASCII substring followed by one of the s...
Definition strexpr.h:4825
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition strexpr.h:3973
constexpr std::optional< R > strip_suffix_ia(str_piece suffix) const
Get a string without a suffix if it ends with one in case-insensitive ASCII characters.
Definition strexpr.h:5048
constexpr R trimmed_suffix(str_piece suffix) const
Returns a string with the beginning of the string removed if it begins with the specified prefix.
Definition strexpr.h:5378
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition strexpr.h:3384
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition strexpr.h:4545
constexpr bool equal_ia(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
Definition strexpr.h:3894
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4620
constexpr size_t find_end_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string, or the end of a string.
Definition strexpr.h:4094
constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept
Check if a string begins with the specified substring followed by one of the specified characters.
Definition strexpr.h:4770
The concept of a string expression compatible with a given character type.
Definition strexpr.h:525
Concept of "String Expressions".
Definition strexpr.h:507
Base concept of string object.
Definition strexpr.h:312
Checks whether two types are compatible string types.
Definition strexpr.h:173
constexpr expr_if< A > e_if(bool c, const A &a)
Creating a conditional string expression expr_if.
Definition strexpr.h:1540
simstr::expr_stdstr< typename T::value_type, T > operator+(const T &str)
Unary operator + for converting standard strings to string expressions.
Definition strexpr.h:8014
constexpr expr_spaces< uws, N > e_spcw()
Generates a string of N wchar_t spaces.
Definition strexpr.h:1104
expr_fill< K, A, true > e_fill_left(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:2695
constexpr empty_expr< uws > eew
Empty string expression of type wchar_t.
Definition strexpr.h:862
constexpr expr_spaces< u8s, N > e_spca()
Generates a string of N char spaces.
Definition strexpr.h:1086
constexpr empty_expr< u32s > eeuu
Empty string expression of type char32_t.
Definition strexpr.h:874
constexpr expr_num< K, T > e_num(T t)
Convert an integer to a string expression.
Definition strexpr.h:1716
constexpr empty_expr< ubs > eeb
Empty string expression of type char8_t.
Definition strexpr.h:856
HexFlags
Flags for the e_hex function.
Definition strexpr.h:2523
constexpr auto e_hex(T v)
Creates an object that can be converted to a string expression that generates a hexadecimal represent...
Definition strexpr.h:2580
constexpr strexprjoin< A, B > operator+(const A &a, const B &b)
An addition operator for two arbitrary string expressions of the same character type.
Definition strexpr.h:719
constexpr expr_repeat_lit< K, M - 1 > e_repeat(T &&s, size_t l)
Generate a string from l string constants s of type K.
Definition strexpr.h:1214
constexpr expr_literal< typename const_lit< T >::symb_type, static_cast< size_t >(N - 1)> e_t(T &&s)
Converts a string literal to a string expression.
Definition strexpr.h:991
expr_fill< K, A, false > e_fill_right(const A &a, size_t width, K symbol=K(' '))
Creates an expression that expands the specified string expression to the specified length given char...
Definition strexpr.h:2721
constexpr expr_choice< A, B > e_choice(bool c, const A &a, const B &b)
Create a conditional string expression expr_choice.
Definition strexpr.h:1462
constexpr expr_char< K > e_char(K s)
Generates a string of 1 given character.
Definition strexpr.h:918
constexpr empty_expr< u8s > eea
Empty string expression of type char.
Definition strexpr.h:850
constexpr expr_pad< K > e_c(size_t l, K s)
Generates a string of l characters s of type K.
Definition strexpr.h:1149
constexpr empty_expr< u16s > eeu
Empty string expression of type char16_t.
Definition strexpr.h:868
auto e_repl_const_symbols(A &&src, Repl &&... other)
Returns a string expression that generates a string containing the given characters replaced with giv...
Definition strexpr.h:6628
constexpr auto e_int(T v)
Creates an object that is converted to a string expression that generates a string representation of ...
Definition strexpr.h:2379
constexpr auto e_repl(A &&w, T &&p, X &&r)
Get a string expression that generates a string with all occurrences of a given substring replaced.
Definition strexpr.h:6135
constexpr auto e_join(const T &s, L &&d)
Get a string expression concatenating the strings in the container into a single string with the give...
Definition strexpr.h:2799
@ Short
without leading zeroes
Definition strexpr.h:2524
Small namespace for standard string methods.
Definition strexpr.h:1596
std::basic_string< K, std::char_traits< K >, A > & append(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Append a string expression to a standard string.
Definition strexpr.h:7553
std::basic_string< K, std::char_traits< K >, A > & prepend(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Insert a string expression at the beginning of a standard string.
Definition strexpr.h:7586
std::basic_string< K, std::char_traits< K >, A > & make_ascii_upper(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change lowercase ASCII string characters (a-z) to uppercase.
Definition strexpr.h:7952
std::basic_string< K, std::char_traits< K >, A > & change(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, size_t count, const E &expr)
Replace a portion of a standard string with the given string expression.
Definition strexpr.h:7480
std::basic_string< K, std::char_traits< K >, A > & replace(std::basic_string< K, std::char_traits< K >, A > &str, T &&pattern, const E &repl, size_t offset=0, size_t max_count=-1)
A function for searching for substrings in a standard string and replacing the found occurrences with...
Definition strexpr.h:7811
std::basic_string< K, std::char_traits< K >, A > & make_ascii_lower(std::basic_string< K, std::char_traits< K >, A > &str, size_t from=0, size_t to=-1)
Change uppercase ASCII string characters (A-Z) to lowercase.
Definition strexpr.h:7975
std::basic_string< K, std::char_traits< K >, A > & overwrite(std::basic_string< K, std::char_traits< K >, A > &str, const E &expr)
Rewrite the entire string with the given string expression, resizing the string if necessary.
Definition strexpr.h:7650
std::basic_string< K, std::char_traits< K >, A > & insert(std::basic_string< K, std::char_traits< K >, A > &str, size_t from, const E &expr)
Insert a string expression at the specified position in a standard string.
Definition strexpr.h:7621
Library namespace.
Definition sstring.cpp:12
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition strexpr.h:2866
@ Overflow
Переполнение, число не помещается в заданный тип
Definition strexpr.h:2869
@ Success
Успешно
Definition strexpr.h:2867
@ NotNumber
Вообще не число
Definition strexpr.h:2870
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition strexpr.h:2868
Some methods for working with standard strings.
Definition strexpr.h:7993
Generate a string based on the original one, replacing all ASCII uppercase letters (A-Z) with lowerca...
Definition strexpr.h:7433
Generate a string based on the original one, replacing all ASCII lowercase letters (a-z) with upperca...
Definition strexpr.h:7410
constexpr e_concat(G &&glue, Arg &&arg, Args &&...args)
Create a string expression concatenating the specified string expressions using the specified delimit...
Definition strexpr.h:6967
constexpr e_subst(const details::subst_params< K, PtLen, NParams > &subst, Args &&...args)
Creates a string expression that substitutes the values of the passed string expressions into the s...
Definition strexpr.h:7189
constexpr e_vsubst(str_src< K > pattern, Args &&...args)
Creates a string expression that substitutes the values of the passed string expressions into the s...
Definition strexpr.h:7288
An "empty" string expression.
Definition strexpr.h:835
Conditional selection string expression.
Definition strexpr.h:1341
Conditional selection string expression.
Definition strexpr.h:1407
Conditional selection string expression.
Definition strexpr.h:1250
Conditional selection string expression.
Definition strexpr.h:1279
A type of string expression that returns N specified characters.
Definition strexpr.h:1120
constexpr expr_replace_symbols(str_t source, const std::vector< std::pair< K, str_t > > &repl)
Expression constructor.
Definition strexpr.h:6712
A string expression that generates a string replacing all occurrences of the given substring to strin...
Definition strexpr.h:6253
constexpr expr_replaced_e(str_src< K > w, str_src< K > p, const E &e)
Constructor.
Definition strexpr.h:6272
A string expression that generates a string replacing all occurrences of the given substring to anoth...
Definition strexpr.h:6147
constexpr expr_replaced(str_src< K > w, str_src< K > p, str_src< K > r)
Constructor.
Definition strexpr.h:6165
A type of string expression that returns N specified characters.
Definition strexpr.h:1060
A type for using std::basic_string and std::basic_string_view as sources in string expressions.
Definition strexpr.h:1565
Base class for converting string expressions to standard strings.
Definition strexpr.h:659
A class that claims to refer to a null-terminated string.
Definition strexpr.h:5586
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition strexpr.h:5649
constexpr str_src_nt(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5620
constexpr str_src_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition strexpr.h:5611
constexpr str_src_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5638
constexpr str_src_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5626
The simplest class of an immutable non-owning string.
Definition strexpr.h:5450
constexpr str_src(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition strexpr.h:5479
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition strexpr.h:5504
constexpr size_t length() const noexcept
Get the length of the string.
Definition strexpr.h:5490
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition strexpr.h:5533
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition strexpr.h:5544
constexpr str_src(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition strexpr.h:5469
constexpr bool is_same(str_src< K > other) const noexcept
Check if two objects point to the same string.
Definition strexpr.h:5513
constexpr str_src(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition strexpr.h:5484
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5497
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition strexpr.h:5557
constexpr bool is_part_of(str_src< K > other) const noexcept
Check if a string is part of another string.
Definition strexpr.h:5522
constexpr str_src(T &&v) noexcept
Constructor from a string literal.
Definition strexpr.h:5463
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:744
Template class for concatenating two string expressions into one using operator +
Definition strexpr.h:682