simstr/include/simstr/strexpr.h

5140 lines
254 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* ver. 1.4.0
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
* Base for string concatenations via compile-time expressions
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <climits>
#include <limits>
#include <string>
#include <string_view>
#include <concepts>
#include <vector>
#include <optional>
#include <charconv>
#if defined __has_builtin
# if __has_builtin(__builtin_mul_overflow) && __has_builtin(__builtin_add_overflow)
# define HAS_BUILTIN_OVERFLOW
# endif
#endif
#ifdef _MSC_VER
#define _no_unique_address msvc::no_unique_address
#define decl_empty_bases __declspec(empty_bases)
#else
#define _no_unique_address no_unique_address
#define decl_empty_bases
#endif
#ifdef _MSC_VER
/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
Пишут, что баг исправлен, но видимо не до конца.
Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
They write that the bug has been fixed, but apparently not completely.
Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
#define SS_CONSTEVAL constexpr
#else
#define SS_CONSTEVAL consteval
#endif
/*!
* @ru @brief Пространство имён для объектов библиотеки
* @en @brief Library namespace
*/
namespace simstr {
// Выводим типы для 16 и 32 битных символов в зависимости от размера wchar_t
// Infer types for 16 and 32 bit characters depending on the size of wchar_t
inline constexpr bool wchar_is_u16 = sizeof(wchar_t) == 2;
using wchar_type = std::conditional<wchar_is_u16, char16_t, char32_t>::type;
inline wchar_type* to_w(wchar_t* p) {
return (reinterpret_cast<wchar_type*>(p));
}
inline const wchar_type* to_w(const wchar_t* p) {
return (reinterpret_cast<const wchar_type*>(p));
}
inline wchar_t* from_w(wchar_type* p) {
return (reinterpret_cast<wchar_t*>(p));
}
inline const wchar_t* from_w(const wchar_type* p) {
return (reinterpret_cast<const wchar_t*>(p));
}
using u8s = char;
using ubs = char8_t;
using uws = wchar_t;
using u16s = char16_t;
using u32s = char32_t;
using uu8s = std::make_unsigned<u8s>::type;
template<typename T, typename K = void, typename... Types>
struct is_one_of_type {
static constexpr bool value = std::is_same_v<T, K> || is_one_of_type<T, Types...>::value;
};
template<typename T>
struct is_one_of_type<T, void> : std::false_type {};
template<typename K>
constexpr bool is_one_of_char_v = is_one_of_type<K, u8s, ubs, wchar_t, u16s, u32s>::value;
template<typename K>
constexpr bool is_one_of_std_char_v = is_one_of_type<K, u8s, ubs, wchar_t, wchar_type>::value;
template<typename From>
requires (is_one_of_std_char_v<From>)
auto to_one_of_std_char(From* from) {
if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
return from;
} else if constexpr (std::is_same_v<From, ubs>) {
return reinterpret_cast<u8s*>(from);
} else {
return from_w(from);
}
}
template<typename From>
requires (is_one_of_std_char_v<From>)
auto to_one_of_std_char(const From* from) {
if constexpr (std::is_same_v<From, u8s> || std::is_same_v<From, wchar_t>) {
return from;
} else if constexpr (std::is_same_v<From, ubs>) {
return reinterpret_cast<const u8s*>(from);
} else {
return from_w(from);
}
}
/*!
* @ingroup StrExprs
* @ru @brief Проверка, являются ли два типа совместимыми строковыми типами.
* @tparam K1 - первый проверяемый тип.
* @tparam K2 - второй проверяемый тип.
* @details Оба типа должны быть строковыми типами и совпадать по размеру.
* То есть char и char8_t всегда совместимы, wchar_t тождественен в Linux char32_t, а в Windows - char16_t.
* Это для возможности смешивать строковые выражения совместимых типов.
* @en @brief Checks whether two types are compatible string types.
* @tparam K1 is the first type to check.
* @tparam K2 is the second type to check.
* @details Both types must be string types and the same size.
* That is, char and char8_t are always compatible, wchar_t is identical to char32_t on Linux, and char16_t on Windows.
* This is for the ability to mix string expressions of compatible types.
*/
template<typename K1, typename K2>
constexpr bool is_equal_str_type_v = is_one_of_char_v<K1> && is_one_of_char_v<K2> && sizeof(K1) == sizeof(K2);
/*
Вспомогательные шаблоны для определения строковых литералов.
Используются для того, чтобы в параметрах функций ограничивать типы строго как `const K(&)[N]`
Если пишем
template<size_t N>
void func(const char(&lit)[N]);
то в такую функцию можно будет передать не константный буфер, что может вызывать ошибку:
// Выделили место под символы
char buf[100];
// Как то наполнили буфер, допустим, до половины.
stringa text = buf;
Тут компилятор приведет buf из типа char[100] в тип const char[100] и вызовет конструктор
для строкового литерала, в text запишется просто указатель на buf и длина 100.
Поэтому такие параметры объявляем как
template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
void func(T&& lit);
Тогда компилятор будет подставлять для T точный тип параметра без попыток привести тип к другому типу,
и выражение с параметром char[100] - не скомпилируется.
Helper templates for defining string literals.
They are used to limit types in function parameters strictly as `const K(&)[N]`
If we write
template<size_t N>
void func(const char(&lit)[N]);
then it will be possible to pass a non-constant buffer to such a function, which may cause an error:
// Allocate space for symbols
char buf[100];
// Somehow the buffer was filled, say, to half.
stringa text = buf;
Here the compiler will convert buf from type char[100] to type const char[100] and call the constructor
for a string literal, text will simply contain a pointer to buf and length 100.
Therefore, we declare such parameters as
template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
void func(T&& lit);
Then the compiler will substitute for T the exact type of the parameter without attempting to cast the type to another type,
and an expression with the char[100] parameter will not compile.
*/
template<typename T> struct const_lit; // sfinae отработает, так как не найдёт определения | sfinae will work because it won't find a definition
// Для правильных типов параметров есть определение, в виде специализации шаблона
// There is a definition for the correct parameter types, in the form of a template specialization
template<typename T, size_t N>
requires(is_one_of_char_v<T>)
struct const_lit<const T(&)[N]> {
using symb_type = T;
constexpr static size_t Count = N;
};
// Тут ещё дополнительно ограничиваем тип литерала
// Here we further restrict the type of the literal
template<typename K, typename T> struct const_lit_for;
template<typename K, typename P, size_t N>
requires(is_equal_str_type_v<K, P>)
struct const_lit_for<K, const P(&)[N]> {
constexpr static size_t Count = N;
};
template<typename K, size_t N>
class const_lit_to_array {
template<size_t Idx>
constexpr size_t find(K s) const {
if constexpr (Idx < N) {
return s == symbols_[Idx] ? Idx : find<Idx + 1>(s);
}
return -1;
}
template<size_t Idx>
constexpr bool exist(K s) const {
if constexpr (Idx < N) {
return s == symbols_[Idx] || exist<Idx + 1>(s);
}
return false;
}
public:
const K (&symbols_)[N + 1];
template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
constexpr const_lit_to_array(T&& s)
: symbols_(s) {}
constexpr bool contain(K s) const {
return exist<0>(s);
}
constexpr size_t index_of(K s) const {
return find<0>(s);
}
};
/*!
* @ru @brief Базовая концепция строкового объекта.
* @tparam A - проверяемый тип
* @tparam K - тип символов
* @details В библиотеке для разных целей могут использоваться различные типы объектов строк.
* Мы считаем строковым объектом любой объект, поддерживающий методы:
* - `is_empty()`: возвращает, пуста ли строка.
* - `length()`: возвращает длину строки без нулевого терминатора.
* - `symbols()`: возвращает указатель на строку символов.
* - `typename symb_type`: задаёт тип символов строки
*
* @en @brief Base concept of string object.
* @tparam A - tested type
* @tparam K - type of symbols
* @details The library can use different types of string objects for different purposes.
* We consider a string object to be any object that supports methods:
* - `is_empty()`: Returns whether the string is empty.
* - `length()`: returns the length of a string without a null terminator.
* - `symbols()`: returns a pointer to a string of symbols.
* - `typename symb_type`: sets the character type of the string
*/
template<typename A, typename K>
concept StrType = requires(const A& a) {
{ a.is_empty() } -> std::same_as<bool>;
{ a.length() } -> std::convertible_to<size_t>;
{ a.symbols() } -> std::same_as<const K*>;
} && std::is_same_v<typename std::remove_cvref_t<A>::symb_type, K>;
/*!
* @ru @defgroup StrExprs Строковые выражения
* @brief Описание строковых выражений
* @details Все типы владеющих строк в simstr могут инициализироваться с помощью "строковых выражений".
* (по сути это вариант https://en.wikipedia.org/wiki/Expression_templates для строк).
* Строковое выражение - это объект произвольного типа, у которого имеются методы:
* - `size_t length() const`: выдает длину строки
* - `K* place(K*) const`: скопировать символы строки в предназначенный буфер и вернуть указатель за последним символом
* - `typename symb_type`: показывает, с каким типом символов он работает.
*
* При инициализации строковый объект запрашивает у строкового выражения его размер, выделяет необходимую память,
* и передает память строковому выражению, которое помещает символы в отведённый буфер.
*
* Кроме того, для совместимости с `std`, строковые выражения simstr могут конвертироваться в стандартные строки
* (std::basic_string) совместимых типов. До C++23 используется `resize` и потом заполнение через `data()`, начиная с C++23
* используется более оптимальный `resize_and_overwrite`. Это позволяет использовать быструю конкатенацию там, где требуются стандартные строки.
*
* Все строковые объекты библиотеки сами являются строковыми выражениями, которые просто копирует исходную строку.
* В-основном строковые выражения используются для конкатенации или конвертации строк.
*
* Для всех строковых выражений определен @ref op_plus_str_expr "operator +", который из двух операндов создает новое строковое выражение
* simstr::strexprjoin, объединяющее два строковых выражения, и которое в методе `length` возвращает сумму `length` исходных операндов,
* а в методе `place` - размещает в буфер результата сначала первый операнд, потом второй.
* И так как этот оператор сам возвращает строковое выражение, то к нему снова можно применить `operator +`, формируя цепочку из нескольких
* строковых выражений, и в итоге "материализовать" последний получившийся объект, который сначала посчитает размер всей общей памяти
* для конечного результата, а затем разместит вложенные подвыражения в один буфер.
*
* Также `operator +` определён для строковых выражений и строковых литералов, строковых выражений и чисел
* (числа конвертируются в десятичное представление), а также вы можете сами добавить желаемые типы строковых выражений.
* Пример:
* ```cpp
* stringa text = header + ", count = " + count + ", done";
* ```
* Существует несколько типов строковых выражений "из коробки", для выполнения различных операций со строками
* - `expr_spaces< ТипСимвола, КоличествоСимволов, Символ>{}`: выдает строку длиной КоличествоСимволов,
* заполненную заданным символом. Количество символов и символ - константы времени компиляции.
* Для некоторых случаев есть сокращенная запись:
* - `e_spca< КоличествоСимволов>()`: строка char пробелов
* - `e_spcw< КоличествоСимволов>()`: строка w_char пробелов
* - `expr_pad< ТипСимвола>{КоличествоСимволов, Символ}`: выдает строку длинной КоличествоСимволов,
* заполненную заданным символом. Количество символов и символ могут задаваться в рантайме.
* Сокращенная запись: `e_c (КоличествоСимволов, Символ)`
* - `e_choice (bool Condition, StrExpr1, StrExpr2)`: если Condition == true, результат будет равен StrExpr1, иначе StrExpr2
* - `e_num< ТипСимвола>(ЦелоеЧисло)`: конвертирует число в десятичное представление. Редко используется, так как
* для строковых выражений и чисел переопределен оператор "+", и число можно просто написать как text + number;
* - `e_real< ТипСимвола>(ВещественноеЧисло)`: конвертирует число в десятичное представление. Редко используется, так как
* для строковых выражений и чисел переопределен оператор "+", и число можно просто написать как `text + number`;
* - `e_join< bool ПослеПоследнего = false, bool ПропускатьПустые = false>(контейнер, "Разделитель")`: конкатенирует все строки
* в контейнере, используя разделитель.
* Если `ПослеПоследнего == true`, то разделитель добавляется и после последнего элемента контейнера, иначе только
* между элементами.
* Если `ПропускатьПустые == true`, то пустые строки не добавляют разделитель, иначе для каждой пустой строки
* тоже вставляется разделитель
* - `e_repl(ИсходнаяСтрока, Искать, Заменять)`: заменяет в исходной строке вхождения Искать на Заменять.
* - `e_hex(Число)`: генерирует строку с 16ричным представлением числа.
* - `e_fill_left(StrExpr, width, symbol)`, `e_fill_right(StrExpr, width, symbol)`: дополняет строковое выражение до нужной длины заданным символом.
* и т.д. и т.п.
*
* В одно выражение могут объединятся строковые выражения для символов разных, но совместимых типов.
* То есть можно сочетать `char` и `char8_t`, под Linux `wchar_t` и `char32_t`, под Windows `wchar_t` и `char16_t`.
*
* @en @defgroup StrExprs String Expressions
* @brief Description of String Expressions
* @details All owning string types can be initialized using "string expressions"
* (essentially a variant of https://en.wikipedia.org/wiki/Expression_templates for strings).
* A string expression is an object of an arbitrary type that has methods:
* - `size_t length() const`: returns the length of the string
* - `K* place(K*) const`: copy the characters of the string to the intended buffer and return a pointer behind the last character
* - `typename symb_type`: shows what type of symbols it works with.
*
* During initialization, a string object asks the string expression for its size, allocates the necessary memory,
* and passes the memory to a string expression that places the characters in the allocated buffer.
*
* Additionally, for compatibility with `std`, simstr string expressions can be converted to standard strings
* (std::basic_string) compatible types. Before C++23, `resize` and `data` is used, starting with C++23
* the more optimal `resize_and_overwrite` is used. This allows for fast concatenation where standard strings are required.
*
* All library string objects are themselves string expressions that simply copy the original string.
* Basically, string expressions are used to concatenate or convert strings.
*
* For all string expressions, @ref op_plus_str_expr "operator +" is defined, which creates a new string expression from two operands
* simstr::strexprjoin, which combines two string expressions, and which in the `length` method returns the sum of the `length` original operands,
* and in the `place` method - places first the first operand, then the second, into the result buffer.
* And since this operator itself returns a string expression, you can again apply `operator +` to it, forming a chain of several
* string expressions, and eventually “materialize” the last resulting object, which will first calculate the size of the entire shared memory
* for the final result, and then will place the nested subexpressions into a single buffer.
*
* Also `operator +` is defined for string expressions and string literals, string expressions and numbers
* (numbers are converted to decimal representation), and you can also add the desired types of string expressions yourself.
* Example:
* ```cpp
* stringa text = header + ", count = " + count + ", done";
* ```
* There are several types of string expressions out of the box to perform various operations on strings
* - `expr_spaces< Character Type, Number of Characters, Symbol>{}`: returns a string of length Number of Characters,
* filled with the specified character. The number of characters and character are compile-time constants.
* For some cases there is a shorthand notation:
* - `e_spca< Number of Characters>()`: char string of spaces
* - `e_spcw< Number of Characters>()`: w_char string of spaces
* - `expr_pad< Character Type>{Number of Characters, Symbol}`: produces a string long Number of Characters,
* filled with the specified character. The number of characters and the symbol can be set at runtime.
* Shorthand: `e_c(Number of Characters, Character)`
* - `e_choice(bool Condition, StrExpr1, StrExpr2)`: if Condition == true, the result will be StrExpr1, otherwise StrExpr2
* - `e_num< CharacterType>(IntegerNumber)`: Converts a number to decimal notation. Rarely used because
* for string expressions and numbers the "+" operator is redefined, and the number can simply be written as text + number;
* - `e_real< CharacterType>(RealNumber)`: Converts a number to decimal notation. Rarely used because
* for string expressions and numbers the "+" operator is overridden, and the number can simply be written as `text + number`;
* - `e_join< bool AfterLast = false, bool SkipEmpty = false>(container, "Separator")`: concatenates all strings
* in a container using a separator.
* If `AfterLast == true`, then the separator is added after the last element of the container, otherwise only
* between elements.
* If `Skip Empty == true`, then empty lines do not add a separator, otherwise for each empty line
* a separator is also inserted
* - `e_repl(SourceString, Search, Replace)`: replaces the occurrences of Search with Replace in the source string.
* - `e_hex(Number)`: generates a string with hexadecimal representation of the number.
* - `e_fill_left(StrExpr, width, symbol)`, `e_fill_right(StrExpr, width, symbol)`: fills a string expression to the required length with a given character.
* etc. etc.
*
* String expressions for characters of different but compatible types can be combined into one expression.
* That is, you can combine `char` and `char8_t`, and under Linux `wchar_t` and `char32_t`, under Windows `wchar_t` and `char16_t`.
*/
/*!
* @ingroup StrExprs
* @ru @brief Концепт "Строковых выражений".
* @details Это концепт, проверяющий, является ли тип "строковым выражением".
* @en @brief Concept of "String Expressions".
* @details This is a concept that checks whether a type is a "string expression".
*/
template<typename A>
concept StrExpr = requires(const A& a) {
typename A::symb_type;
{ a.length() } -> std::convertible_to<size_t>;
{ a.place(std::declval<typename A::symb_type*>()) } -> std::same_as<typename A::symb_type*>;
};
/*!
* @ingroup StrExprs
* @ru @brief Концепт строкового выражения, совместимого с заданным типом символов.
* @tparam A - проверяемый тип.
* @tparam K - проверяемый тип символов.
* @details Служит для задания ограничения к строковому выражению по типу символов.
* @en @brief The concept of a string expression compatible with a given character type.
* @tparam A - type being checked.
* @tparam K - character type to be checked.
* @details Used to set restrictions on a string expression by character type.
*/
template<typename A, typename K>
concept StrExprForType = StrExpr<A> && is_equal_str_type_v<K, typename A::symb_type>;
/*
* Шаблонные классы для создания строковых выражений из нескольких источников.
* Благодаря компиляторно-шаблонной "магии" позволяют максимально эффективно
* получать результирующую строку - сначала вычисляется длина результирующей строки,
* потом один раз выделяется память для результата, после символы помещаются в
* выделенную память.
* Для конкатенация двух объектов строковых выражений в один
* Template classes for creating string expressions from multiple sources.
* Thanks to compiler-template "magic" they allow you to maximize efficiency
* get the resulting string - first the length of the resulting string is calculated,
* then memory is allocated once for the result, after which the characters are placed in
* allocated memory.
* For concatenating two string expression objects into one.
*/
template<typename K, typename Allocator, StrExpr A>
constexpr std::basic_string<K, std::char_traits<K>, Allocator> to_std_string(const A& expr) {
std::basic_string<K, std::char_traits<K>, Allocator> res;
if (size_t l = expr.length()) {
auto fill = [&](K* ptr, size_t size) -> size_t {
expr.place((typename A::symb_type*)ptr);
return l;
};
if constexpr (requires { res.resize_and_overwrite(l, fill); }) {
res.resize_and_overwrite(l, fill);
} else if constexpr (requires{ res._Resize_and_overwrite(l, fill); }) {
// Work in MSVC std lib before C++23
res._Resize_and_overwrite(l, fill);
} else {
res.resize(l); // bad, fill by 0 first.
expr.place((typename A::symb_type*)res.data());
}
}
return res;
}
/*!
* @ru @brief Базовый класс для преобразования строковых выражений в стандартные строки
* @details Если хотите, чтобы ваш тип строкового выражения конвертировался в стандартную строку,
* наследуйтесь от этого класса.
* @tparam Impl - конечный класс-наследник, для CRTP.
* @en @brief Base class for converting string expressions to standard strings
* @details If you want your string expression type to be converted to a standard string,
* inherit from this class.
* @tparam Impl - final descendant class for CRTP.
*/
template<typename Impl>
struct expr_to_std_string {
template<typename P, typename Allocator>
requires is_equal_str_type_v<typename Impl::symb_type, P>
constexpr operator std::basic_string<P, std::char_traits<P>, Allocator>() const {
return to_std_string<P, Allocator>(*static_cast<const Impl*>(this));
}
};
/*!
* @ingroup StrExprs
* @ru @brief Шаблонный класс для конкатенации двух строковых выражений в одно с помощью `operator +`
* @tparam A - Тип первого операнда
* @tparam B - Тип второго операнда
* @details Этот объект запоминает ссылки на два операнда операции сложения.
* Когда у него запрашивают необходимый для результата размер буфера - он выдает сумму длин своих операндов.
* Когда запрашивают размещение символов в буфере - размещает сначала первый операнд, затем второй.
* @en @brief Template class for concatenating two string expressions into one using `operator +`
* @tparam A - Type of first operand
* @tparam B - Type of second operand
* @details This object remembers references to the two operands of the addition operation.
* When asked for the required buffer size for a result, it gives the sum of the lengths of its operands.
* When asked to place characters in a buffer, place the first operand first, then the second.
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
struct strexprjoin : expr_to_std_string<strexprjoin<A, B>>{
using symb_type = typename A::symb_type;
const A& a;
const B& b;
constexpr strexprjoin(const A& a_, const B& b_) : a(a_), b(b_){}
constexpr size_t length() const noexcept {
return a.length() + b.length();
}
constexpr symb_type* place(symb_type* p) const noexcept {
return (symb_type*)b.place((typename B::symb_type*)a.place(p));
}
};
/*!
* @ingroup StrExprs
* @anchor op_plus_str_expr
* @ru @brief Оператор сложения двух произвольных строковых выражения для одинакового типа символов.
* @param a - первое строковое выражение.
* @param b - второе строковое выражение.
* @return strexprjoin<A, B>, строковое выражение, генерирующее объединение переданных выражений.
* @details Когда складываются два объекта - строковых выражения, один типа `A`, другой типа `B`,
* мы возвращаем объект типа strexprjoin<A, B>, который содержит ссылки на два этих операнда.
* А сам объект strexprjoin<A, B> тоже в свою очередь является строковым выражением, и может участвовать
* в следующих операциях сложения. Таким образом формируется "дерево" из исходных строковых
* выражений, которое потом за один вызов "материализуется" в конечный результат.
*
* @en @brief An addition operator for two arbitrary string expressions of the same character type.
* @param a - first string expression
* @param b - second string expression
* @return strexprjoin<A, B>, a string expression that generates a join of the given expressions.
* @details When two objects are added - string expressions, one of type `A`, the other of type `B`,
* we return an object of type strexprjoin<A, B>, which contains references to these two operands.
* And the strexprjoin<A, B> object itself, in turn, is also a string expression, and can participate
* in the following addition operations. In this way, a “tree” is formed from the original strings
* expressions, which are then “materialized” into the final result in one call.
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
constexpr strexprjoin<A, B> operator+(const A& a, const B& b) {
return {a, b};
}
/*!
* @ingroup StrExprs
* @ru @brief Конкатенация ссылки на строковое выражение и значения строкового выражения.
* @tparam A - Тип одного строкового выражения.
* @tparam B - Тип другого строкового выражения.
* @tparam last - какое из них первое.
* @details Чтобы иметь возможность складывать строковое выражение с операндами, не являющимися строковым выражением,
* нам нужно иметь возможность вернуть из `operator+` объект, который сохранит ссылку на операнд, являющийся строковым
* выражением, а для не строкового операнда будет иметь поле со строковым выражением, обрабатывающим второй операнд.
* Можно посмотреть пример в simstr::operator+<StrExpr A, FromIntNumber T>()
*
* @en @brief Concatenation of a reference to a string expression and the value of the string expression.
* @tparam A - Type of a single string expression.
* @tparam B - Type of another string expression.
* @tparam last - which one is the first.
* @details To be able to add a string expression with non-string operands,
* we need to be able to return an object from `operator+` that will retain a reference to the operand, which is a string
* expression, and for a non-string operand will have a field with a string expression that processes the second operand.
* You can see an example in simstr::operator+<StrExpr A, FromIntNumber T>()
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B, bool last = true>
struct strexprjoin_c : expr_to_std_string<strexprjoin_c<A, B, last>>{
using symb_type = typename A::symb_type;
const A& a;
B b;
template<typename... Args>
constexpr strexprjoin_c(const A& a_, Args&&... args_) : a(a_), b(std::forward<Args>(args_)...) {}
constexpr size_t length() const noexcept {
return a.length() + b.length();
}
constexpr symb_type* place(symb_type* p) const noexcept {
if constexpr (last) {
return (symb_type*)b.place((typename B::symb_type*)a.place(p));
} else {
return a.place((symb_type*)b.place((typename B::symb_type*)p));
}
}
};
/*!
* @ingroup StrExprs
* @ru @brief "Пустое" строковое выражение.
* @tparam K - тип символа.
* @details Простое строковое выражение, генерирующее пустую строку.
* В основном применяется в функции e_choice, когда одна из веток должна вернуть пустую строку.
* Либо для начала операции сложения строковых выражений, когда другой операнд не является строковым выражением,
* но для него есть оператор сложения со строковыми выражениями.
* Для удобства уже определены константные объекты этого типа для разных видов символов:
* - eea для пустой строки char
* - eew для пустой строки wchar_t
* - eeu для пустой строки char16_t
* - eeuu для пустой строки char32_t
*
* @en @brief An "empty" string expression.
* @tparam K is a symbol.
* @details A simple string expression that generates an empty string.
* Mainly used in the e_choice function when one of the branches should return an empty string.
* Either to start the addition operation of string expressions when the other operand is not a string expression,
* but there is an addition operator for it with string expressions.
* For convenience, constant objects of this type have already been defined for different types of symbols:
* - eea for empty char string
* - eew for empty string wchar_t
* - eeu for empty string char16_t
* - eeuu for empty string char32_t
*
*
* @ru Пример: @en Example: @~
* ```cpp
* result = shost + e_choice(sserv.is_empty(), eea, ":" + sserv);
* ```
*
* ```cpp
* result += "E" + e_choice(adjusted_exponent > 0, "+"_ss, eea) + adjusted_exponent;
* ```
*/
template<typename K>
struct empty_expr : expr_to_std_string<empty_expr<K>>{
using symb_type = K;
constexpr size_t length() const noexcept {
return 0;
}
constexpr symb_type* place(symb_type* p) const noexcept {
return p;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Пустое строковое выражение типа char.
* @en @brief Empty string expression of type char.
*/
inline constexpr empty_expr<u8s> eea{};
/*!
* @ingroup StrExprs
* @ru @brief Пустое строковое выражение типа char8_t.
* @en @brief Empty string expression of type char8_t.
*/
inline constexpr empty_expr<u8s> eeb{};
/*!
* @ingroup StrExprs
* @ru @brief Пустое строковое выражение типа wchar_t.
* @en @brief Empty string expression of type wchar_t.
*/
inline constexpr empty_expr<uws> eew{};
/*!
* @ingroup StrExprs
* @ru @brief Пустое строковое выражение типа char16_t.
* @en @brief Empty string expression of type char16_t.
*/
inline constexpr empty_expr<u16s> eeu{};
/*!
* @ingroup StrExprs
* @ru @brief Пустое строковое выражение типа char32_t.
* @en @brief Empty string expression of type char32_t.
*/
inline constexpr empty_expr<u32s> eeuu{};
template<typename K>
struct expr_char : expr_to_std_string<expr_char<K>>{
using symb_type = K;
K value;
constexpr expr_char(K v) : value(v){}
constexpr size_t length() const noexcept {
return 1;
}
constexpr symb_type* place(symb_type* p) const noexcept {
*p++ = value;
return p;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Оператор сложения строкового выражения и одного символа.
* @return строковое выражение, объединяющее переданное выражение и символ.
* @en @brief Addition operator of a string expression and one character.
* @return a string expression that combines the passed expression and a character.
* @details @ru Пример: @en Example: @~
* @~
* ```cpp
* reply = prompt + '>' + result;
* ```
*/
template<typename K, StrExprForType<K> A>
constexpr strexprjoin_c<A, expr_char<K>> operator+(const A& a, K s) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из 1 заданного символа.
* @param s - символ.
* @return строковое выражение для строки из одного символа.
*
* @en @brief Generates a string of 1 given character.
* @param s - symbol.
* @return string expression for a single character string.
*/
template<typename K>
constexpr expr_char<K> e_char(K s) {
return {s};
}
template<typename K, size_t N>
struct expr_literal : expr_to_std_string<expr_literal<K, N>> {
using symb_type = K;
const K (&str)[N + 1];
constexpr expr_literal(const K (&str_)[N + 1]) : str(str_){}
constexpr size_t length() const noexcept {
return N;
}
constexpr symb_type* place(symb_type* p) const noexcept {
if constexpr (N != 0)
std::char_traits<K>::copy(p, str, N);
return p + N;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Преобразует строковый литерал в строковое выражение.
* @details Строковые литералы сами по себе не являются строковыми выражениями.
* Обычно в операциях конкатенации это не вызывает проблем, так как второй операнд уже является строковым выражением,
* и для него срабатывает сложение с литералом. Но есть ситуации, когда второй операнд тоже не является
* строковым выражением. Например:
* ```cpp
* int intVar = calculate();
* ...
* res = "text" + intVar;
* ...
* res = intVar + "text";
* ```
* В этом случае можно преобразовать литерал в строковое выражение двумя способами:
* - дописать _ss: `"text"_ss`, что преобразует литерал в simple_str_nt: `res = "text"_ss + intVar`
* - применить e_t: `e_t("text")`, что преобразует литерал в expr_literal: `res = e_t("text") + intVar`
*
* Во втором способе компилятор может более агрессивно применить оптимизации, связанные с известным при компиляции
* размером литерала.
*
* Хотя строго говоря, в этих ситуации можно пользоваться и другими способами:
* - Добавить операнд - пустое строковое выражение: `result = eea + "text" + intVar`, `result = "text" + eea + intVar`
* - Преобразовать другой операнд в строковое выражение: `result = "text" + e_num<u8s>(intVar)`.
*
* Все эти способы работают и выдают одинаковый результат. Каким пользоваться - дело вкуса.
*
* @en @brief Converts a string literal to a string expression.
* @details String literals are not themselves string expressions.
* This usually does not cause problems in concatenation operations, since the second operand is already a string expression,
* and addition with a literal works for it. But there are situations when the second operand is not either
* string expression. For example:
* ```cpp
* int intVar = calculate();
* ...
* res = "text" + intVar;
* ...
* res = intVar + "text";
* ```
* In this case, you can convert the literal to a string expression in two ways:
* - add _ss: `"text"_ss`, which converts the literal to simple_str_nt: `res = "text"_ss + intVar`
* - apply e_t: `e_t("text")`, which converts the literal to expr_literal: `res = e_t("text") + intVar`
*
* In the second method, the compiler can more aggressively apply optimizations related to what is known at compilation
* literal size.
*
* Although strictly speaking, in these situations you can use other methods:
* - Add an operand - an empty string expression: `result = eea + "text" + intVar`, `result = "text" + eea + intVar`
* - Convert another operand to a string expression: `result = "text" + e_num<u8s>(intVar)`.
*
* All these methods work and give the same result. Which one to use is a matter of taste.
*/
template<typename T, size_t N = const_lit<T>::Count>
constexpr expr_literal<typename const_lit<T>::symb_type, static_cast<size_t>(N - 1)> e_t(T&& s) {
return {s};
}
template<bool first, typename K, size_t N, typename A>
struct expr_literal_join : expr_to_std_string<expr_literal_join<first, K, N, A>> {
using symb_type = K;
using atype = typename A::symb_type;
const K (&str)[N + 1];
const A& a;
constexpr expr_literal_join(const K (&str_)[N + 1], const A& a_) : str(str_), a(a_){}
constexpr size_t length() const noexcept {
return N + a.length();
}
constexpr symb_type* place(symb_type* p) const noexcept {
if constexpr (N != 0) {
if constexpr (first) {
std::char_traits<K>::copy(p, str, N);
return (symb_type*)a.place((atype*)(p + N));
} else {
p = (symb_type*)a.place((atype*)p);
std::char_traits<K>::copy(p, str, N);
return p + N;
}
} else {
return a.place(p);
}
}
};
/*!
* @ingroup StrExprs
* @ru @brief Оператор сложения для строкового выражения и строкового литерала такого же типа символов.
* @return Строковое выражение, объединяющее операнды.
* @en @brief The addition operator for a string expression and a string literal of the same character type.
* @return A string expression concatenating the operands.
*/
template<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>
constexpr expr_literal_join<false, P, (N - 1), A> operator+(const A& a, T&& s) {
return {s, a};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор сложения для строкового литерала такого же типа символов и строкового выражения.
* @return Строковое выражение, объединяющее операнды.
* @en @brief The addition operator for a string literal of the same character type and string expression.
* @return A string expression concatenating the operands.
*/
template<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>
constexpr expr_literal_join<true, P, (N - 1), A> operator+(T&& s, const A& a) {
return {s, a};
}
/*!
* @ingroup StrExprs
* @ru @brief Тип строкового выражения, возвращающего N заданных символов.
* @details Количество символов и сам символ константы, т.е. задаются при компиляции.
* @tparam K - тип символа.
* @tparam N - количество символов.
* @tparam S - символ, по умолчанию пробел.
* @en @brief A type of string expression that returns N specified characters.
* @details The number of characters and the constant symbol itself, i.e. are specified during compilation.
* @tparam K is a symbol.
* @tparam N - number of characters.
* @tparam S - character, space by default.
*/
template<typename K, size_t N, size_t S = ' '>
struct expr_spaces : expr_to_std_string<expr_spaces<K, N>> {
using symb_type = K;
constexpr size_t length() const noexcept {
return N;
}
constexpr symb_type* place(symb_type* p) const noexcept {
if constexpr (N != 0)
std::char_traits<K>::assign(p, N, static_cast<K>(S));
return p + N;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из N char пробелов.
* @tparam N - Количество пробелов.
* @return строковое выражение для N char пробелов.
* @en @brief Generates a string of N char spaces.
* @tparam N - Number of spaces.
* @return string expression for N char spaces.
* @details @ru Пример: @en Example: @~
* ```cpp
* stringa text = e_spca<10>() + text + e_spca<10>();
* ```
*/
template<size_t N>
constexpr expr_spaces<u8s, N> e_spca() {
return {};
}
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из N wchar_t пробелов.
* @tparam N - Количество пробелов.
* @return строковое выражение для N wchar_t пробелов.
* @en @brief Generates a string of N wchar_t spaces.
* @tparam N - Number of spaces.
* @return string expression for N wchar_t spaces.
* @~ @details @ru Пример: @en Example: @~
* ```cpp
* stringw text = e_spcw<10>() + text + e_spcw<10>();
* ```
*/
template<size_t N>
constexpr expr_spaces<uws, N> e_spcw() {
return {};
}
/*!
* @ingroup StrExprs
* @ru @brief Тип строкового выражения, возвращающего N заданных символов.
* @tparam K - тип символа.
* @details Количество символов и сам символ переменные, т.е. могут меняться в рантайм.
* Напрямую обычно не используется, создается через e_c().
* @en @brief A type of string expression that returns N specified characters.
* @tparam K is a symbol.
* @details The number of characters and the character itself are variable, i.e. can change at runtime.
* Usually not used directly, created via e_c().
*/
template<typename K>
struct expr_pad : expr_to_std_string<expr_pad<K>> {
using symb_type = K;
size_t len;
K s;
constexpr expr_pad(size_t len_, K s_) : len(len_), s(s_){}
constexpr size_t length() const noexcept {
return len;
}
constexpr symb_type* place(symb_type* p) const noexcept {
if (len)
std::char_traits<K>::assign(p, len, s);
return p + len;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из l символов s типа K.
* @tparam K - тип символа.
* @param l - количество символов.
* @param s - символ.
* @return строковое выражение, генерирующее строку из l символов k.
* @en @brief Generates a string of l characters s of type K.
* @tparam K is a symbol.
* @param l - number of characters.
* @param s - symbol.
* @return a string expression that generates a string of l characters k.
*/
template<typename K>
constexpr expr_pad<K> e_c(size_t l, K s) {
return { l, s };
}
template<typename K, size_t N>
struct expr_repeat_lit : expr_to_std_string<expr_repeat_lit<K, N>> {
using symb_type = K;
size_t repeat_;
const K (&s)[N + 1];
constexpr expr_repeat_lit(size_t repeat, const K (&s_)[N + 1]) : repeat_(repeat), s(s_){}
constexpr size_t length() const noexcept {
return N * repeat_;
}
constexpr symb_type* place(symb_type* p) const noexcept {
if constexpr (N) {
for (size_t i = 0; i < repeat_; i++) {
std::char_traits<K>::copy(p, s, N);
p += N;
}
}
return p;
}
};
template<StrExpr A>
struct expr_repeat_expr : expr_to_std_string<expr_repeat_expr<A>> {
using symb_type = typename A::symb_type;
size_t repeat_;
const A& expr_;
constexpr expr_repeat_expr(size_t repeat, const A& expr) : repeat_(repeat), expr_(expr){}
constexpr size_t length() const noexcept {
if (repeat_) {
return repeat_ * expr_.length();
}
return 0;
}
constexpr symb_type* place(symb_type* p) const noexcept {
if (repeat_) {
if (repeat_ == 1) {
return expr_.place(p);
}
symb_type* start = p;
p = expr_.place(p);
size_t len = size_t(p - start);
if (len) {
for (size_t i = 1; i < repeat_; i++) {
std::char_traits<symb_type>::copy(p, start, len);
p += len;
}
}
}
return p;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из l строковых констант s типа K
* @tparam K - тип символа
* @param l - количество повторов
* @param s - строковый литерал
* @return строковое выражение, генерирующее строку из l строк s
* @en @brief Generate a string from l string constants s of type K
* @tparam K - character type
* @param l - number of repetitions
* @param s - string literal
* @return a string expression generating a string of l strings s
*/
template<typename T, typename K = const_lit<T>::symb_type, size_t M = const_lit<T>::Count> requires (M > 0)
constexpr expr_repeat_lit<K, M - 1> e_repeat(T&& s, size_t l) {
return { l, s };
}
/*!
* @ingroup StrExprs
* @ru @brief Генерирует строку из l строковых выражений s типа K
* @tparam K - тип символа
* @param l - количество повторов
* @param s - строковое выражение
* @return строковое выражение, генерирующее строку из l строковых выражений s
* @en @brief Generate a string from l string expressions s of type K
* @tparam K - character type
* @param l - number of repetitions
* @param s - string expression
* @return a string expression generating a string of l string expressions s
*/
template<StrExpr A>
constexpr expr_repeat_expr<A> e_repeat(const A& s, size_t l) {
return { l, s };
}
/*!
* @ingroup StrExprs
* @ru @brief Строковое выражение условного выбора.
* @tparam A Тип ветки для true.
* @tparam B Тип ветки для false.
* @details Выражение, в зависимости от истинности условия генерирующее либо выражение A, либо выражение B.
* Напрямую тип обычно не используется, создаётся через e_choice().
* @en @brief Conditional selection string expression.
* @tparam A Branch type for true.
* @tparam B Branch type for false.
* @details An expression that, depending on the truth of the condition, generates either expression A or expression B.
* The type is usually not used directly; it is created via e_choice().
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
struct expr_choice : expr_to_std_string<expr_choice<A, B>> {
using symb_type = typename A::symb_type;
using my_type = expr_choice<A, B>;
const A& a;
const B& b;
bool choice;
constexpr expr_choice(const A& _a, const B& _b, bool _choice) : a(_a), b(_b), choice(_choice){}
constexpr size_t length() const noexcept {
return choice ? a.length() : b.length();
}
constexpr symb_type* place(symb_type* ptr) const noexcept {
return choice ? a.place(ptr) : (symb_type*)b.place((typename B::symb_type*)ptr);
}
};
/*!
* @ingroup StrExprs
* @ru @brief Строковое выражение условного выбора.
* @tparam A Тип ветки для true.
* @details Выражение, в зависимости от истинности условия генерирующее либо выражение A, либо пустую строку.
* Напрямую тип обычно не используется, создаётся через e_if().
* @en @brief Conditional selection string expression.
* @tparam A Branch type for true.
* @details An expression that, depending on the truth of the condition, generates either expression A or an empty string.
* Title type usually not used, create through e_if().
*/
template<StrExpr A>
struct expr_if : expr_to_std_string<expr_if<A>> {
using symb_type = typename A::symb_type;
using my_type = expr_if<A>;
const A& a;
bool choice;
constexpr expr_if(const A& _a, bool _choice) : a(_a), choice(_choice){}
constexpr size_t length() const noexcept {
return choice ? a.length() : 0;
}
constexpr symb_type* place(symb_type* ptr) const noexcept {
return choice ? a.place(ptr) : ptr;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Строковое выражение условного выбора.
* @tparam A Тип ветки для true.
* @details Выражение, в зависимости от истинности условия генерирующее либо выражение A, либо строку из строкового литерала.
* Напрямую тип обычно не используется, создаётся через e_choice().
*
* Так как строковые литералы не являются строковыми выражениями, то использовать их в виде одиночного выражения в частях
* e_choice или e_if требовало бы их обрамления какими-либо конструкциями, преобразующими их в строковое выражение.
* Приходилось бы писать например так:
* ```cpp
* e_choice(condition, text, e_t("empty"));
* e_choice(condition, text, eea + "empty");
* e_choice(condition, text, "empty"_ss);
* e_if(!condition, "empty"_ss);
* ```
* Это, с одной стороны - захламляет код, с другой - делает его менее оптимальным.
* Поэтому для таких случаев сделаны перегрузки e_choice и e_if для случаев, когда их параметрами являются строковые литералы.
* В этих перегрузках и используются expr_choice_one_lit и expr_choice_two_lit, позволяя писать так:
* ```cpp
* e_choice(condition, text, "empty");
* e_choice(condition, "false", "true");
* e_if(!condition, "empty");
* ```
* @en @brief Conditional selection string expression.
* @tparam A Branch type for true.
* @details An expression that, depending on the truth of the condition, generates either expression A or a string from a string literal.
* The type is usually not used directly; it is created via e_choice().
*
* Since string literals are not string expressions, use them as a single expression in parts
* e_choice or e_if would require them to be surrounded by some constructs that convert them to a string expression.
* You would have to write something like this:
* ```cpp
* e_choice(condition, text, e_t("empty"));
* e_choice(condition, text, eea + "empty");
* e_choice(condition, text, "empty"_ss);
* e_if(!condition, "empty"_ss);
* ```
* This, on the one hand, clutters up the code, on the other, makes it less optimal.
* These overloads use expr_choice_one_lit and expr_choice_two_lit, allowing you to write like this:
* ```cpp
* e_choice(condition, text, "empty");
* e_choice(condition, "false", "true");
* e_if(!condition, "empty");
* ```
*/
template<typename L, StrExprForType<L> A, size_t N, bool Compare>
struct expr_choice_one_lit : expr_to_std_string<expr_choice_one_lit<L, A, N, Compare>> {
using symb_type = L;
const symb_type (&str)[N + 1];
const A& a;
bool choice;
constexpr expr_choice_one_lit(const symb_type (&_str)[N + 1], const A& _a, bool _choice) : str(_str), a(_a), choice(_choice){}
constexpr size_t length() const noexcept {
return choice == Compare ? a.length() : N;
}
constexpr symb_type* place(symb_type* ptr) const noexcept {
if (choice == Compare) {
return (L*)a.place((typename A::symb_type*)ptr);
}
if constexpr (N != 0) {
std::char_traits<symb_type>::copy(ptr, str, N);
}
return ptr + N;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Строковое выражение условного выбора.
* @details Выражение, в зависимости от истинности условия генерирующее либо один строковый литерал, либо другой.
* Напрямую тип обычно не используется, создаётся через e_choice().
*
* Так как строковые литералы не являются строковыми выражениями, то использовать их в виде одиночного выражения в частях
* e_choice или e_if требовало бы их обрамления какими-либо конструкциями, преобразующими их в строковое выражение.
* Приходилось бы писать например так:
* ```cpp
* e_choice(condition, text, e_t("empty"));
* e_choice(condition, text, eea + "empty");
* e_choice(condition, text, "empty"_ss);
* e_if(!condition, "empty"_ss);
* ```
* Это, с одной стороны - захламляет код, с другой - делает его менее оптимальным.
* Поэтому для таких случаев сделаны перегрузки e_choice и e_if для случаев, когда их параметрами являются строковые литералы.
* В этих перегрузках и используются expr_choice_one_lit и expr_choice_two_lit, позволяя писать так:
* ```cpp
* e_choice(condition, text, "empty");
* e_choice(condition, "false", "true");
* e_if(!condition, "empty");
* ```
* @en @brief Conditional selection string expression.
* @details An expression that, depending on the truth of the condition, generates either one string literal or another.
* The type is usually not used directly; it is created via e_choice().
*
* Since string literals are not string expressions, use them as a single expression in parts
* e_choice or e_if would require them to be surrounded by some constructs that convert them to a string expression.
* You would have to write something like this:
* ```cpp
* e_choice(condition, text, e_t("empty"));
* e_choice(condition, text, eea + "empty");
* e_choice(condition, text, "empty"_ss);
* e_if(!condition, "empty"_ss);
* ```
* This, on the one hand, clutters up the code, on the other, makes it less optimal.
* These overloads use expr_choice_one_lit and expr_choice_two_lit, allowing you to write like this:
* ```cpp
* e_choice(condition, text, "empty");
* e_choice(condition, "false", "true");
* e_if(!condition, "empty");
* ```
*/
template<typename K, size_t N, typename P, size_t M>
struct expr_choice_two_lit : expr_to_std_string<expr_choice_two_lit<K, N, P, M>> {
using symb_type = K;
const K (&str_a)[N + 1];
const P (&str_b)[M + 1];
bool choice;
constexpr expr_choice_two_lit(const K(&_str_a)[N + 1], const P(&_str_b)[M + 1], bool _choice)
: str_a(_str_a), str_b(_str_b), choice(_choice){}
constexpr size_t length() const noexcept {
return choice ? N : M;
}
constexpr symb_type* place(symb_type* ptr) const noexcept {
if (choice) {
if constexpr (N != 0) {
std::char_traits<symb_type>::copy(ptr, str_a, N);
}
return ptr + N;
}
if constexpr (M != 0) {
std::char_traits<symb_type>::copy(ptr, (const K(&)[M + 1])str_b, M);
}
return ptr + M;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Создание условного строкового выражения expr_choice.
* @tparam A - Тип выражение при истинности условия, выводится из аргумента.
* @tparam B - Тип выражения при ложности условия, выводится из аргумента.
* @param c - булево условие.
* @param a - строковое выражение, выполняющееся при `c == true`.
* @param b - строковое выражение, выполняющееся при `c == false`.
* @details Служит для возможности в одном выражении выбирать разные варианты в зависимости от условия.
* @en @brief Create a conditional string expression expr_choice.
* @tparam A - Type expression when the condition is true, inferred from the argument.
* @tparam B - The type of expression when the condition is false, inferred from the argument.
* @param c is a Boolean condition.
* @param a is a string expression that is executed when `c == true`.
* @param b is a string expression that is executed when `c == false`.
* @details Serves to allow you to select different options in one expression depending on the condition.
*
* @ru Примеры: @en Example: @~
* ```cpp
* columns_metadata.emplace_back(e_choice(name.is_empty(), "?column?", name) + "::" + metadata_column.type.to_string());
* ```
* ```cpp
* lstringa<512> str = e_choice(!ret_type_resolver_, sql_value::type_name(ret_type_), "any") + " " + name_ + "(";
* ```
* @ru Иначе такие операции приходилось бы разбивать на несколько модификаций строки или применению временных строк,
* что не оптимально и снизит производительность. (Это проверяется в бенчмарке "Build Full Func Name")
* @en Otherwise, such operations would have to be split into several string modifications or the use of temporary strings,
* which is not optimal and will reduce performance. (This is checked in the "Build Full Func Name" benchmark)
*/
template<StrExpr A, StrExprForType<typename A::symb_type> B>
constexpr expr_choice<A, B> e_choice(bool c, const A& a, const B& b) {
return {a, b, c};
}
/*!
* @ingroup StrExprs
* @ru @brief Перегрузка e_choice, когда третий аргумент - строковый литерал.
* @en @brief Overload e_choice when the third argument is a string literal.
*/
template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, true> e_choice(bool c, const A& a, T&& str) {
return {str, a, c};
}
/*!
* @ingroup StrExprs
* @ru @brief Перегрузка e_choice, когда второй аргумент - строковый литерал.
* @en @brief Overload e_choice when the second argument is a string literal.
*/
template<StrExpr A, typename T, size_t N = const_lit_for<typename A::symb_type, T>::Count>
constexpr expr_choice_one_lit<typename const_lit<T>::symb_type, A, N - 1, false> e_choice(bool c, T&& str, const A& a) {
return {str, a, c};
}
/*!
* @ingroup StrExprs
* @ru @brief Перегрузка e_choice, когда второй и третий аргумент - строковые литералы.
* @en @brief Overload e_choice when the second and third arguments are string literals.
*/
template<typename T, typename L, typename K = typename const_lit<T>::symb_type, typename P = typename const_lit<L>::symb_type,
size_t N = const_lit<T>::Count, size_t M = const_lit_for<typename const_lit<T>::symb_type, L>::Count>
requires is_equal_str_type_v<K, P>
constexpr expr_choice_two_lit<K, N -1, P, M - 1> e_choice(bool c, T&& str_a, L&& str_b) {
return {str_a, str_b, c};
}
/*!
* @ingroup StrExprs
* @ru @brief Создание условного строкового выражения expr_if
* @tparam A - Тип выражение при истинности условия, выводится из аргумента
* @param c - булево условие
* @param a - строковое выражение, выполняющееся при `c == true`
* @details Служит для возможности в одном выражении генерировать в зависимости от условия либо указанный вариант, либо пустую строку.
* @en @brief Creating a conditional string expression expr_if
* @tparam A - Type expression when the condition is true, inferred from the argument
* @param c - boolean condition
* @param a - string expression executed when `c == true`
* @details Serves to allow one expression to generate, depending on the condition, either the specified option or an empty string.
*
* @ru Примеры: @en Example @~
* ```cpp
* void sql_func_info::build_full_name() {
* // Временный буфер для результата, возьмём с запасом
* // Temporary buffer for the result, take it with reserve
* lstringa<512> str = e_choice(!ret_type_resolver_, sql_value::type_name(ret_type_), "any") + " " + name_ + "(";
*
* bool add_comma = false;
*
* for (const auto& param : params_) {
* str += e_if(add_comma, ", ") + e_if(param.optional_, "[");
* // Добавляет к str названия допустимых типов
* // Adds the names of valid types to str
* param.allowed_types.to_string(str);
* if (param.optional_) {
* str += "]";
* }
* add_comma = true;
* }
* // Сохраним в stringa
* // Save it in stringa
* full_name_ = str + e_if(unlim_params_, e_if(add_comma, ", ") + "...") + ")";
* }
* ```
* @ru Иначе такие операции приходилось бы разбивать на несколько модификаций строки или применению временных строк,
* что не оптимально и снизит производительность. (Этот пример проверяется в бенчмарке "Build Full Func Name")
* @en Otherwise, such operations would have to be split into several string modifications or the use of temporary strings,
* which is not optimal and will reduce performance. (This example is tested in the "Build Full Func Name" benchmark)
*/
template<StrExpr A>
constexpr expr_if<A> e_if(bool c, const A& a) {
return {a, c};
}
/*!
* @ingroup StrExprs
* @ru @brief Перегрузка e_if, когда второй аргумент - строковый литерал.
* @en @brief Overload e_if when the second argument is a string literal.
*/
template<typename T, size_t N = const_lit<T>::Count>
constexpr auto e_if(bool c, T&& str) {
using K = typename const_lit<T>::symb_type;
const K empty[1] = {0};
return expr_choice_two_lit<K, N - 1, K, 0>{str, empty, c};
}
template<typename T> struct is_std_string_source : std::false_type{};
template<typename K, typename A>
struct is_std_string_source<std::basic_string<K, std::char_traits<K>, A>> : std::true_type{};
template<typename K>
struct is_std_string_source<std::basic_string_view<K, std::char_traits<K>>> : std::true_type{};
template<typename T>
constexpr bool is_std_string_source_v = is_std_string_source<T>::value;
template<typename T>
concept StdStrSource = is_std_string_source_v<std::remove_cvref_t<T>>;
/*!
* @ingroup StrExprs
* @ru @brief Тип для использования std::basic_string и std::basic_string_view как источников в строковых выражениях.
* @tparam K - тип символа.
* @tparam T - тип источника.
* @en @brief A type for using std::string and std::string_view as sources in string expressions.
* @tparam K is a symbol.
* @tparam T - source type.
*/
template<typename K, StdStrSource T> requires is_equal_str_type_v<K, typename T::value_type>
struct expr_stdstr {
using symb_type = K;
const T& t_;
expr_stdstr(const T& t) : t_(t){}
constexpr size_t length() const noexcept {
return t_.length();
}
constexpr symb_type* place(symb_type* p) const noexcept {
size_t s = t_.size();
std::char_traits<K>::copy(p, (const K*)t_.data(), s);
return p + s;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Оператор сложения для строкового выражения и стандартных строк совместимого типа.
* @en @brief The addition operator for string expression and standard strings of compatible type.
*/
template<StdStrSource T, StrExprForType<typename T::value_type> A>
constexpr strexprjoin_c<A, expr_stdstr<typename T::value_type, T>, true> operator+(const A& a, const T& s) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор сложения для стандартных строк и строкового выражения совместимого типа.
* @en @brief The addition operator for standard strings and string expressions of compatible type.
*/
template<StdStrSource T, StrExprForType<typename T::value_type> A>
constexpr strexprjoin_c<A, expr_stdstr<typename T::value_type, T>, false> operator+(const T& s, const A& a) {
return {a, s};
}
namespace str {
constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
} // namespace str
template<typename K>
struct ch_traits : std::char_traits<K>{};
template<typename T>
concept FromIntNumber =
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;
template<typename T>
concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
template<typename K, bool I, typename T>
struct need_sign { // NOLINT
bool negate;
need_sign(T& t) : negate(t < 0) {
if (negate && t != std::numeric_limits<T>::min())
t = -t;
}
void after(K*& ptr) {
if (negate)
*--ptr = '-';
}
};
template<typename K, typename T>
struct need_sign<K, false, T> {
need_sign(T&) {}
void after(K*&) {}
};
template<typename K, typename T>
constexpr size_t fromInt(K* bufEnd, T val) {
const char* twoDigit =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
if (val) {
need_sign<K, std::is_signed_v<T>, T> sign(val);
K* itr = bufEnd;
// Когда у нас минимальное отрицательное число, оно не меняется и остается меньше нуля
// When we have a minimum negative number, it does not change and remains less than zero
if constexpr (std::is_signed_v<T>) {
if (val < 0) {
// Возьмем две последние цифры
// Take the last two digits
const char* ptr = twoDigit - (val % 100) * 2;
*--itr = static_cast<K>(ptr[1]);
*--itr = static_cast<K>(ptr[0]);
val /= 100;
val = -val;
}
}
while (val >= 100) {
const char* ptr = twoDigit + (val % 100) * 2;
*--itr = static_cast<K>(ptr[1]);
*--itr = static_cast<K>(ptr[0]);
val /= 100;
}
if (val < 10) {
*--itr = static_cast<K>('0' + val);
} else {
const char* ptr = twoDigit + val * 2;
*--itr = static_cast<K>(ptr[1]);
*--itr = static_cast<K>(ptr[0]);
}
sign.after(itr);
return size_t(bufEnd - itr);
}
bufEnd[-1] = '0';
return 1;
}
template<typename K, typename T>
struct expr_num : expr_to_std_string<expr_num<K, T>> {
using symb_type = K;
using my_type = expr_num<K, T>;
enum { bufSize = 24 };
mutable T value;
mutable K buf[bufSize];
constexpr expr_num(T t) : value(t) {}
constexpr expr_num(expr_num<K, T>&& t) : value(t.value) {}
size_t length() const noexcept {
value = (T)fromInt(buf + bufSize, value);
return (size_t)value;
}
K* place(K* ptr) const noexcept {
size_t len = (size_t)value;
ch_traits<K>::copy(ptr, buf + bufSize - len, len);
return ptr + len;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для строкового выражения и целого числа.
* @param a - строковое выражение.
* @param s - число.
* @details Число конвертируется в десятичное строковое представление.
* @en @brief Concatenation operator for string expression and integer.
* @param a is a string expression.
* @param s - number.
* @details The number is converted to a decimal string representation.
*/
template<StrExpr A, FromIntNumber T>
constexpr strexprjoin_c<A, expr_num<typename A::symb_type, T>> operator + (const A& a, T s) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для целого числа и строкового выражения.
* @param s - число.
* @param a - строковое выражение.
* @details Число конвертируется в десятичное строковое представление.
* @en @brief Concatenation operator for integer and string expression.
* @param s - number.
* @param a is a string expression.
* @details The number is converted to a decimal string representation.
*/
template<StrExpr A, FromIntNumber T>
constexpr strexprjoin_c<A, expr_num<typename A::symb_type, T>, false> operator + (T s, const A& a) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Преобразование целого числа в строковое выражение.
* @tparam K - тип символов.
* @tparam T - тип числа, выводится из аргумента.
* @param t - число.
* @details Возвращает строковое выражение, которое генерирует десятичное представление заданного числа.
* Может использоваться, когда надо конкатенировать число и строковый литерал.
* @en @brief Convert an integer to a string expression.
* @tparam K - character type.
* @tparam T - number type, inferred from the argument.
* @param t - number.
* @details Returns a string expression that generates the decimal representation of the given number.
* Can be used when you need to concatenate a number and a string literal.
*/
template<typename K, FromIntNumber T>
constexpr expr_num<K, T> e_num(T t) {
return {t};
}
template<typename K>
struct expr_real : expr_to_std_string<expr_real<K>> {
using symb_type = K;
mutable u8s buf[40];
mutable size_t l;
double v;
constexpr expr_real(double d) : v(d) {}
constexpr expr_real(float d) : v(d) {}
size_t length() const noexcept {
auto [ptr, ec] = std::to_chars(buf, buf + std::size(buf), v);
l = ec != std::errc{} ? 0 : ptr - buf;
return l;
}
K* place(K* ptr) const noexcept {
if constexpr (sizeof(K) == sizeof(buf[0])) {
ch_traits<K>::copy(ptr, (K*)buf, l);
} else {
for (size_t i = 0; i < l; i++) {
ptr[i] = buf[i];
}
}
return ptr + l;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для строкового выражения и вещественного числа (`float`, `double`).
* @param a - строковое выражение.
* @param s - число.
* @details Число конвертируется в строковое представление через sprintf("%.16g").
* @en @brief Concatenation operator for string expression and real number (`float`, `double`).
* @param a is a string expression.
* @param s - number.
* @details The number is converted to a string representation via sprintf("%.16g").
*/
template<StrExpr A, typename R>
requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
inline constexpr strexprjoin_c<A, expr_real<typename A::symb_type>> operator+(const A& a, R s) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для вещественного числа (`float`, `double`) и строкового выражения.
* @param s - число.
* @param a - строковое выражение.
* @details Число конвертируется в строковое представление через `sprintf("%.16g")`.
* @en @brief Concatenation operator for float (`float`, `double`) and string expression.
* @param s - number.
* @param a is a string expression.
* @details The number is converted to a string representation via `sprintf("%.16g")`.
*/
template<StrExpr A, typename R>
requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
inline constexpr strexprjoin_c<A, expr_real<typename A::symb_type>, false> operator+(R s, const A& a) {
return {a, s};
}
/*!
* @ingroup StrExprs
* @ru @brief Преобразование `double` числа в строковое выражение.
* @param t - число.
* @details Возвращает строковое выражение, которое генерирует десятичное представление заданного числа.
* с помощью `sprintf("%.16g")`. Может использоваться, когда надо конкатенировть число и строковый литерал.
* @en @brief Convert a `double` number to a string expression.
* @param t - number.
* @details Returns a string expression that generates the decimal representation of the given number.
* using `sprintf("%.16g")`. Can be used when you need to concatenate a number and a string literal.
*/
template<typename K> requires is_one_of_char_v<K>
inline constexpr expr_real<K> e_num(double t) {
return {t};
}
template<typename K, bool Ucase>
constexpr K hex_symbols[16] = {K('0'), K('1'), K('2'), K('3'), K('4'), K('5'), K('6'),
K('7'), K('8'), K('9'), K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'),
K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'), K(Ucase ? 'F' : 'f')};
template<typename K, typename Val, bool All, bool Ucase, bool Ox>
requires std::is_unsigned_v<Val>
struct expr_hex : expr_to_std_string<expr_hex<K, Val, All, Ucase, Ox>> {
using symb_type = K;
mutable Val v_;
mutable K buf_[sizeof(Val) * 2];
explicit constexpr expr_hex(Val v) : v_(v){}
constexpr size_t length() const noexcept {
K* ptr = buf_ + sizeof(Val) * 2;
Val value = v_;
size_t l = 0;
for (;;) {
*--ptr = hex_symbols<K, Ucase>[value & 0xF];
value >>= 4;
l++;
if (value) {
*--ptr = hex_symbols<K, Ucase>[value & 0xF];
value >>= 4;
l++;
}
if (!value) {
if constexpr (All) {
if (size_t need = sizeof(Val) * 2 - l) {
ch_traits<K>::assign(buf_, need, K('0'));
l = sizeof(Val) * 2;
}
}
break;
}
}
v_ = (Val)l;
return l + (Ox ? 2 : 0);
}
constexpr K* place(K* ptr) const noexcept {
if constexpr (Ox) {
*ptr++ = K('0');
*ptr++ = K('x');
}
ch_traits<K>::copy(ptr, buf_ + sizeof(Val) * 2 - v_, v_);
return ptr + v_;
}
};
template<typename Val, bool All, bool Ucase, bool Ox>
requires std::is_unsigned_v<Val>
struct expr_hex_src {
explicit constexpr expr_hex_src(Val v) : v_(v){}
Val v_;
};
/*!
* @ingroup StrExprs
* @ru @brief Флаги для функции e_hex.
* @en @brief Flags for the e_hex function.
*/
enum HexFlags : unsigned {
Short = 1, ///< without leading zeroes
No0x = 2, //< without 0x prefix
Lcase = 4, //< Use lower case
};
/*!
* @ingroup StrExprs
* @ru @brief Позволяет конкатенировать текст и беззнаковое число в 16-ричном виде.
* @tparam Flags - флаги форматирования, побитовое ИЛИ из HexFlags.
* @tparam T - тип числа, выводится автоматически.
* @en @brief Allows you to concatenate text and a unsigned number in hexadecimal.
* @tparam Flags - format flags, bitwise OR of HexFlags.
* @tparam T - number type, deducted automatically.
* @details @ru Пример @en Example @~
* ```cpp
* stringa text = +"val = "sv + e_hex(10u);
* EXPECT_EQ(text, "val = 0x0000000A");
*
* stringu textu = +u"val = 0X"sv + e_hex<HexFlags::No0x | HexFlags::Short | HexFlags::Lcase>(0x12Au);
* EXPECT_EQ(textu, u"val = 0X12a");
* ```
*/
template<unsigned Flags = 0, FromIntNumber T> requires std::is_unsigned_v<T>
constexpr auto e_hex(T v) {
return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для строкового выражения и 16ричного представления числа из e_hex().
* @param a - строковое выражение.
* @param b - e_hex(число).
* @en @brief Concatenation operator for a string expression and a hexadecimal representation of a number from e_hex().
* @param a is a string expression.
* @param b is e_hex(number).
*/
template<StrExpr A, typename Val, bool All, bool Ucase, bool Ox>
constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>, true> operator+(const A& a, const expr_hex_src<Val, All, Ucase, Ox>& b) {
return {a, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>{b.v_}};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для 16ричного представления числа из e_hex() и строкового выражения.
* @param a - e_hex(число).
* @param b - строковое выражение.
* @en @brief Concatenation operator for the hexadecimal representation of a number from e_hex() and a string expression.
* @param a - e_hex(number).
* @param b is a string expression.
*/
template<StrExpr A, typename Val, bool All, bool Ucase, bool Ox>
constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>, false> operator+(const expr_hex_src<Val, All, Ucase, Ox>& b, const A& a) {
return {a, expr_hex<typename A::symb_type, Val, All, Ucase, Ox>{b.v_}};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для строкового выражения и указателя, представляет его как 0xDEADBEEF.
* @param a - строковое выражение.
* @param b - указатель.
* @en @brief Concatenation operator for a string expression and a pointer, representing it as 0xDEADBEEF.
* @param a is a string expression.
* @param b is a pointer.
*/
template<StrExpr A>
constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, uintptr_t, true, true, true>, true> operator+(const A& a, const void* b) {
return {a, (uintptr_t)b};
}
/*!
* @ingroup StrExprs
* @ru @brief Оператор конкатенации для указателя и строкового выражения, представляет его как 0xDEADBEEF.
* @param a - указатель.
* @param b - строковое выражение.
* @en @brief Concatenation operator for a pointer and a string expression, representing it as 0xDEADBEEF.
* @param a - pointer.
* @param b is a string expression.
*/
template<StrExpr A>
constexpr strexprjoin_c<A, expr_hex<typename A::symb_type, uintptr_t, true, true, true>, false> operator+(const void* b, const A& a) {
return {a, (uintptr_t)b};
}
template<typename K, StrExprForType<K> A, bool Left>
struct expr_fill : expr_to_std_string<expr_fill<K, A, Left>>{
using symb_type = K;
K symbol_;
size_t width_;
const A& a_;
mutable size_t alen_{};
constexpr expr_fill(K symbol, size_t width, const A& a) : symbol_(symbol), width_(width), a_(a){}
constexpr size_t length() const noexcept {
alen_ = a_.length();
return std::max(alen_, width_);
}
constexpr K* place(K* ptr) const noexcept {
if (alen_ >= width_) {
return (K*)a_.place((typename A::symb_type*)ptr);
}
size_t w = width_ - alen_;
if constexpr (Left) {
ch_traits<K>::assign(ptr, w, symbol_);
ptr += w;
return (K*)a_.place((typename A::symb_type*)ptr);
} else {
ptr = (K*)a_.place((typename A::symb_type*)ptr);
ch_traits<K>::assign(ptr, w, symbol_);
return ptr + w;
}
}
};
/*!
* @ingroup StrExprs
* @ru @brief Создает выражение, которое дополняет указанное строковое выражение до заданной длины
* заданным символом слева.
* @param width - дополнять до этой ширины
* @param symbol - символ для заполнения
* @details Если строковое выражение выдаёт строку короче заданной длины, добавляет перед ней
* указанный символ, дополняя до нужной длины. Не обрезает строку до указанной длины.
* Будьте внимательны, длина берётся в code units, не в code points, а символ заполнения
* не может быть суррогатным, то есть занимать более одного code unit. Если надо быть точным
* с Unicode-символами - используйте конвертацию в char32_t и обратно.
* @en @brief Creates an expression that expands the specified string expression to the specified length
* given character to the left.
* @param width - pad to this width
* @param symbol - symbol to fill
* @details If a string expression produces a string shorter than the given length, prepend it with
* the specified character, padding to the desired length. Does not truncate the string to the specified length.
* Be careful, the length is taken in code units, not in code points, and the padding character
* cannot be a surrogate, that is, occupy more than one code unit. If you have to be precise
* with Unicode characters - use conversion to char32_t and vice versa.
*/
template<StrExpr A, typename K = typename A::symb_type>
expr_fill<K, A, true> e_fill_left(const A& a, size_t width, K symbol = K(' ')) {
return {symbol, width, a};
}
/*!
* @ingroup StrExprs
* @ru @brief Создает выражение, которое дополняет указанное строковое выражение до заданной длины
* заданным символом справа.
* @param width - дополнять до этой ширины
* @param symbol - символ для заполнения
* @details Если строковое выражение выдаёт строку короче заданной длины, добавляет к ней
* указанный символ, дополняя до нужной длины. Не обрезает строку до указанной длины.
* Будьте внимательны, длина берётся в code units, не в code points, а символ заполнения
* не может быть суррогатным, то есть занимать более одного code unit. Если надо быть точным
* с Unicode-символами - используйте конвертацию в char32_t и обратно.
* @en @brief Creates an expression that expands the specified string expression to the specified length
* given character to the right.
* @param width - pad to this width
* @param symbol - symbol to fill
* @details If a string expression produces a string shorter than the given length, appends it
* the specified character, padding to the desired length. Does not truncate the string to the specified length.
* Be careful, the length is taken in code units, not in code points, and the padding character
* cannot be a surrogate, that is, occupy more than one code unit. If you have to be precise
* with Unicode characters - use conversion to char32_t and vice versa.
*/
template<StrExpr A, typename K = typename A::symb_type>
expr_fill<K, A, false> e_fill_right(const A& a, size_t width, K symbol = K(' ')) {
return {symbol, width, a};
}
/*
* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
* K - тип символов строки
* T - тип контейнера строк (vector, list)
* I - длина разделителя в символах
* tail - добавлять разделитель после последнего элемента контейнера.
* Если контейнер пустой, разделитель в любом случае не добавляется
* skip_empty - пропускать пустые строки без добавления разделителя
* To create string concatenations with vectors and lists joined by a constant delimiter
* K is the symbols
* T - type of string container (vector, list)
* I - length of separator in characters
* tail - add a separator after the last element of the container.
* If the container is empty, the separator is not added anyway
* skip_empty - skip empty lines without adding a separator
*/
template<typename K, typename T, size_t I, bool tail, bool skip_empty>
struct expr_join : expr_to_std_string<expr_join<K, T, I, tail, skip_empty>> {
using symb_type = K;
using my_type = expr_join<K, T, I, tail, skip_empty>;
const T& s;
const K* delim;
constexpr expr_join(const T& _s, const K* _delim) : s(_s), delim(_delim){}
constexpr size_t length() const noexcept {
size_t l = 0;
for (const auto& t: s) {
size_t len = t.length();
if (len > 0 || !skip_empty) {
if (I > 0 && l > 0) {
l += I;
}
l += len;
}
}
return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
}
constexpr K* place(K* ptr) const noexcept {
if (s.empty()) {
return ptr;
}
K* write = ptr;
for (const auto& t: s) {
size_t copyLen = t.length();
if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
ch_traits<K>::copy(write, delim, I);
write += I;
}
ch_traits<K>::copy(write, t.data(), copyLen);
write += copyLen;
}
if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
ch_traits<K>::copy(write, delim, I);
write += I;
}
return write;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Получить строковое выражение, конкатенирующее строки в контейнере в одну строку с заданным разделителем.
* @tparam tail - добавлять ли разделитель после последней строки.
* @tparam skip_empty - пропускать пустые строки без добавления разделителя.
* @param s - контейнер со строками, должен поддерживать `range for`.
* @param d - разделитель, строковый литерал.
* @en @brief Get a string expression concatenating the strings in the container into a single string with the given delimiter.limiter.limiter.
* @tparam tail - whether to add a separator after the last line.
* @tparam skip_empty - skip empty lines without adding a separator.
* @param s - container with strings, must support `range for`.
* @param d - delimiter, string literal.
*/
template<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>
inline constexpr auto e_join(const T& s, L&& d) {
return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
}
template<size_t N>
concept is_const_pattern = N > 1 && N <= 17;
template<typename K, size_t I>
struct _ascii_mask { // NOLINT
constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
};
template<typename K>
struct _ascii_mask<K, 0> {
constexpr static const size_t value = 0;
};
template<typename K>
struct ascii_mask { // NOLINT
using uns = std::make_unsigned_t<K>;
constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
};
template<typename K>
constexpr inline bool isAsciiUpper(K k) {
return k >= 'A' && k <= 'Z';
}
template<typename K>
constexpr inline bool isAsciiLower(K k) {
return k >= 'a' && k <= 'z';
}
template<typename K>
constexpr inline K makeAsciiLower(K k) {
return isAsciiUpper(k) ? k | 0x20 : k;
}
template<typename K>
constexpr inline K makeAsciiUpper(K k) {
return isAsciiLower(k) ? k & ~0x20 : k;
}
enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
template<TrimSides S, typename K, size_t N, bool withSpaces = false>
struct trim_operator;
template<size_t I>
struct digits_selector {
using wider_type = uint16_t;
};
template<>
struct digits_selector<2> {
using wider_type = uint32_t;
};
template<>
struct digits_selector<4> {
using wider_type = uint64_t;
};
/*!
* @ru @brief Перечисление с возможными результатами преобразования строки в целое число
* @en @brief Enumeration with possible results of converting a string to an integer
*/
enum class IntConvertResult : char {
Success, //!< Успешно
BadSymbolAtTail, //!< Число закончилось не числовым символом
Overflow, //!< Переполнение, число не помещается в заданный тип
NotNumber //!< Вообще не число
};
template<bool CanNegate, bool CheckOverflow, typename T>
struct result_type_selector { // NOLINT
using type = T;
};
template<typename T>
struct result_type_selector<true, false, T> {
using type = std::make_unsigned_t<T>;
};
template<unsigned Base>
constexpr unsigned digit_width() {
if (Base <=2) {
return 1;
}
if (Base <= 4) {
return 2;
}
if (Base <= 8) {
return 3;
}
if (Base <= 16) {
return 4;
}
if (Base <= 32) {
return 5;
}
return 6;
}
template<typename T, unsigned Base>
constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
template<typename T>
struct convert_result {
T value;
IntConvertResult ec;
size_t read;
};
struct int_convert { // NOLINT
inline static const uint8_t NUMBERS[] = {
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,
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,
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,
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,
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,
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,
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,
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,
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,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
template<typename K, unsigned Base>
static constexpr std::make_unsigned_t<K> toDigit(K s) {
auto us = static_cast<std::make_unsigned_t<K>>(s);
if constexpr (Base <= 10) {
return us - '0';
} else {
if constexpr (sizeof(K) == 1) {
return NUMBERS[us];
} else {
return us < 256 ? NUMBERS[us] : us;
}
}
}
template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
requires(Base != 0)
static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
using u_type = std::make_unsigned_t<T>;
#ifndef HAS_BUILTIN_OVERFLOW
u_type maxMult = 0, maxAdd = 0;
if constexpr (CheckOverflow) {
maxMult = std::numeric_limits<u_type>::max() / Base;
maxAdd = std::numeric_limits<u_type>::max() % Base;
}
#endif
u_type number = 0;
unsigned maxDigits = max_overflow_digits<u_type, Base>;
IntConvertResult error = IntConvertResult::NotNumber;
const K* from = current;
bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
if (no_need_check_o_f) {
for (;;) {
const u_type digit = toDigit<K, Base>(*current);
if (digit >= Base) {
break;
}
number = number * Base + digit;
if (++current == end) {
error = IntConvertResult::Success;
break;
}
}
} else {
for (;maxDigits; maxDigits--) {
const u_type digit = toDigit<K, Base>(*current);
if (digit >= Base) {
break;
}
number = number * Base + digit;
++current;
}
if (!maxDigits) {
// Прошли все цифры, дальше надо с проверкой на overflow
// All numbers have passed, then we need to check for overflow
for (;;) {
const u_type digit = toDigit<K, Base>(*current);
if (digit >= Base) {
break;
}
#ifdef HAS_BUILTIN_OVERFLOW
if (__builtin_mul_overflow(number, Base, &number) ||
__builtin_add_overflow(number, digit, &number)) {
#else
if (number < maxMult || (number == maxMult && number < maxAdd)) {
number = number * Base + digit;
} else {
#endif
error = IntConvertResult::Overflow;
while(++current < end) {
if (toDigit<K, Base>(*current) >= Base) {
break;
}
}
break;
}
if (++current == end) {
error = IntConvertResult::Success;
break;
}
}
}
}
T result;
if constexpr (std::is_signed_v<T>) {
result = negate ? 0 - number : number;
if constexpr (CheckOverflow) {
if (error != IntConvertResult::Overflow) {
if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
error = IntConvertResult::Overflow;
}
}
}
} else {
result = number;
}
if (error == IntConvertResult::NotNumber && current > from) {
error = IntConvertResult::BadSymbolAtTail;
}
return {result, error, size_t(current - start)};
}
public:
// Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
// Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
// If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
// If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
requires(Base == -1 || (Base < 37 && Base != 1))
static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
const K *ptr = start, *end = ptr + len;
bool negate = false;
if constexpr (SkipWs) {
while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
ptr++;
}
if (ptr != end) {
if constexpr (std::is_signed_v<T>) {
if constexpr (AllowSign) {
// Может быть число, +число или -число
// Can be a number, +number or -number
if (*ptr == '+') {
ptr++;
} else if (*ptr == '-') {
negate = true;
ptr++;
}
} else {
// Может быть число или -число
// Can be a number or -number
if (*ptr == '-') {
negate = true;
ptr++;
}
}
} else if constexpr (AllowSign) {
// Может быть число или +число
// Can be a number or +number
if (*ptr == '+') {
ptr++;
}
}
}
if (ptr != end) {
if constexpr (Base == 0 || Base == -1) {
if (*ptr == '0') {
ptr++;
if (ptr != end) {
if (*ptr == 'x' || *ptr == 'X') {
return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
}
if constexpr (Base == -1) {
if (*ptr == 'b' || *ptr == 'B') {
return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
}
if (*ptr == 'o' || *ptr == 'O') {
return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
}
}
return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
}
return {0, IntConvertResult::Success, size_t(ptr - start)};
}
return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
} else
return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
}
return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
}
};
template<typename K, typename Impl>
class null_terminated {
public:
/*!
* @ru @brief Получить указатель на константный буфер символов строки
* @return const K* - указатель на константный буфер символов строки
* @en @brief Get a pointer to a constant character buffer of a string
* @return const K* - pointer to a constant string character buffer
*/
constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
};
template<typename K, typename Impl, bool Mutable> class buffer_pointers;
/*!
* @ru @brief Базовый класс для строкового буфера.
* @tparam K - тип символов.
* @tparam Impl - класс реализации.
* @en @brief Base class for a string buffer.
* @tparam K - character type.
* @tparam Impl - implementation class.
*/
template<typename K, typename Impl>
class buffer_pointers<K, Impl, false> {
constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
public:
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* data() const { return d().symbols(); }
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* begin() const { return d().symbols(); }
/*!
* @ru @brief Указатель на константный символ после после последнего символа строки.
* @return const K* - конец строки.
* @en @brief Pointer to a constant character after the last character of the string.
* @return const K* - end of line.
*/
constexpr const K* end() const { return d().symbols() + d().length(); }
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* cbegin() const { return d().symbols(); }
/*!
* @ru @brief Указатель на константный символ после после последнего символа строки.
* @return const K* - конец строки.
* @en @brief Pointer to a constant character after the last character of the string.
* @return const K* - end of line.
*/
constexpr const K* cend() const { return d().symbols() + d().length(); }
};
template<typename K, typename Impl>
class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
constexpr Impl& d() { return *static_cast<Impl*>(this); }
using base = buffer_pointers<K, Impl, false>;
public:
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* data() const { return base::data(); }
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* begin() const { return base::begin(); }
/*!
* @ru @brief Указатель на константный символ после после последнего символа строки.
* @return const K* - конец строки.
* @en @brief Pointer to a constant character after the last character of the string.
* @return const K* - end of line.
*/
constexpr const K* end() const { return base::end(); }
/*!
* @ru @brief Получить указатель на константный буфер символов строки.
* @return const K* - указатель на константный буфер символов строки.
* @en @brief Get a pointer to a constant character buffer of a string.
* @return const K* - pointer to a constant buffer of string characters.
*/
constexpr const K* cbegin() const { return base::cbegin(); }
/*!
* @ru @brief Указатель на константный символ после после последнего символа строки.
* @return const K* - конец строки.
* @en @brief Pointer to a constant character after the last character of the string.
* @return const K* - end of line.
*/
constexpr const K* cend() const { return base::cend(); }
/*!
* @ru @brief Получить указатель на буфер символов строки.
* @return K* - указатель на буфер символов строки.
* @en @brief Get a pointer to the string's character buffer.
* @return K* - pointer to a string character buffer.
*/
constexpr K* data() { return d().str(); }
/*!
* @ru @brief Получить указатель на буфер символов строки.
* @return K* - указатель на буфер символов строки.
* @en @brief Get a pointer to the string's character buffer.
* @return K* - pointer to a string character buffer.
*/
constexpr K* begin() { return d().str(); }
/*!
* @ru @brief Указатель на символ после после последнего символа строки.
* @return K* - конец строки.
* @en @brief Pointer to the character after the last character of the string.
* @return K* - end of line.
*/
constexpr K* end() { return d().str() + d().length(); }
};
/*!
* @ru @brief Класс для последовательного получения подстрок по заданному разделителю.
* @tparam K - тип символов.
* @en @brief Class for sequentially obtaining substrings by a given delimiter.
* @tparam K - character type.
*/
template<typename K, typename StrSrc>
class SplitterBase {
using str_t = StrSrc;
str_t text_;
str_t delim_;
public:
constexpr SplitterBase(str_t text, str_t delim) : text_(text), delim_(delim) {}
/*!
* @ru @brief Узнать, не закончились ли подстроки.
* @en @brief Find out if substrings are running out.
*/
constexpr bool is_done() const {
return text_.length() == str::npos;
}
/*!
* @ru @brief Получить следующую подстроку.
* @return simple_str.
* @en @brief Get the next substring.
* @return simple_str.
*/
constexpr str_t next() {
if (!text_.length()) {
auto ret = text_;
text_.str++;
text_.len--;
return ret;
} else if (text_.length() == str::npos) {
return {nullptr, 0};
}
size_t pos = text_.find(delim_), next = 0;
if (pos == str::npos) {
pos = text_.length();
next = pos + 1;
} else {
next = pos + delim_.length();
}
str_t result{text_.str, pos};
text_.str += next;
text_.len -= next;
return result;
}
};
/*!
* @ru @brief Класс с базовыми константными строковыми алгоритмами.
* @details Является базой для классов, могущих выполнять константные операции со строками.
* Ничего не знает о хранении строк, ни сам, ни у класса наследника, то есть работает
* только с указателем на строку и её длиной.
* Для работы класс-наследник должен реализовать методы:
* - size_t length() const noexcept - возвращает длину строки.
* - const K* symbols() const noexcept - возвращает указатель на начало строки.
* - bool is_empty() const noexcept - проверка, не пустая ли строка.
* @tparam K - тип символов.
* @tparam StrRef - тип хранилища куска строки.
* @tparam Impl - конечный класс наследник.
* @en @brief A class with basic constant string algorithms.
* @details Is the base for classes that can perform constant operations on strings.
* Doesnt know anything about storing strings, neither itself nor the descendant class, that is, it works
* only with a pointer to a string and its length.
* To work, the descendant class must implement the following methods:
* - size_t length() const noexcept - returns the length of the string.
* - const K* symbols() const noexcept - returns a pointer to the beginning of the line.
* - bool is_empty() const noexcept - checks whether the string is empty.
* @tparam K - character type.
* @tparam StrRef - storage type for the string chunk.
* @tparam Impl - the final class is the successor.
*/
template<typename K, typename StrRef, typename Impl, bool Mutable>
class str_src_algs : public buffer_pointers<K, Impl, Mutable> {
constexpr const Impl& d() const noexcept {
return *static_cast<const Impl*>(this);
}
constexpr size_t _len() const noexcept {
return d().length();
}
constexpr const K* _str() const noexcept {
return d().symbols();
}
constexpr bool _is_empty() const noexcept {
return d().is_empty();
}
public:
using symb_type = K;
using str_piece = StrRef;
using traits = ch_traits<K>;
using uns_type = std::make_unsigned_t<K>;
using my_type = Impl;
using base = str_src_algs<K, StrRef, Impl, Mutable>;
str_src_algs() = default;
/*!
* @ru @brief Копировать строку в указанный буфер.
* @details Метод предполагает, что размер выделенного буфера достаточен для всей строки, т.е.
* предварительно была запрошена `length()`. Не добавляет `\0`.
* @param ptr - указатель на буфер.
* @return указатель на символ после конца размещённой в буфере строки.
* @en @brief Copy the string to the specified buffer.
* @details The method assumes that the size of the allocated buffer is sufficient for the entire line, i.e.
* `length()` was previously requested. Does not add `\0`.
* @param ptr - pointer to the buffer.
* @return pointer to the character after the end of the symbols placed in the buffer.
*/
constexpr K* place(K* ptr) const noexcept {
size_t myLen = _len();
traits::copy(ptr, _str(), myLen);
return ptr + myLen;
}
/*!
* @ru @brief Копировать строку в указанный буфер.
* @details Метод добавляет `\0` после скопированных символов. Не выходит за границы буфера.
* @param buffer - указатель на буфер
* @param bufSize - размер буфера в символах.
* @en @brief Copy the string to the specified buffer.
* @details The method adds `\0` after the copied characters. Does not exceed buffer boundaries.
* @param buffer - pointer to buffer
* @param bufSize - buffer size in characters.
*/
void copy_to(K* buffer, size_t bufSize) {
size_t tlen = std::min(_len(), bufSize - 1);
traits::copy(buffer, _str(), tlen);
buffer[tlen] = 0;
}
/*!
* @ru @brief Размер строки в символах.
* @return size_t
* @en @brief The size of the string in characters.
* @return size_t
*/
constexpr size_t size() const {
return _len();
}
/*!
* @ru @brief Конвертировать в std::basic_string_view.
* @return std::basic_string_view<K>.
* @en @brief Convert to std::basic_string_view.
* @return std::basic_string_view<K>.
*/
template<typename D = K> requires is_equal_str_type_v<K, D>
constexpr std::basic_string_view<D> to_sv() const noexcept {
return {(const D*)_str(), _len()};
}
/*!
* @ru @brief Конвертировать в std::basic_string_view.
* @return std::basic_string_view<K>.
* @en @brief Convert to std::basic_string_view.
* @return std::basic_string_view<K>.
*/
template<typename D, typename Traits> requires is_equal_str_type_v<K, D>
constexpr operator std::basic_string_view<D, Traits>() const {
return {(const D*)_str(), _len()};
}
/*!
* @ru @brief Конвертировать в std::basic_string.
* @return std::basic_string<K>.
* @en @brief Convert to std::basic_string.
* @return std::basic_string<K>.
*/
template<typename D = K, typename Traits = std::char_traits<D>, typename Allocator = std::allocator<D>> requires is_equal_str_type_v<K, D>
constexpr std::basic_string<D, Traits, Allocator> to_string() const {
return {(const D*)_str(), _len()};
}
/*!
* @ru @brief Конвертировать в std::basic_string.
* @return std::basic_string<K>.
* @en @brief Convert to std::basic_string.
* @return std::basic_string<K>.
*/
template<typename D, typename Traits, typename Allocator> requires is_equal_str_type_v<K, D>
constexpr operator std::basic_string<D, Traits, Allocator>() const {
return {(const D*)_str(), _len()};
}
/*!
* @ru @brief Преобразовать себя в "кусок строки", включающий всю строку.
* @return str_piece.
* @en @brief Convert itself to a "string chunk" that includes the entire string.
* @return str_piece.
*/
constexpr operator str_piece() const noexcept {
return str_piece{_str(), _len()};
}
/*!
* @ru @brief Преобразовать себя в "кусок строки", включающий всю строку.
* @return str_piece.
* @en @brief Convert itself to a "string chunk" that includes the entire string.
* @return str_piece.
*/
constexpr str_piece to_str() const noexcept {
return {_str(), _len()};
}
/*!
* @ru @brief Получить часть строки как "str_src".
* @param from - количество символов от начала строки.
* @param len - количество символов в получаемом "куске".
* @return Подстроку, str_src.
* @details Если `from` меньше нуля, то отсчитывается `-from` символов от конца строки в сторону начала.
* Если `len` меньше или равно нулю, то отсчитать `-len` символов от конца строки
* @en @brief Get part of a string as "str_src".
* @param from - number of characters from the beginning of the line.
* @param len - the number of characters in the resulting "chunk".
* @return Substring, str_src.
* @details If `from` is less than zero, then `-from` characters are counted from the end of the line towards the beginning.
* If `len` is less than or equal to zero, then count `-len` characters from the end of the line
* @~
* ```cpp
* "0123456789"_ss(5, 2) == "56";
* "0123456789"_ss(5) == "56789";
* "0123456789"_ss(5, -1) == "5678";
* "0123456789"_ss(-3) == "789";
* "0123456789"_ss(-3, 2) == "78";
* "0123456789"_ss(-4, -1) == "678";
* ```
*/
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
size_t myLen = _len(), idxStart = from >= 0 ? from : myLen > -from ? myLen + from : 0,
idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
if (idxEnd > myLen)
idxEnd = myLen;
if (idxStart > idxEnd)
idxStart = idxEnd;
return str_piece{_str() + idxStart, idxEnd - idxStart};
}
/*!
* @ru @brief Получить часть строки как "кусок строки".
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
* @param len - количество символов в получаемом "куске". При выходе за пределы строки вернёт всё до конца строки.
* @return Подстроку, str_src.
* @en @brief Get part of a string as "string chunk".
* @param from - number of characters from the beginning of the line. If the string size is exceeded, it will return an empty string.
* @param len - the number of characters in the resulting "chunk". When going beyond the line, it will return everything up to the end of the line.
* @return Substring, str_src.
*/
constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
if (idxEnd > myLen)
idxEnd = myLen;
if (idxStart > idxEnd)
idxStart = idxEnd;
return str_piece{_str() + idxStart, idxEnd - idxStart};
}
/*!
* @ru @brief Получить подстроку str_src с позиции от from до позиции to (не включая её).
* @details Для производительности метод никак не проверяет выходы за границы строки, используйте
* в сценариях, когда точно знаете, что это позиции внутри строки и to >= from.
* @param from - начальная позиция.
* @param to - конечная позиция (не входит в результат).
* @return Подстроку, str_src.
* @en @brief Get the substring str_src from position from to position to (not including it).
* @details For performance reasons, the method does not check for line boundaries in any way, use
* in scenarios when you know for sure that these are positions inside the line and to >= from.
* @param from - starting position.
* @param to - final position (not included in the result).
* @return Substring, str_src.
*/
constexpr str_piece from_to(size_t from, size_t to) const noexcept {
return str_piece{_str() + from, to - from};
}
/*!
* @ru @brief Проверка на пустоту.
* @en @brief Check for emptiness.
*/
constexpr bool operator!() const noexcept {
return _is_empty();
}
/*!
* @ru @brief Получить символ на заданной позиции .
* @param idx - индекс символа. Для отрицательных значений отсчитывается от конца строки.
* @return K - символ.
* @details Не производит проверку на выход за границы строки.
* @en @brief Get the character at the given position.
* @param idx - symbol index. For negative values, it is counted from the end of the line.
* @return K - character.
* @details Does not check for line boundaries.
*/
constexpr K at(ptrdiff_t idx) const {
return _str()[idx >= 0 ? idx : _len() + idx];
}
// Сравнение строк
// String comparison
constexpr int compare(const K* text, size_t len) const {
size_t myLen = _len();
int cmp = traits::compare(_str(), text, std::min(myLen, len));
return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
}
/*!
* @ru @brief Сравнение строк посимвольно.
* @param o - другая строка.
* @return <0 эта строка меньше, ==0 - строки равны, >0 - эта строка больше.
* @en @brief Compare strings character by character.
* @param o - another line.
* @return <0 this string is less, ==0 - strings are equal, >0 - this string is greater.
*/
constexpr int compare(str_piece o) const {
return compare(o.symbols(), o.length());
}
/*!
* @ru @brief Сравнение с C-строкой посимвольно.
* @param text - другая строка.
* @return <0 эта строка меньше, ==0 - строки равны, >0 - эта строка больше.
* @en @brief Compare with C-string character by character.
* @param text - another line.
* @return <0 this string is less, ==0 - strings are equal, >0 - this string is greater.
*/
constexpr int strcmp(const K* text) const {
size_t myLen = _len(), idx = 0;
const K* ptr = _str();
for (; idx < myLen; idx++) {
uns_type s1 = (uns_type)text[idx];
if (!s1) {
return 1;
}
uns_type s2 = (uns_type)ptr[idx];
if (s1 < s2) {
return 1;
} else if (s1 > s2) {
return -1;
}
}
return text[idx] == 0 ? 0 : -1;
}
constexpr bool equal(const K* text, size_t len) const noexcept {
return len == _len() && traits::compare(_str(), text, len) == 0;
}
/*!
* @ru @brief Сравнение строк на равенство.
* @param other - другая строка.
* @return равны ли строки.
* @en @brief String comparison for equality.
* @param other - another line.
* @return whether the strings are equal.
*/
constexpr bool equal(str_piece other) const noexcept {
return equal(other.symbols(), other.length());
}
/*!
* @ru @brief Оператор сравнение строк на равенство.
* @param other - другая строка.
* @return равны ли строки.
* @en @brief Operator comparing strings for equality.
* @param other - another line.
* @return whether the strings are equal.
*/
constexpr bool operator==(const base& other) const noexcept {
return equal(other._str(), other._len());
}
/*!
* @ru @brief Оператор сравнения строк.
* @param other - другая строка.
* @en @brief String comparison operator.
* @param other - another line.
*/
constexpr auto operator<=>(const base& other) const noexcept {
return compare(other._str(), other._len()) <=> 0;
}
/*!
* @ru @brief Оператор сравнения строки и строкового литерала на равенство.
* @param other - строковый литерал.
* @en @brief Operator for comparing a string and a string literal for equality.
* @param other - string literal.
*/
template<typename T, size_t N = const_lit_for<K, T>::Count>
constexpr bool operator==(T&& other) const noexcept {
return N - 1 == _len() && traits::compare(_str(), other, N - 1) == 0;
}
/*!
* @ru @brief Оператор сравнения строки и строкового литерала.
* @param other - строковый литерал.
* @en @brief Comparison operator between a string and a string literal.
* @param other is a string literal.
*/
template<typename T, size_t N = const_lit_for<K, T>::Count>
constexpr auto operator<=>(T&& other) const noexcept {
size_t myLen = _len();
int cmp = traits::compare(_str(), other, std::min(myLen, N - 1));
int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
return res <=> 0;
}
// Сравнение ascii строк без учёта регистра
// Compare ascii strings without taking into account case
constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
if (!len)
return _is_empty() ? 0 : 1;
size_t myLen = _len(), checkLen = std::min(myLen, len);
const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
while (checkLen--) {
uns_type s1 = *ptr1++, s2 = *ptr2++;
if (s1 == s2)
continue;
s1 = makeAsciiLower(s1);
s2 = makeAsciiLower(s2);
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
}
return myLen == len ? 0 : myLen > len ? 1 : -1;
}
/*!
* @ru @brief Сравнение строк посимвольно без учёта регистра ASCII символов.
* @param text - другая строка.
* @return <0 эта строка меньше, ==0 - строки равны, >0 - эта строка больше.
* @en @brief Compare strings character by character and not case sensitive ASCII characters.
* @param text - another line.
* @return <0 this string is less, ==0 - strings are equal, >0 - this string is greater.
*/
constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
return compare_ia(text.symbols(), text.length());
}
/*!
* @ru @brief Равна ли строка другой строке посимвольно без учёта регистра ASCII символов.
* @param text - другая строка.
* @return равны ли строки.
* @en @brief Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
* @param text - another line.
* @return whether the strings are equal.
*/
constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
}
/*!
* @ru @brief Меньше ли строка другой строки посимвольно без учёта регистра ASCII символов.
* @param text - другая строка.
* @return меньше ли строка.
* @en @brief Whether a string is smaller than another string, character-by-character-insensitive, ASCII characters.
* @param text - another line.
* @return whether the string is smaller.
*/
constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
return compare_ia(text.symbols(), text.length()) < 0;
}
constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
size_t lenText = _len();
// Образец, не вмещающийся в строку и пустой образец не находим
// We don't look for an empty line or a line longer than the text.
if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
return str::npos;
lenPattern--;
const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
pattern++;
for (const K* fnd = text + offset;; ++fnd) {
fnd = traits::find(fnd, last - fnd, first);
if (!fnd)
return str::npos;
if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
return static_cast<size_t>(fnd - text);
}
}
/*!
* @ru @brief Найти начало первого вхождения подстроки в этой строке.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию начала вхождения подстроки, или -1, если не найдена.
* @en @brief Find the beginning of the first occurrence of a substring in this string.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @return size_t - the position of the beginning of the occurrence of the substring, or -1 if not found.
*/
constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
return find(pattern.symbols(), pattern.length(), offset);
}
/*!
* @ru @brief Найти начало первого вхождения подстроки в этой строке или выкинуть исключение.
* @tparam Exc - тип исключения.
* @tparam Args... - типы параметров для конструирования исключения, выводятся из аргументов.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @param args - аргументы для конструктора исключения.
* @return size_t - позицию начала вхождения подстроки, или выбрасывает исключение Exc, если не найдена.
* @en @brief Find the beginning of the first occurrence of a substring in this string or throw an exception.
* @tparam Exc - exception type.
* @tparam Args... - types of parameters for constructing an exception, inferred from the arguments.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @param args - arguments for the exception constructor.
* @return size_t - the position of the beginning of the substring occurrence, or throws an Exc exception if not found.
*/
template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
return fnd;
}
throw Exc(std::forward<Args>(args)...);
}
/*!
* @ru @brief Найти конец вхождения подстроки в этой строке.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию сразу за вхождением подстроки, или -1, если не найдена.
* @en @brief Find the end of the occurrence of a substring in this string.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @return size_t - the position immediately after the occurrence of the substring, or -1 if not found.
*/
constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
size_t fnd = find(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? fnd : fnd + pattern.length();
}
/*!
* @ru @brief Найти начало первого вхождения подстроки в этой строке или конец строки.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию начала вхождения подстроки, или длину строки, если не найдена.
* @en @brief Find the beginning of the first occurrence of a substring in this string or the end of the string.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @return size_t - the position at which the substring begins, or the length of the string if not found.
*/
constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
auto fnd = find(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? _len() : fnd;
}
/*!
* @ru @brief Найти конец первого вхождения подстроки в этой строке или конец строки.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию сразу за вхождением подстроки, или длину строки, если не найдена.
* @en @brief Find the end of the first occurrence of a substring in this string, or the end of a string.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @return size_t - the position immediately after the occurrence of the substring, or the length of the string if not found.
*/
constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
auto fnd = find(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? _len() : fnd + pattern.length();
}
constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
if (lenPattern == 1)
return find_last(pattern[0], offset);
size_t lenText = std::min(_len(), offset);
// Образец, не вмещающийся в строку и пустой образец не находим
// We don't look for an empty line or a line longer than the text.
if (!lenPattern || lenPattern > lenText)
return str::npos;
lenPattern--;
const K *text = _str() + lenPattern, last = pattern[lenPattern];
lenText -= lenPattern;
while(lenText) {
if (text[--lenText] == last) {
if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
return lenText;
}
}
}
return str::npos;
}
/*!
* @ru @brief Найти начало последнего вхождения подстроки в этой строке.
* @param pattern - искомая строка.
* @param offset - c какой позиции вести поиск в обратную сторону, -1 - с самого конца.
* @return size_t - позицию начала вхождения подстроки, или -1, если не найдена.
* @en @brief Find the beginning of the last occurrence of a substring in this string.
* @param pattern - the search string.
* @param offset - from which position to search in the opposite direction, -1 - from the very end.
* @return size_t - the position of the beginning of the occurrence of the substring, or -1 if not found.
*/
constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
return find_last(pattern.symbols(), pattern.length(), offset);
}
/*!
* @ru @brief Найти конец последнего вхождения подстроки в этой строке.
* @param pattern - искомая строка.
* @param offset - c какой позиции вести поиск в обратную сторону, -1 - с самого конца.
* @return size_t - позицию сразу за последним вхождением подстроки, или -1, если не найдена.
* @en @brief Find the end of the last occurrence of a substring in this string.
* @param pattern - the search string.
* @param offset - from which position to search in the opposite direction, -1 - from the very end.
* @return size_t - the position immediately after the last occurrence of the substring, or -1 if not found.
*/
constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? fnd : fnd + pattern.length();
}
/*!
* @ru @brief Найти начало последнего вхождения подстроки в этой строке или конец строки.
* @param pattern - искомая строка.
* @param offset - c какой позиции вести поиск в обратную сторону, -1 - с самого конца.
* @return size_t - позицию начала вхождения подстроки, или длину строки, если не найдена.
* @en @brief Find the beginning of the last occurrence of a substring in this string or the end of the string.
* @param pattern - the search string.
* @param offset - from which position to search in the opposite direction, -1 - from the very end.
* @return size_t - the position at which the substring begins, or the length of the string if not found.
*/
constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? _len() : fnd;
}
/*!
* @ru @brief Найти конец последнего вхождения подстроки в этой строке или конец строки.
* @param pattern - искомая строка.
* @param offset - c какой позиции вести поиск в обратную сторону, -1 - с самого конца.
* @return size_t - позицию сразу за последним вхождением подстроки, или длину строки, если не найдена.
* @en @brief Find the end of the last occurrence of a substring in this string, or the end of a string.
* @param pattern - the search string.
* @param offset - from which position to search in the opposite direction, -1 - from the very end.
* @return size_t - the position immediately after the last occurrence of the substring, or the length of the string if not found.
*/
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
return fnd == str::npos ? _len() : fnd + pattern.length();
}
/*!
* @ru @brief Содержит ли строка указанную подстроку.
* @param pattern - искомая строка.
* @param offset - с какой позиции начинать поиск.
* @return bool.
* @en @brief Whether the string contains the specified substring.
* @param pattern - the search string.
* @param offset - from which position to start the search.
* @return bool.
*/
constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
return find(pattern, offset) != str::npos;
}
/*!
* @ru @brief Найти символ в этой строке.
* @param s - искомый символ.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию найденного символа, или -1, если не найден.
* @en @brief Find a character in this string.
* @param s is an optional character.
* @param offset - from which position to start the search.
* @return size_t - position of the found character, or -1 if not found.
*/
constexpr size_t find(K s, size_t offset = 0) const noexcept {
size_t len = _len();
if (offset < len) {
const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
if (fnd)
return static_cast<size_t>(fnd - str);
}
return str::npos;
}
/*!
* @ru @brief Найти символ в этой строке или конец строки.
* @param s - искомый символ.
* @param offset - с какой позиции начинать поиск.
* @return size_t - позицию найденного символа, или длину строки, если не найден.
* @en @brief Find a character in this string or the end of a string.
* @param s is an optional character.
* @param offset - from which position to start the search.
* @return size_t - position of the found character, or string length if not found.
*/
constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
size_t len = _len();
if (offset < len) {
const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
if (fnd)
return static_cast<size_t>(fnd - str);
}
return len;
}
template<typename Op>
constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
if (!maxCount)
maxCount--;
while (maxCount-- > 0) {
size_t fnd = find(pattern, patternLen, offset);
if (fnd == str::npos)
break;
op(fnd);
offset = fnd + patternLen;
}
}
/*!
* @ru @brief Вызвать функтор для всех найденных вхождений подстроки в этой строке.
* @param op - функтор, принимающий строку.
* @param pattern - искомая подстрока.
* @param offset - позиция начала поиска.
* @param maxCount - максимальное количество обрабатываемых вхождений, 0 - без ограничений.
* @en @brief Call a functor on all found occurrences of a substring in this string.
* @param op is a functor that takes a string.
* @param pattern - the substring to search for.
* @param offset - search start position.
* @param maxCount - the maximum number of occurrences to be processed, 0 - no restrictions.
*/
template<typename Op>
constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
}
template<typename To = std::vector<size_t>>
constexpr To find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
To result;
for_all_finded([&](auto f) { result.emplace_back(f); }, pattern, patternLen, offset, maxCount);
return result;
}
/*!
* @ru @brief Найти все вхождения подстроки в этой строке.
* @param pattern - искомая подстрока.
* @param offset - позиция начала поиска.
* @param maxCount - максимальное количество обрабатываемых вхождений, 0 - без ограничений.
* @return std::vector<size_t> - вектор с позициями начал найденных вхождений.
* @en @brief Find all occurrences of a substring in this string.
* @param pattern - the substring to search for.
* @param offset - search start position.
* @param maxCount - the maximum number of occurrences to be processed, 0 - no restrictions.
* @return std::vector<size_t> - a vector with the positions of the beginnings of the found occurrences.
*/
template<typename To = std::vector<size_t>>
constexpr To find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
}
template<typename To = std::vector<size_t>>
constexpr void find_all_to(To& to, const K* pattern, size_t len, size_t offset = 0, size_t maxCount = 0) const {
return for_all_finded([&](size_t pos) {
to.emplace_back(pos);
}, pattern, len, offset, maxCount);
}
/*!
* @ru @brief Найти последнее вхождения символа в этой строке.
* @param s - искомый символ.
* @param offset - c какой позиции вести поиск в обратную сторону, -1 - с самого конца.
* @return size_t - позицию найденного символа, или -1, если не найден.
* @en @brief Find the last occurrence of a character in this string.
* @param s is an optional character.
* @param offset - from which position to search in the opposite direction, -1 - from the very end.
* @return size_t - position of the found character, or -1 if not found.
*/
constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
size_t len = std::min(_len(), offset);
const K *text = _str();
while (len > 0) {
if (text[--len] == s)
return len;
}
return str::npos;
}
/*!
* @ru @brief Найти первое вхождение символа из заданного набора символов.
* @param pattern - строка, задающая набор искомых символов.
* @param offset - позиция начала поиска.
* @return size_t - позицию найденного вхождения, или -1, если не найден.
* @en @brief Find the first occurrence of a character from a given character set.
* @param pattern - a string specifying the set of characters to search for.
* @param offset - search start position.
* @return size_t - position of the found occurrence, or -1 if not found.
*/
constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
return std::string_view{_str(), _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
}
/*!
* @ru @brief Найти первое вхождение символа из заданного набора символов.
* @param pattern - строка, задающая набор искомых символов.
* @param offset - позиция начала поиска.
* @return std::pair<size_t, size_t> - пару из позиции найденного вхождения и номера найденного символа в наборе, или -1, если не найден.
* @en @brief Find the first occurrence of a character from a given character set.
* @param pattern - a string specifying the set of characters to search for.
* @param offset - search start position.
* @return std::pair<size_t, size_t> - a pair from the position of the found occurrence and the number of the found character in the set, or -1 if not found.
*/
constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
const K* text = _str();
size_t fnd = std::string_view{text, _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
}
/*!
* @ru @brief Найти первое вхождение символа не из заданного набора символов.
* @param pattern - строка, задающая набор символов.
* @param offset - позиция начала поиска.
* @return size_t - позицию найденного вхождения, или -1, если не найден.
* @en @brief Find the first occurrence of a character not from the given character set.
* @param pattern - a string specifying the character set.
* @param offset - search start position.
* @return size_t - position of the found occurrence, or -1 if not found.
*/
constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
return std::string_view{_str(), _len()}.find_first_not_of(std::string_view{pattern.str, pattern.len}, offset);
}
/*!
* @ru @brief Найти последнее вхождение символа из заданного набора символов.
* @param pattern - строка, задающая набор искомых символов.
* @param offset - позиция начала поиска.
* @return size_t - позицию найденного вхождения, или -1, если не найден.
* @en @brief Find the last occurrence of a character from a given character set.
* @param pattern - a string specifying the set of characters to search for.
* @param offset - search start position.
* @return size_t - position of the found occurrence, or -1 if not found.
*/
constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
return std::string_view{_str(), _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
}
/*!
* @ru @brief Найти последнее вхождение символа из заданного набора символов.
* @param pattern - строка, задающая набор искомых символов.
* @param offset - позиция начала поиска.
* @return std::pair<size_t, size_t> - пару из позиции найденного вхождения и номера найденного символа в наборе, или -1, если не найден.
* @en @brief Find the last occurrence of a character from a given character set.
* @param pattern - a string specifying the set of characters to search for.
* @param offset - search start position.
* @return std::pair<size_t, size_t> - a pair from the position of the found occurrence and the number of the found character in the set, or -1 if not found.
*/
constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
const K* text = _str();
size_t fnd = std::string_view{text, _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
}
/*!
* @ru @brief Найти последнее вхождение символа не из заданного набора символов.
* @param pattern - строка, задающая набор символов.
* @param offset - позиция начала поиска.
* @return size_t - позицию найденного вхождения, или -1, если не найден.
* @en @brief Find the last occurrence of a character not from the given character set.
* @param pattern - a string specifying the character set.
* @param offset - search start position.
* @return size_t - position of the found occurrence, or -1 if not found.
*/
constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
return std::string_view{_str(), _len()}.find_last_not_of(std::string_view{pattern.str, pattern.len}, offset);
}
/*!
* @ru @brief Получить подстроку. Работает аналогично operator(), только результат выдает того же типа, к которому применён метод.
* @param from - количество символов от начала строки. Если меньше нуля, отсчитывается от конца строки в сторону начала.
* @param len - количество символов в получаемом "куске". Если меньше или равно нулю, то отсчитать len символов от конца строки.
* @return my_type - подстроку, объект того же типа, к которому применён метод.
* @en @brief Get a substring. Works similarly to operator(), only the result is the same type as the method applied to.
* @param from - number of characters from the beginning of the line. If less than zero, it is counted from the end of the line towards the beginning.
* @param len - the number of characters in the resulting "chunk". If less than or equal to zero, then count len characters from the end of the line.
* @return my_type - a substring, an object of the same type to which the method is applied.
*/
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
return my_type{d()(from, len)};
}
/*!
* @ru @brief Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
* @param len - количество символов в получаемом "куске". При выходе за пределы строки вернёт всё до конца строки.
* @return Строку того же типа, к которому применён метод.
* @en @brief Get part of a string with an object of the same type to which the method is applied, similar to mid.
* @param from - number of characters from the beginning of the line. If the string size is exceeded, it will return an empty string.
* @param len - the number of characters in the resulting "chunk". When going beyond the line, it will return everything up to the end of the line.
* @return A string of the same type to which the method is applied.
*/
constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
return my_type{d().mid(from, len)};
}
/*!
* @ru @brief Преобразовать строку в число заданного типа.
* @tparam T - желаемый тип числа.
* @tparam CheckOverflow - проверять на переполнение.
* @tparam Base - основание счисления числа, от -1 до 36, кроме 1.
* - Если 0: то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10.
* - Если -1: то пытается определить основание по префиксам:
* - 0 или 0[oO]: 8
* - 0[bB]: 2
* - 0[xX]: 16
* - в остальных случаях 10.
* @tparam SkipWs - пропускать пробельные символы в начале строки.
* @tparam AllowSign - допустим ли знак '+' перед числом.
* @return T - число, результат преобразования, насколько оно получилось, или 0 при переполнении.
* @en @brief Convert a string to a number of the given type.
* @tparam T - the desired number type.
* @tparam CheckOverflow - check for overflow.
* @tparam Base - the base of the number, from -1 to 36, except 1.
* - If 0: then tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10.
* - If -1: then tries to determine the base by prefixes:
* - 0 or 0[oO]: 8
* - 0[bB]: 2
* - 0[xX]: 16
* - in other cases 10.
* @tparam SkipWs - skip whitespace characters at the beginning of the line.
* @tparam AllowSign - whether the '+' sign is allowed before a number.
* @return T - a number, the result of the transformation, how much it turned out, or 0 if it overflows.
*/
template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
constexpr T as_int() const noexcept {
auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
return err == IntConvertResult::Overflow ? 0 : res;
}
/*!
* @ru @brief Преобразовать строку в число заданного типа.
* @tparam T - желаемый тип числа.
* @tparam CheckOverflow - проверять на переполнение.
* @tparam Base - основание счисления числа, от -1 до 36, кроме 1.
* - Если 0: то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
* - Если -1: то пытается определить основание по префиксам:
* - 0 или 0[oO]: 8
* - 0[bB]: 2
* - 0[xX]: 16
* - в остальных случаях 10.
* @tparam SkipWs - пропускать пробельные символы в начале строки. Пропускаются все символы с ASCII кодами <= 32.
* @tparam AllowSign - допустим ли знак '+' перед числом.
* @return convert_result<T> - кортеж из полученного числа, успешности преобразования и количестве обработанных символов.
* @en @brief Convert a string to a number of the given type.
* @tparam T - the desired number type.
* @tparam CheckOverflow - check for overflow.
* @tparam Base - the base of the number, from -1 to 36, except 1.
* - If 0: then tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
* - If -1: then tries to determine the base by prefixes:
* - 0 or 0[oO]: 8
* - 0[bB]: 2
* - 0[xX]: 16
* - in other cases 10.
* @tparam SkipWs - skip whitespace characters at the beginning of the line. All characters with ASCII codes <= 32 are skipped.
* @tparam AllowSign - whether the '+' sign is allowed before a number.
* @return convert_result<T> - a tuple of the received number, the success of the conversion and the number of characters processed.
*/
template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
constexpr convert_result<T> to_int() const noexcept {
return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
}
/*!
* @ru @brief Преобразовать строку в double.
* @return std::optional<double>.
* @en @brief Convert string to double.
* @return std::optional<double>.
*/
template<bool SkipWS = true, bool AllowPlus = true> requires (sizeof(K) == 1)
std::optional<double> to_double() const noexcept {
size_t len = _len();
const K* ptr = _str();
if constexpr (SkipWS) {
while (len && uns_type(*ptr) <= ' ') {
len--;
ptr++;
}
}
if constexpr (AllowPlus) {
if (len && *ptr == K('+')) {
ptr++;
len--;
}
}
if (!len) {
return {};
}
double d{};
if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
return d;
}
return {};
}
/*!
* @ru @brief Преобразовать строку в 16ричной записи в double. Пока работает только для char.
* @return std::optional<double>.
* @en @brief Convert string in hex form to double.
* @return std::optional<double>.
*/
template<bool SkipWS = true> requires (sizeof(K) == 1)
std::optional<double> to_double_hex() const noexcept {
size_t len = _len();
const K* ptr = _str();
if constexpr (SkipWS) {
while (len && uns_type(*ptr) <= ' ') {
len--;
ptr++;
}
}
if (len) {
double d{};
if (std::from_chars(ptr, ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
return d;
}
}
return {};
}
/*!
* @ru @brief Преобразовать строку в целое число.
* @tparam T - тип числа, выводится из аргумента.
* @param t - переменная, в которую записывается результат.
* @en @brief Convert a string to an integer.
* @tparam T - number type, inferred from the argument.
* @param t - the variable into which the result is written.
*/
template<ToIntNumber T>
constexpr void as_number(T& t) const {
t = as_int<T>();
}
template<typename T, typename Op>
constexpr T splitf(const K* delimiter, size_t lendelimiter, const Op& beforeFunc, size_t offset) const {
size_t mylen = _len();
std::conditional_t<std::is_same_v<T, void>, char, T> results;
str_piece me{_str(), mylen};
for (int i = 0;; i++) {
size_t beginOfDelim = find(delimiter, lendelimiter, offset);
if (beginOfDelim == str::npos) {
str_piece last{me.symbols() + offset, me.length() - offset};
if constexpr (std::is_invocable_v<Op, str_piece&>) {
beforeFunc(last);
}
if constexpr (requires { results.emplace_back(last); }) {
if (last.is_same(me)) {
// Пробуем положить весь объект.
// Try to put the entire object.
results.emplace_back(d());
} else {
results.emplace_back(last);
}
} else if constexpr (requires { results.push_back(last); }) {
if (last.is_same(me)) {
// Пробуем положить весь объект.
// Try to put the entire object.
results.push_back(d());
} else {
results.push_back(last);
}
} else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
if (i < std::size(results)) {
if (last.is_same(me)) {
// Пробуем положить весь объект.
// Try to put the entire object.
results[i] = d();
} else
results[i] = last;
}
}
break;
}
str_piece piece{me.symbols() + offset, beginOfDelim - offset};
if constexpr (std::is_invocable_v<Op, str_piece&>) {
beforeFunc(piece);
}
if constexpr (requires { results.emplace_back(piece); }) {
results.emplace_back(piece);
} else if constexpr (requires { results.push_back(piece); }) {
results.push_back(piece);
} else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
if (i < std::size(results)) {
results[i] = piece;
if (i == results.size() - 1) {
break;
}
}
}
offset = beginOfDelim + lendelimiter;
}
if constexpr (!std::is_same_v<T, void>) {
return results;
}
}
/*!
* @ru @brief Разделить строку на части по заданному разделителю, с возможным применением функтора к каждой подстроке.
* @tparam T - тип контейнера для складывания подстрок.
* @param delimiter - подстрока разделитель.
* @param beforeFunc - функтор для применения к найденным подстрокам, перед помещением их в результат.
* @param offset - позиция начала поиска разделителя.
* @return T - результат.
* @details Для каждой найденной подстроки, если функтор может принять её, вызывается функтор, и подстрока
* присваивается результату функтора. Далее подстрока пытается добавиться в результат,
* вызывая один из его методов - `emplace_back`, `push_back`, `operator[]`. Если ни одного этого метода
* нет, ничего не делается, только вызов функтора.
* `operator[]` пытается применится, если у результата можно получить размер через `std::size` и
* мы не выходим за этот размер.
* При этом, если найденная подстрока получается совпадающей со всей строкой - в результат пытается
* поместить не подстроку, а весь объект строки, что позволяет, например, эффективно копировать sstring.
* @en @brief Split a string into parts at a given delimiter, possibly applying a functor to each substring.
* @tparam T - type of container for folding substrings.
* @param delimiter - substring delimiter.
* @param beforeFunc - a functor to apply to the found substrings, before placing them in the result.
* @param offset - the position to start searching for the separator.
* @return T - result.
* @details For each substring found, if the functor can accept it, the functor is called, and the substring
* is assigned to the result of the functor. Next, the substring tries to be added to the result,
* calling one of its methods - `emplace_back`, `push_back`, `operator[]`. If none of this method
* no, nothing is done, just calling the functor.
* `operator[]` tries to apply if the result can have a size via `std::size` and
* we do not exceed this size.
* At the same time, if the found substring turns out to match the entire string, the result is attempted
* place not a substring, but the entire string object, which allows, for example, to effectively copy sstring.
*/
template<typename T, typename Op>
constexpr T splitf(str_piece delimiter, const Op& beforeFunc, size_t offset = 0) const {
return splitf<T>(delimiter.symbols(), delimiter.length(), beforeFunc, offset);
}
/*!
* @ru @brief Разделить строку на подстроки по заданному разделителю.
* @tparam T - тип контейнера для результата.
* @param delimiter - разделитель.
* @param offset - позиция начала поиска разделителя.
* @return T - контейнер с результатом.
* @en @brief Split a string into substrings using a given delimiter.
* @tparam T - container type for the result.
* @param delimiter - delimiter.
* @param offset - the position to start searching for the separator.
* @return T - container with the result.
*/
template<typename T>
constexpr T split(str_piece delimiter, size_t offset = 0) const {
return splitf<T>(delimiter.symbols(), delimiter.length(), 0, offset);
}
// Начинается ли эта строка с указанной подстроки
// Does this string start with the specified substring
constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
return _len() >= l && 0 == traits::compare(_str(), prefix, l);
}
/*!
* @ru @brief Начинается ли строка с заданной подстроки.
* @param prefix - подстрока.
* @en @brief Whether the string begins with the given substring.
* @param prefix - substring.
*/
constexpr bool starts_with(str_piece prefix) const noexcept {
return starts_with(prefix.symbols(), prefix.length());
}
constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
size_t myLen = _len();
if (myLen < len) {
return false;
}
const K* ptr1 = _str();
while (len--) {
K s1 = *ptr1++, s2 = *prefix++;
if (s1 == s2)
continue;
if (makeAsciiLower(s1) != makeAsciiLower(s2))
return false;
}
return true;
}
/*!
* @ru @brief Начинается ли строка с заданной подстроки без учёта регистра ASCII символов.
* @param prefix - подстрока.
* @en @brief Whether the string begins with the given substring in a case-insensitive ASCII character.
* @param prefix - substring.
*/
constexpr bool starts_with_ia(str_piece prefix) const noexcept {
return starts_with_ia(prefix.symbols(), prefix.length());
}
// Является ли эта строка началом указанной строки
// Is this string the beginning of the specified string
constexpr bool prefix_in(const K* text, size_t len) const noexcept {
size_t myLen = _len();
if (myLen > len)
return false;
return !myLen || 0 == traits::compare(text, _str(), myLen);
}
/*!
* @ru @brief Является ли эта строка началом другой строки.
* @param text - другая строка.
* @en @brief Whether this string is the beginning of another string.
* @param text - another string.
*/
constexpr bool prefix_in(str_piece text) const noexcept {
return prefix_in(text.symbols(), text.length());
}
// Заканчивается ли строка указанной подстрокой
// Does the string end with the specified substring
constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
size_t myLen = _len();
return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
}
/*!
* @ru @brief Заканчивается ли строка указанной подстрокой.
* @param suffix - подстрока.
* @en @brief Whether the string ends with the specified substring.
* @param suffix - substring.
*/
constexpr bool ends_with(str_piece suffix) const noexcept {
return ends_with(suffix.symbols(), suffix.length());
}
// Заканчивается ли строка указанной подстрокой без учета регистра ASCII
// Whether the string ends with the specified substring, case insensitive ASCII
constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
size_t myLen = _len();
if (myLen < len) {
return false;
}
const K* ptr1 = _str() + myLen - len;
while (len--) {
K s1 = *ptr1++, s2 = *suffix++;
if (s1 == s2)
continue;
if (makeAsciiLower(s1) != makeAsciiLower(s2))
return false;
}
return true;
}
/*!
* @ru @brief Заканчивается ли строка указанной подстрокой без учёта регистра ASCII символов.
* @param suffix - подстрока.
* @en @brief Whether the string ends with the specified substring in a case-insensitive ASCII character.
* @param suffix - substring.
*/
constexpr bool ends_with_ia(str_piece suffix) const noexcept {
return ends_with_ia(suffix.symbols(), suffix.length());
}
/*!
* @ru @brief Содержит ли строка только ASCII символы.
* @en @brief Whether the string contains only ASCII characters.
*/
constexpr bool is_ascii() const noexcept {
if (_is_empty())
return true;
const int sl = ascii_mask<K>::WIDTH;
const size_t mask = ascii_mask<K>::VALUE;
size_t len = _len();
const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
if constexpr (sl > 1) {
const size_t roundMask = sizeof(size_t) - 1;
while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
if (*ptr++ > 127)
return false;
len--;
}
while (len >= sl) {
if (*reinterpret_cast<const size_t*>(ptr) & mask)
return false;
ptr += sl;
len -= sl;
}
}
while (len--) {
if (*ptr++ > 127)
return false;
}
return true;
}
/*!
* @ru @brief Получить копию строки в верхнем регистре ASCII символов.
* @tparam R - желаемый тип строки, по умолчанию тот же, чей метод вызывался.
* @return R - копию строки в верхнем регистре.
* @en @brief Get a copy of the string in uppercase ASCII characters.
* @tparam R - the desired string type, by default the same whose method was called.
* @return R - uppercase copy of the string.
*/
template<typename R = my_type>
R upperred_only_ascii() const {
return R::upperred_only_ascii_from(d());
}
/*!
* @ru @brief Получить копию строки в нижнем регистре ASCII символов.
* @tparam R - желаемый тип строки, по умолчанию тот же, чей метод вызывался.
* @return R - копию строки в нижнем регистре.
* @en @brief Get a copy of the string in lowercase ASCII characters.
* @tparam R - the desired string type, by default the same whose method was called.
* @return R - lowercase copy of the string.
*/
template<typename R = my_type>
R lowered_only_ascii() const {
return R::lowered_only_ascii_from(d());
}
/*!
* @ru @brief Получить копию строки с заменёнными вхождениями подстрок.
* @tparam R - желаемый тип строки, по умолчанию тот же, чей метод вызывался.
* @param pattern - искомая подстрока.
* @param repl - строка, на которую заменять.
* @param offset - начальная позиция поиска.
* @param maxCount - максимальное количество замен, 0 - без ограничений.
* @return R строку заданного типа, по умолчанию того же, чей метод вызывался.
* @en @brief Get a copy of the string with occurrences of substrings replaced.
* @tparam R - the desired string type, by default the same whose method was called.
* @param pattern - the substring to search for.
* @param repl - the string to replace with.
* @param offset - starting position of the search.
* @param maxCount - maximum number of replacements, 0 - no restrictions.
* @return R a string of the given type, by default the same whose method was called.
*/
template<typename R = my_type>
R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
return R::replaced_from(d(), pattern, repl, offset, maxCount);
}
template<StrType<K> From>
constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
str_piece sfrom = from, newPos = opTrim(sfrom);
if (newPos.is_same(sfrom)) {
my_type res = from;
return res;
}
return my_type{newPos};
}
template<TrimSides S, StrType<K> From>
constexpr static my_type trim_static(const From& from) {
return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
}
template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
requires is_const_pattern<N>
constexpr static my_type trim_static(const From& from, T&& pattern) {
return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
}
template<TrimSides S, bool withSpaces, StrType<K> From>
constexpr static my_type trim_static(const From& from, str_piece pattern) {
return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
}
/*!
* @ru @brief Получить строку с удалением пробельных символов слева и справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @return R - строка, с удалёнными в начале и в конце пробельными символами.
* @en @brief Get a string with whitespace removed on the left and right.
* @tparam R - desired string type, default str_src.
* @return R - a string with whitespace characters removed at the beginning and end.
*/
template<typename R = str_piece>
constexpr R trimmed() const {
return R::template trim_static<TrimSides::TrimAll>(d());
}
/*!
* @ru @brief Получить строку с удалением пробельных символов слева.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @return R - строка, с удалёнными в начале пробельными символами.
* @en @brief Get a string with whitespace removed on the left.
* @tparam R - desired string type, default str_src.
* @return R - a string with leading whitespace characters removed.
*/
template<typename R = str_piece>
R trimmed_left() const {
return R::template trim_static<TrimSides::TrimLeft>(d());
}
/*!
* @ru @brief Получить строку с удалением пробельных символов справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @return R - строка, с удалёнными в конце пробельными символами.
* @en @brief Get a string with whitespace removed on the right.
* @tparam R - desired string type, default str_src.
* @return R - a string with whitespace characters removed at the end.
*/
template<typename R = str_piece>
R trimmed_right() const {
return R::template trim_static<TrimSides::TrimRight>(d());
}
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, слева и справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале и в конце символами, содержащимися в литерале.
* @en @brief Get a string with the characters specified by the string literal removed from the left and right.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the literal removed at the beginning and at the end.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed(T&& pattern) const {
return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, слева.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале символами, содержащимися в литерале.
* @en @brief Get a string with the characters specified by the string literal removed from the left.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the literal removed at the beginning.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_left(T&& pattern) const {
return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в конце символами, содержащимися в литерале.
* @en @brief Get a string with the characters specified by the string literal removed from the right.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with characters contained in the literal removed at the end.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_right(T&& pattern) const {
return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
}
// Триминг по символам в литерале и пробелам
// Trimming by characters in literal and spaces
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, а также
* пробельных символов, слева и справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале и в конце символами, содержащимися в литерале
* и пробельными символами.
* @en @brief Get a string with the characters specified by the string literal removed, as well as
* whitespace characters, left and right.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the literal removed at the beginning and at the end
* and whitespace characters.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, а также
* пробельных символов, слева.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале символами, содержащимися в литерале
* и пробельными символами.
* @en @brief Get a string with the characters specified by the string literal removed, as well as
* whitespace characters, left.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the literal removed at the beginning
* and whitespace characters.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_left_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных строковым литералом, а также
* пробельных символов, справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строковый литерал, задающий символы, которые будут обрезаться.
* @return R - строка, с удалёнными в конце символами, содержащимися в литерале
* и пробельными символами.
* @en @brief Get a string with the characters specified by the string literal removed, as well as
* whitespace characters, right.
* @tparam R - desired string type, default str_src.
* @param pattern is a string literal specifying the characters that will be trimmed.
* @return R - a string with characters contained in the literal removed at the end
* and whitespace characters.
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_right_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
}
// Триминг по динамическому источнику
// Trimming by dynamic source
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, слева и справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале и в конце символами, содержащимися в шаблоне.
* @en @brief Get a string with characters specified by another string removed, left and right.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the pattern removed at the beginning and at the end.
*/
template<typename R = str_piece>
R trimmed(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, слева.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале символами, содержащимися в шаблоне.
* @en @brief Get a string with characters specified by another string removed from the left.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the pattern removed at the beginning.
*/
template<typename R = str_piece>
R trimmed_left(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в конце символами, содержащимися в шаблоне.
* @en @brief Get a string with characters specified by another string removed to the right.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with characters contained in the pattern removed at the end.
*/
template<typename R = str_piece>
R trimmed_right(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, а также
* пробельных символов, слева и справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале и в конце символами, содержащимися в шаблоне
* и пробельными символами.
* @en @brief Get a string, removing characters specified by another string, as well as
* whitespace characters, left and right.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the pattern removed at the beginning and at the end
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, а также
* пробельных символов, слева.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в начале символами, содержащимися в шаблоне
* и пробельными символами.
* @en @brief Get a string, removing characters specified by another string, as well as
* whitespace characters, left.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with the characters contained in the pattern removed at the beginning
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_left_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
}
/*!
* @ru @brief Получить строку с удалением символов, заданных другой строкой, а также
* пробельных символов, справа.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param pattern - строка, задающая символы, которые будут обрезаться.
* @return R - строка, с удалёнными в конце символами, содержащимися в шаблоне
* и пробельными символами.
* @en @brief Get a string, removing characters specified by another string, as well as
* whitespace characters, right.
* @tparam R - desired string type, default str_src.
* @param pattern - a string specifying the characters that will be trimmed.
* @return R - a string with characters contained in the template removed at the end
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_right_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
}
/*!
* @ru @brief Получить объект `Splitter` по заданному разделителю, который позволяет последовательно
* получать подстроки методом `next()`, пока `is_done()` false.
* @param delimiter - разделитель.
* @return Splitter<K>.
* @en @brief Retrieve a `Splitter` object by the given splitter, which allows sequential
* get substrings using the `next()` method while `is_done()` is false.
* @param delimiter - delimiter.
* @return Splitter<K>.
*/
constexpr SplitterBase<K, str_piece> splitter(str_piece delimiter) const {
return SplitterBase<K, str_piece>{*this, delimiter};
}
};
template<size_t N> requires (N > 1)
struct find_all_container {
static constexpr size_t max_capacity = N;
size_t positions_[N];
size_t added_{};
void emplace_back(size_t pos) {
positions_[added_++] = pos;
}
};
/*!
* @ru @brief Простейший класс иммутабельной не владеющей строки.
* @details Этот класс заменяет `simple_str`, если вы используете только "strexpr.h".
* Аналог std::string_view. Содержит только указатель и длину.
* Как наследник от str_algs поддерживает все константные строковые методы,
* за исключением парсинга double из любых типов символов - поддерживаются только
* char, wchar_t, и совместимые с ними по размеру символы. Также не содержит
* работы с упрощённым юникодом.
* @tparam K - тип символов строки.
* @en @brief The simplest class of an immutable non-owning string.
* @details This class replaces `simple_str` if you only use `strexpr.h`.
* Analogous to std::string_view. Contains only a pointer and a length.
* As a descendant of str_algs, it supports all constant string methods,
* except for parsing double from any character types - only supported
* char, wchar_t, and characters compatible with them in size. Also does not contain
* work with simplified Unicode.
* @tparam K - the character type of the string.
*/
template<typename K>
struct str_src : str_src_algs<K, str_src<K>, str_src<K>, false> {
using symb_type = K;
using my_type = str_src<K>;
const symb_type* str;
size_t len;
str_src() = default;
/*!
* @ru @brief Конструктор из строкового литерала.
* @en @brief Constructor from a string literal.
*/
template<typename T, size_t N = const_lit_for<K, T>::Count>
constexpr str_src(T&& v) noexcept : str(v), len(N - 1) {}
/*!
* @ru @brief Конструктор из указателя и длины.
* @en @brief Constructor from pointer and length.
*/
constexpr str_src(const K* p, size_t l) noexcept : str(p), len(l) {}
template<StrType<K> T>
constexpr str_src(T&& t) : str(t.symbols()), len(t.length()){}
/*!
*@ru @brief Конструктор из std::basic_string.
*@en @brief Constructor from std::basic_string.
*/
template<typename A>
constexpr str_src(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
/*!
*@ru @brief Конструктор из std::basic_string_view.
*@en @brief Constructor from std::basic_string_view.
*/
constexpr str_src(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
/*!
* @ru @brief Получить длину строки.
* @en @brief Get the length of the string.
*/
constexpr size_t length() const noexcept {
return len;
}
/*!
* @ru @brief Получить указатель на константный буфер с символами строки.
* @en @brief Get a pointer to a constant buffer containing string characters.
*/
constexpr const symb_type* symbols() const noexcept {
return str;
}
/*!
* @ru @brief Проверить, не пуста ли строка.
* @en @brief Check if a string is empty.
*/
constexpr bool is_empty() const noexcept {
return len == 0;
}
/*!
* @ru @brief Проверить, не указывают ли два объекта на одну строку.
* @param other - другая строка.
* @en @brief Check if two objects point to the same string.
* @param other - another string.
*/
constexpr bool is_same(str_src<K> other) const noexcept {
return str == other.str && len == other.len;
}
/*!
* @ru @brief Проверить, не является ли строка частью другой строки.
* @param other - другая строка.
* @en @brief Check if a string is part of another string.
* @param other - another string.
*/
constexpr bool is_part_of(str_src<K> other) const noexcept {
return str >= other.str && str + len <= other.str + other.len;
}
/*!
* @ru @brief Получить символ из указанной позиции. Проверка границ не выполняется.
* @param idx - позиция символа.
* @return K - символ.
* @en @brief Get the character from the specified position. Bounds checking is not performed.
* @param idx - position of the symbol.
* @return K is a symbol.
*/
constexpr K operator[](size_t idx) const {
return str[idx];
}
/*!
* @ru @brief Сдвигает начало строки на заданное количество символов.
* @param delta - количество символов.
* @return my_type&.
* @en @brief Shifts the start of a line by the specified number of characters.
* @param delta - number of characters.
* @return my_type&.
*/
constexpr my_type& remove_prefix(size_t delta) {
str += delta;
len -= delta;
return *this;
}
/*!
* @ru @brief Укорачивает строку на заданное количество символов.
* @param delta - количество символов.
* @return my_type&.
* @en @brief Shortens the string by the specified number of characters.
* @param delta - number of characters.
* @return my_type&.
*/
constexpr my_type& remove_suffix(size_t delta) {
len -= delta;
return *this;
}
};
/*!
* @ru @brief Класс, заявляющий, что ссылается на нуль-терминированную строку.
* @tparam K - тип символов строки.
* @details Упрощённая реализация simple_str_nt, когда вы используете только strexpr.h.
* Служит для показа того, что функция параметром хочет получить
* строку с нулем в конце, например, ей надо дальше передавать его в
* стороннее API. Без этого ей надо было бы либо указывать параметром
* конкретный класс строки, что лишает универсальности, либо приводило бы
* к постоянным накладным расходам на излишнее копирование строк во временный
* буфер. Источником нуль-терминированных строк могут быть строковые литералы
* при компиляции, либо классы, хранящие строки.
* @en @brief A class that claims to refer to a null-terminated string.
* @tparam K - the character type of the string.
* @details Simplified implementation of simple_str_nt when you only use strexpr.h.
* Shows what the function wants to receive as a parameter
* a string with a zero at the end, for example, she needs to further transfer it to
* third party API. Without this, she would have to either specify the parameter
* specific string class, which deprives universality, or would lead
* to the constant overhead of unnecessary copying of string into the temporary
* buffer. Null-terminated strings can be sourced from string literals
* during compilation, or classes that store strings.
*/
template<typename K>
struct str_src_nt : str_src<K>, null_terminated<K, str_src_nt<K>> {
using symb_type = K;
using my_type = str_src_nt<K>;
using base = str_src<K>;
constexpr static const K empty_string[1] = {0};
str_src_nt() = default;
/*!
* @ru @brief Явный конструктор из С-строки.
* @param p - указатель на C-строку (нуль-терминированная строка).
* @details Это единственный конструктор из всех строковых объектов, принимающий C-строку.
* Вычисляет её длину при инициализации. Все остальные строковые объекты не инициализируются
* C-строками. Это для того, чтобы `strlen` вызывалась только в одном месте библиотеки,
* длина C-строки вычислялась только один раз и далее не терялась случайно при передаче между разными
* типами строковых объектов.
* @en @brief Explicit constructor from C-string.
* @param p - pointer to a C-string (null-terminated string).
* @details This is the only constructor of all string objects that accepts a C-string.
* Calculates its length upon initialization. All other string objects are not initialized
* C-strings. This is to ensure that `strlen` is called only in one place in the library,
* the length of the C-string was calculated only once and was not subsequently lost accidentally when transferred between different
* types of string objects.
*/
template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
constexpr explicit str_src_nt(T&& p) noexcept {
base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
base::str = base::len ? p : empty_string;
}
/*!
* @ru @brief Конструктор из строкового литерала.
* @en @brief Constructor from a string literal.
*/
template<typename T, size_t N = const_lit_for<K, T>::Count>
constexpr str_src_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
/*!
* @ru @brief Конструктор из указателя и длины.
* @en @brief Constructor from pointer and length.
*/
constexpr str_src_nt(const K* p, size_t l) noexcept : base(p, l) {}
template<StrType<K> T>
constexpr str_src_nt(T&& t) {
base::str = t.symbols();
base::len = t.length();
}
/*!
*@ru @brief Конструктор из std::basic_string.
*@en @brief Constructor from std::basic_string.
*/
template<typename A>
constexpr str_src_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
static const my_type empty_str;
/*!
* @ru @brief Получить нуль-терминированную строку, сдвинув начало на заданное количество символов.
* @param from - на сколько символов сдвинуть начало строки.
* @return my_type.
* @en @brief Get a null-terminated string by shifting the start by the specified number of characters.
* @param from - by how many characters to shift the beginning of the line.
* @return my_type.
*/
constexpr my_type to_nts(size_t from) {
if (from > base::len) {
from = base::len;
}
return {base::str + from, base::len - from};
}
};
template<typename K>
inline const str_src_nt<K> str_src_nt<K>::empty_str{str_src_nt<K>::empty_string, 0};
template<typename K> struct simple_str_selector;
#ifndef IN_FULL_SIMSTR
template<typename K>
using simple_str = str_src<K>;
template<typename K>
struct simple_str_selector {
using type = simple_str<K>;
};
template<typename K>
using simple_str_nt = str_src_nt<K>;
template<typename K>
using Splitter = SplitterBase<K, str_src<K>>;
using ssa = str_src<u8s>;
using ssb = str_src<ubs>;
using ssw = str_src<wchar_t>;
using ssu = str_src<u16s>;
using ssuu = str_src<u32s>;
using stra = str_src_nt<u8s>;
using strb = str_src_nt<ubs>;
using strw = str_src_nt<wchar_t>;
using stru = str_src_nt<u16s>;
using struu = str_src_nt<u32s>;
inline namespace literals {
/*!
* @ru @brief Оператор литерал в str_src.
* @param ptr - указатель на строку.
* @param l - длина строки.
* @return str_src.
* @en @brief Operator literal in str_src.
* @param ptr - pointer to a string.
* @param l - string length.
* @return str_src.
*/
SS_CONSTEVAL str_src_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
return str_src_nt<u8s>{ptr, l};
}
/*!
* @ru @brief Оператор литерал в str_src.
* @param ptr - указатель на строку.
* @param l - длина строки.
* @return str_src.
* @en @brief Operator literal in str_src.
* @param ptr - pointer to a string.
* @param l - string length.
* @return str_src.
*/
SS_CONSTEVAL str_src_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
return str_src_nt<ubs>{ptr, l};
}
/*!
* @ru @brief Оператор литерал в str_src.
* @param ptr - указатель на строку.
* @param l - длина строки.
* @return str_src.
* @en @brief Operator literal in str_src.
* @param ptr - pointer to a string.
* @param l - string length.
* @return str_src.
*/
SS_CONSTEVAL str_src_nt<uws> operator""_ss(const uws* ptr, size_t l) {
return str_src_nt<uws>{ptr, l};
}
/*!
* @ru @brief Оператор литерал в str_src.
* @param ptr - указатель на строку.
* @param l - длина строки.
* @return str_src.
* @en @brief Operator literal in str_src.
* @param ptr - pointer to a string.
* @param l - string length.
* @return str_src.
*/
SS_CONSTEVAL str_src_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
return str_src_nt<u16s>{ptr, l};
}
/*!
* @ru @brief Оператор литерал в str_src.
* @param ptr - указатель на строку.
* @param l - длина строки.
* @return str_src.
* @en @brief Operator literal in str_src.
* @param ptr - pointer to a string.
* @param l - string length.
* @return str_src.
*/
SS_CONSTEVAL str_src_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
return str_src_nt<u32s>{ptr, l};
}
} // namespace literals
#endif
template<typename K, bool withSpaces>
struct CheckSpaceTrim {
constexpr bool is_trim_spaces(K s) const {
return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
}
};
template<typename K>
struct CheckSpaceTrim<K, false> {
constexpr bool is_trim_spaces(K) const {
return false;
}
};
template<typename K>
struct CheckSymbolsTrim {
str_src<K> symbols;
constexpr bool is_trim_symbols(K s) const {
return symbols.len != 0 && str_src<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
}
};
template<typename K, size_t N>
struct CheckConstSymbolsTrim {
const const_lit_to_array<K, N> symbols;
template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
constexpr bool is_trim_symbols(K s) const noexcept {
return symbols.contain(s);
}
};
template<typename K>
struct CheckConstSymbolsTrim<K, 0> {
constexpr bool is_trim_symbols(K) const {
return false;
}
};
template<typename K, size_t N>
struct SymbSelector {
using type = CheckConstSymbolsTrim<K, N>;
};
template<typename K>
struct SymbSelector<K, 0> {
using type = CheckSymbolsTrim<K>;
};
template<typename K>
struct SymbSelector<K, static_cast<size_t>(-1)> {
using type = CheckConstSymbolsTrim<K, 0>;
};
template<TrimSides S, typename K, size_t N, bool withSpaces>
struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
constexpr bool isTrim(K s) const {
return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
}
constexpr str_src<K> operator()(str_src<K> from) const {
if constexpr ((S & TrimSides::TrimLeft) != 0) {
while (from.len) {
if (isTrim(*from.str)) {
from.str++;
from.len--;
} else
break;
}
}
if constexpr ((S & TrimSides::TrimRight) != 0) {
const K* back = from.str + from.len - 1;
while (from.len) {
if (isTrim(*back)) {
back--;
from.len--;
} else
break;
}
}
return from;
}
};
template<TrimSides S, typename K>
using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
constexpr inline auto trimOp(T&& pattern) {
return trim_operator<S, K, N - 1, withSpaces>{pattern};
}
template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
constexpr inline auto trimOp(str_src<K> pattern) {
return trim_operator<S, K, 0, withSpaces>{pattern};
}
template<typename T>
concept StrSource = StdStrSource<T> || requires {
typename std::remove_cvref_t<T>::symb_type;
};
template<typename T>
struct to_src_str_base {
using symb_type = typename T::symb_type;
static str_src<symb_type> get(const T& s) {
return {s.symbols(), s.length()};
}
};
struct to_src_str_base_none {};
template<typename T>
struct to_src_str : std::conditional_t<StrSource<T>, to_src_str_base<T>, to_src_str_base_none> {
};
template<typename K, typename T, typename A>
struct to_src_str<std::basic_string<K, T, A>> {
using symb_type = K;
static str_src<K> get(const std::basic_string<K, T, A>& s) {
return {s.data(), s.length()};
}
};
template<typename K, typename T>
struct to_src_str<std::basic_string_view<K, T>> {
using symb_type = K;
static str_src<K> get(const std::basic_string_view<K, T>& s) {
return {s.data(), s.length()};
}
};
template<typename T>
using src_str_t = to_src_str<std::remove_cvref_t<T>>::symb_type;
template<typename T>
auto get_str_src_from(T&& t) {
return to_src_str<std::remove_cvref_t<T>>::get(std::forward<T>(t));
}
static constexpr size_t FIND_CACHE_SIZE = 16;
template<typename K, size_t N, size_t L>
struct expr_replaces : expr_to_std_string<expr_replaces<K, N, L>> {
using symb_type = K;
using my_type = expr_replaces<K, N, L>;
str_src<K> what;
const K(&pattern)[N + 1];
const K(&repl)[L + 1];
mutable find_all_container<FIND_CACHE_SIZE> matches_;
mutable size_t last_;
constexpr expr_replaces(str_src<K> w, const K(&p)[N + 1], const K(&r)[L + 1]) : what(w), pattern(p), repl(r) {}
constexpr size_t length() const {
size_t l = what.length();
if constexpr (N == L) {
return l;
}
what.find_all_to(matches_, pattern, N, 0, FIND_CACHE_SIZE);
if (matches_.added_) {
last_ = matches_.positions_[matches_.added_ - 1] + N;
l += int(L - N) * matches_.added_;
if (matches_.added_ == FIND_CACHE_SIZE) {
for (;;) {
size_t next = what.find(pattern, N, last_);
if (next == str::npos) {
break;
}
last_ = next + N;
l += L - N;
}
}
}
if (!l) {
matches_.added_ = -1;
}
return l;
}
constexpr K* place(K* ptr) const noexcept {
if constexpr (N == L) {
const K* from = what.symbols();
for (size_t start = 0; start < what.length();) {
size_t next = what.find(pattern, N, start);
if (next == str::npos) {
next = what.length();
}
size_t delta = next - start;
ch_traits<K>::copy(ptr, from + start, delta);
ptr += delta;
ch_traits<K>::copy(ptr, repl, L);
ptr += L;
start = next + N;
}
return ptr;
}
if (matches_.added_ == 0) {
return what.place(ptr);
} else if (matches_.added_ == -1) {
// after replaces text become empty
return ptr;
}
const K* from = what.symbols();
for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
ch_traits<K>::copy(ptr, from + start, offset - start);
ptr += offset - start;
ch_traits<K>::copy(ptr, repl, L);
ptr += L;
start = offset + N;
if (start >= last_) {
size_t tail = what.length() - last_;
ch_traits<K>::copy(ptr, from + last_, tail);
ptr += tail;
break;
} else {
offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern, N, start);
}
}
return ptr;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
* @tparam K - тип символа, выводится из первого аргумента.
* @param w - начальная строка.
* @param p - строковый литерал, искомая подстрока.
* @param r - строковый литерал, на что заменять.
* @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced.
* @tparam K - the type of the symbol, inferred from the first argument.
* @param w - starting string.
* @param p - string literal, searched substring.
* @param r - string literal, what to replace with.
*/
template<StrSource A, typename K = src_str_t<A>, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
requires(N > 1)
constexpr auto e_repl(A&& w, T&& p, X&& r) {
return expr_replaces<K, N - 1, L - 1>{get_str_src_from(std::forward<A>(w)), p, r};
}
/*!
* @ingroup StrExprs
* @ru @brief Строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
* @tparam K - тип строки.
* @details `e_repl` позволяет заменять только с использование строковых литералов.
* В случае, когда искомая подстрока или строка замены не известны при компиляции, и задаются в runtime,
* следует использовать этот тип, например:
* @en @brief A string expression that generates a string replacing all occurrences of the given substring.
* @tparam K - string type.
* @details `e_repl` only allows replacement using string literals.
* In the case when the required substring or replacement string is not known at compilation, and is set at runtime,
* this type should be used, for example:
* @~
* ```cpp
* stringa result = "<header>" + expr_replaced<u8s>{source, pattern, repl} + "</header>";
* ```
*/
template<typename K>
struct expr_replaced : expr_to_std_string<expr_replaced<K>> {
using symb_type = K;
using my_type = expr_replaced<K>;
str_src<K> what;
const str_src<K> pattern;
const str_src<K> repl;
mutable find_all_container<FIND_CACHE_SIZE> matches_;
mutable size_t last_;
/*!
* @ru @brief Конструктор.
* @param w - исходная строка.
* @param p - искомая подстрока.
* @param r - строка замены.
* @en @brief Constructor.
* @param w - source string.
* @param p - the searched substring.
* @param r - replacement string.
*/
constexpr expr_replaced(str_src<K> w, str_src<K> p, str_src<K> r) : what(w), pattern(p), repl(r) {}
constexpr size_t length() const {
size_t l = what.length(), plen = pattern.length(), rlen = repl.length();
if (!plen || plen == rlen) {
return l;
}
what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE);
if (matches_.added_) {
last_ = matches_.positions_[matches_.added_ - 1] + plen;
l += int(rlen - plen) * matches_.added_;
if (matches_.added_ == FIND_CACHE_SIZE) {
for (;;) {
size_t next = what.find(pattern.symbols(), plen, last_);
if (next == str::npos) {
break;
}
last_ = next + plen;
l += rlen - plen;
}
}
}
if (!l) {
matches_.added_ = -1;
}
return l;
}
constexpr K* place(K* ptr) const noexcept {
size_t plen = pattern.length(), rlen = repl.length();
if (plen == rlen) {
const K* from = what.symbols();
for (size_t start = 0; start < what.length();) {
size_t next = what.find(pattern, start);
if (next == str::npos) {
next = what.length();
}
size_t delta = next - start;
ch_traits<K>::copy(ptr, from + start, delta);
ptr += delta;
ch_traits<K>::copy(ptr, repl.symbols(), rlen);
ptr += rlen;
start = next + plen;
}
return ptr;
}
if (matches_.added_ == 0) {
return what.place(ptr);
} else if (matches_.added_ == -1) {
// after replaces text become empty
return ptr;
}
const K* from = what.symbols();
for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) {
ch_traits<K>::copy(ptr, from + start, offset - start);
ptr += offset - start;
ch_traits<K>::copy(ptr, repl.symbols(), rlen);
ptr += rlen;
start = offset + plen;
if (start >= last_) {
size_t tail = what.length() - last_;
ch_traits<K>::copy(ptr, from + last_, tail);
ptr += tail;
break;
} else {
offset = idx < FIND_CACHE_SIZE ? matches_.positions_[idx++] : what.find(pattern.symbols(), plen, start);
}
}
return ptr;
}
};
/*!
* @ingroup StrExprs
* @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
* @tparam K - тип символа, выводится из первого аргумента.
* @param w - начальная строка.
* @param p - строковый литерал, искомая подстрока.
* @param r - строковый объект, может быть рантайм.
* @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced.
* @tparam K - the type of the symbol, inferred from the first argument.
* @param w - starting string.
* @param p - string literal, searched substring.
* @param r - string object, maybe runtime.
*/
template<StrSource A, typename K = src_str_t<A>, typename T, size_t N = const_lit_for<K, T>::Count, StrSource X>
requires std::is_same_v<K, src_str_t<X>>
constexpr auto e_repl(A&& w, T&& p, X&& r) {
return expr_replaced<K>{get_str_src_from(std::forward<A>(w)), p, get_str_src_from(std::forward<X>(r))};
}
/*!
* @ingroup StrExprs
* @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
* @tparam K - тип символа, выводится из первого аргумента.
* @param w - начальная строка.
* @param p - строковый объект, может быть рантайм.
* @param r - строковый литерал, на что заменять.
* @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced.
* @tparam K - the type of the symbol, inferred from the first argument.
* @param w - starting string.
* @param p - string object, maybe runtime.
* @param r - string literal, what to replace with.
*/
template<StrSource A, typename K = src_str_t<A>, StrSource T, typename X, size_t L = const_lit_for<K, X>::Count>
requires std::is_same_v<K, src_str_t<T>>
constexpr auto e_repl(A&& w, T&& p, X&& r) {
return expr_replaced<K>{get_str_src_from(std::forward<A>(w)), get_str_src_from(std::forward<T>(p)), r};
}
/*!
* @ingroup StrExprs
* @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки.
* @tparam K - тип символа, выводится из первого аргумента.
* @param w - начальная строка.
* @param p - строковый объект, может быть рантайм.
* @param r - строковый объект, может быть рантайм.
* @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced.
* @tparam K - the type of the symbol, inferred from the first argument.
* @param w - starting string.
* @param p - string object, maybe runtime.
* @param r - string object, maybe runtime.
*/
template<StrSource A, typename K = src_str_t<A>, StrSource T, StrSource X>
requires (std::is_same_v<K, src_str_t<T>> && std::is_same_v<K, src_str_t<X>>)
constexpr auto e_repl(A&& w, T&& p, X&& r) {
return expr_replaced<K>{get_str_src_from(std::forward<A>(w)), get_str_src_from(std::forward<T>(p)), get_str_src_from(std::forward<X>(r))};
}
template<bool UseVectorForReplace>
struct replace_search_result_store {
size_t count_{};
std::pair<size_t, size_t> replaces_[16];
};
template<>
struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
// Строковое выражение для замены символов
// String expression to replace characters
template<typename K, size_t N, bool UseVectorForReplace>
struct expr_replace_const_symbols : expr_to_std_string<expr_replace_const_symbols<K, N, UseVectorForReplace>> {
using symb_type = K;
inline static const int BIT_SEARCH_TRESHHOLD = 4;
const K pattern_[N];
const str_src<K> source_;
const str_src<K> replaces_[N];
mutable replace_search_result_store<UseVectorForReplace> search_results_;
[[_no_unique_address]]
uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
constexpr expr_replace_const_symbols(str_src<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
size_t length() const {
size_t l = source_.length();
auto [fnd, num] = find_first_of(source_.str, source_.len);
if (fnd == str::npos) {
return l;
}
l += replaces_[num].len - 1;
if constexpr (UseVectorForReplace) {
search_results_.reserve((l >> 4) + 8);
search_results_.emplace_back(fnd, num);
for (size_t start = fnd + 1;;) {
auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
if (fnd == str::npos) {
break;
}
search_results_.emplace_back(fnd, idx);
start = fnd + 1;
l += replaces_[idx].len - 1;
}
} else {
const size_t max_store = std::size(search_results_.replaces_);
search_results_.replaces_[0] = {fnd, num};
search_results_.count_++;
for (size_t start = fnd + 1;;) {
auto [found, idx] = find_first_of(source_.str, source_.len, start);
if (found == str::npos) {
break;
}
if (search_results_.count_ < max_store) {
search_results_.replaces_[search_results_.count_] = {found, idx};
}
l += replaces_[idx].len - 1;
search_results_.count_++;
start = found + 1;
}
}
return l;
}
K* place(K* ptr) const noexcept {
size_t start = 0;
const K* text = source_.str;
if constexpr (UseVectorForReplace) {
for (const auto& [pos, num] : search_results_) {
size_t delta = pos - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[num].place(ptr);
start = pos + 1;
}
} else {
const size_t max_store = std::size(search_results_.replaces_);
size_t founded = search_results_.count_;
for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
const auto [pos, num] = search_results_.replaces_[idx];
size_t delta = pos - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[num].place(ptr);
start = pos + 1;
}
if (founded > max_store) {
founded -= max_store;
while (founded--) {
auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
size_t delta = fnd - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[idx].place(ptr);
start = fnd + 1;
}
}
}
size_t tail = source_.len - start;
ch_traits<K>::copy(ptr, text + start, tail);
return ptr + tail;
}
protected:
template<typename ... Repl>
constexpr expr_replace_const_symbols(int, str_src<K> source, K s, str_src<K> r, Repl&&... repl) :
expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
template<typename ... Repl> requires (sizeof...(Repl) == N)
constexpr expr_replace_const_symbols(int, str_src<K> source, Repl&&... repl) :
source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
{
if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
for (size_t idx = 0; idx < N; idx++) {
uu8s s = static_cast<uu8s>(pattern_[idx]);
if constexpr (sizeof(K) == 1) {
bit_mask_[s >> 3] |= 1 << (s & 7);
} else {
if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
} else {
bit_mask_[s >> 3] |= 1 << (s & 7);
}
}
}
}
}
template<size_t Idx>
size_t index_of(K s) const {
if constexpr (Idx < N) {
return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
}
return -1;
}
bool is_in_mask(uu8s s) const {
return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
}
bool is_in_mask2(uu8s s) const {
return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
}
bool is_in_pattern(K s, size_t& idx) const {
if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
if constexpr (sizeof(K) == 1) {
if (is_in_mask(s)) {
idx = index_of<0>(s);
return true;
}
} else {
if (std::make_unsigned_t<const K>(s) > 255) {
if (is_in_mask2(s)) {
return (idx = index_of<0>(s)) != -1;
}
} else {
if (is_in_mask(s)) {
idx = index_of<0>(s);
return true;
}
}
}
}
return false;
}
std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
size_t idx;
while (offset < len) {
if (is_in_pattern(text[offset], idx)) {
return {offset, idx};
}
offset++;
}
} else {
while (offset < len) {
if (size_t idx = index_of<0>(text[offset]); idx != -1) {
return {offset, idx};
}
offset++;
}
}
return {-1, -1};
}
};
/*!
* @ingroup StrExprs
* @ru @brief Возвращает строковое выражение, генерирующее строку, в которой заданные символы
* заменены на заданные подстроки.
* @tparam UseVector - использовать вектор для сохранения результатов поиска символов.
* Более подробно описано в `expr_replace_symbols`.
* @param src - исходная строка.
* @param symbol - константный символ, который надо заменять.
* @param repl - строковый литерал, на который заменять символ.
* @param ... symbol, repl - другие символы и строки.
* @details Применяется для генерации замены символов на строки, в случае если все они известны
* в compile time. Пример:
* @en @brief Returns a string expression that generates a string containing the given characters
* replaced with given substrings.
* @tparam UseVector - use a vector to save symbol search results.
* Described in more detail in `expr_replace_symbols`.
* @param src - source string.
* @param symbol - constant symbol that needs to be replaced.
* @param repl - string literal to replace the character with.
* @param ... symbol, repl - other symbols and strings.
* @details Used to generate character replacements for strings if all of them are known
* at compile time. Example:
* @~
* ```cpp
* out += "<div>" + e_repl_const_symbols(text, '\"', "&quot;", '<', "&lt;", '\'', "&#39;", '&', "&amp;") + "</div>";
* ```
* @ru В принципе, `e_repl_const_symbols` вполне безопасно возвращать из функции, если исходная строка
* внешняя по отношению к функции.
* @en In principle, `e_repl_const_symbols` is quite safe to return from a function if the source string
* external to function.
* @~
* ```cpp
* auto repl_html_symbols(ssa text) {
* return e_repl_const_symbols(text, '\"', "&quot;", '<', "&lt;", '\'', "&#39;", '&', "&amp;");
* }
* ....
* out += "<div>" + repl_html_symbols(content) + "</div>";
* ```
*/
template<bool UseVector = false, StrSource A, typename K = src_str_t<A>, typename ... Repl>
requires (sizeof...(Repl) % 2 == 0)
auto e_repl_const_symbols(A&& src, Repl&& ... other) {
return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(get_str_src_from(std::forward<A>(src)), std::forward<Repl>(other)...);
}
/*!
* @ingroup StrExprs
* @ru @brief Тип для строкового выражения, генерирующее строку, в которой заданные символы заменяются на заданные строки.
* @tparam K - тип символа.
* @tparam UseVectorForReplace - использовать вектор для запоминания результатов поиска вхождений символов.
* @details Этот тип применяется, когда состав символов или соответствующих им замен не известен в compile time,
* а определяется в runtime. В конструктор передается вектор из пар `символ - строка замены`.
* Параметр `UseVectorForReplace` задаёт стратегию реализации. Дело в том, что работа любых строковых выражений
* разбита на две фазы - вызов `length()`, в котором подсчитывается количество символов в результате,
* и вызов `place()`, в котором результат помещается в предоставленный буфер.
* При `UseVectorForReplace == true` во время фазы подcчёта количества символов, позиции найденных вхождений
* сохраняются в векторе, и во время второй фазы поиск уже не выполняется, а позиции берутся из вектора.
* Это, с одной стороны, уменьшает время во второй фазе - не нужно снова выполнять поиск, но увеличивает
* время в первой фазе - добавление элементов в вектор не бесплатно, и требует времени.
* При `UseVectorForReplace == false` во время фазы подcчёта количества символов, в локальном массиве запоминаются позиции
* первых 16 вхождений и их общее количество, а во время второй фазы, если вхождений больше 16, то поиск повторяется,
* но уже только с позиции 16го вхождения. Это может увеличить время во второй фазе, но сокращает время в первой
* фазе - не нужно добавлять элементы в вектор, не нужна динамическая аллокация.
* В разных сценариях использования более оптимальными могут быть та или иная стратегия, и вы можете сами решить,
* что в каждом конкретном случае больше подойдёт.
* @en @brief A type for a string expression that generates a string in which the given characters are replaced by the given strings.
* @tparam K - symbol type.
* @tparam UseVectorForReplace - use a vector to remember the results of searching for occurrences of characters.
* @details This type is used when the composition of symbols or their corresponding replacements is not known at compile time,
* and is defined at runtime. A vector of `character - replacement string` pairs is passed to the constructor.
* The `UseVectorForReplace` parameter specifies the implementation strategy. The point is that the work of any string expressions
* is divided into two phases - the `length()` call, which counts the number of characters in the result,
* and a call to `place()`, which places the result in the provided buffer.
* When `UseVectorForReplace == true` during the phase of counting the number of characters, the position of the found occurrences
* are stored in the vector, and during the second phase the search is no longer performed, and the positions are taken from the vector.
* This, on the one hand, reduces the time in the second phase - there is no need to search again, but it increases
* time in the first phase - adding elements to the vector is not free, and takes time.
* When `UseVectorForReplace == false` during the phase of counting the number of characters, positions in the local array are remembered
* the first 16 occurrences and their total number, and during the second phase, if there are more than 16 occurrences, then the search is repeated,
* but only from the position of the 16th occurrence. This may increase the time in the second phase, but reduces the time in the first
* phase - no need to add elements to the vector, no need for dynamic allocation.
*In different use cases, one or another strategy may be more optimal, and you can decide for yourself
* whichever is more suitable in each specific case.
*/
template<typename K, bool UseVectorForReplace = false>
struct expr_replace_symbols : expr_to_std_string<expr_replace_symbols<K, UseVectorForReplace>> {
using symb_type = K;
using str_t = typename simple_str_selector<K>::type;
inline static const int BIT_SEARCH_TRESHHOLD = 4;
const str_src<K> source_;
const std::vector<std::pair<K, str_t>>& replaces_;
std::basic_string<K, ch_traits<K>, std::allocator<K>> pattern_;
mutable replace_search_result_store<UseVectorForReplace> search_results_;
uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
/*!
* @ru @brief Конструктор выражения.
* @param source - исходная строка.
* @param repl - вектор из пар "символ->строка замены".
* @details Пример:
* @en @brief Expression constructor.
* @param source - source string.
* @param repl - a vector of "character->replacement string" pairs.
* @details Example:
* @~
* ```cpp
stringa result = expr_replace_symbols<u8s, true>{source, {
{'-', ""},
{'<', "&lt;"},
{'>', "&gt;"},
{'\'', "&#39;"},
{'\"', "&quot;"},
{'&', "&amp;"},
}};
* ```
* @ru Пример приведен для наглядности использования. В данном случае и заменяемые символы, и строки замены
* известны в compile time, и в этом случае лучше применять e_repl_const_symbols, а этот класс
* используется, когда символы или замены задаются в runtime.
* @en An example is provided for clarity of use. In this case, both the characters to be replaced and the replacement strings
* known at compile time, in which case it is better to use e_repl_const_symbols, and this class
* is used when characters or replacements are specified at runtime.
*/
constexpr expr_replace_symbols(str_t source, const std::vector<std::pair<K, str_t>>& repl )
: source_(source), replaces_(repl)
{
size_t pattern_len = replaces_.size();
pattern_.resize(pattern_len);
K* pattern = pattern_.data();
for (size_t idx = 0; idx < replaces_.size(); idx++) {
*pattern++ = replaces_[idx].first;
}
if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
for (size_t idx = 0; idx < pattern_len; idx++) {
uu8s s = static_cast<uu8s>(pattern_[idx]);
if constexpr (sizeof(K) == 1) {
bit_mask_[s >> 3] |= (1 << (s & 7));
} else {
if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
} else {
bit_mask_[s >> 3] |= (1 << (s & 7));
}
}
}
}
}
size_t length() const {
size_t l = source_.length();
auto [fnd, num] = find_first_of(source_.str, source_.len);
if (fnd == str::npos) {
return l;
}
l += replaces_[num].second.len - 1;
if constexpr (UseVectorForReplace) {
search_results_.reserve((l >> 4) + 8);
search_results_.emplace_back(fnd, num);
for (size_t start = fnd + 1;;) {
auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
if (fnd == str::npos) {
break;
}
search_results_.emplace_back(fnd, idx);
start = fnd + 1;
l += replaces_[idx].second.len - 1;
}
} else {
const size_t max_store = std::size(search_results_.replaces_);
search_results_.replaces_[0] = {fnd, num};
search_results_.count_++;
for (size_t start = fnd + 1;;) {
auto [found, idx] = find_first_of(source_.str, source_.len, start);
if (found == str::npos) {
break;
}
if (search_results_.count_ < max_store) {
search_results_.replaces_[search_results_.count_] = {found, idx};
}
l += replaces_[idx].second.len - 1;
search_results_.count_++;
start = found + 1;
}
}
return l;
}
K* place(K* ptr) const noexcept {
size_t start = 0;
const K* text = source_.str;
if constexpr (UseVectorForReplace) {
for (const auto& [pos, num] : search_results_) {
size_t delta = pos - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[num].second.place(ptr);
start = pos + 1;
}
} else {
const size_t max_store = std::size(search_results_.replaces_);
size_t founded = search_results_.count_;
for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
const auto [pos, num] = search_results_.replaces_[idx];
size_t delta = pos - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[num].second.place(ptr);
start = pos + 1;
}
if (founded > max_store) {
founded -= max_store;
while (founded--) {
auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
size_t delta = fnd - start;
ch_traits<K>::copy(ptr, text + start, delta);
ptr += delta;
ptr = replaces_[idx].second.place(ptr);
start = fnd + 1;
}
}
}
size_t tail = source_.len - start;
ch_traits<K>::copy(ptr, text + start, tail);
return ptr + tail;
}
protected:
size_t index_of(K s) const {
return pattern_.find(s);
}
bool is_in_mask(uu8s s) const {
return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
}
bool is_in_mask2(uu8s s) const {
return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
}
bool is_in_pattern(K s, size_t& idx) const {
if constexpr (sizeof(K) == 1) {
if (is_in_mask(s)) {
idx = index_of(s);
return true;
}
} else {
if (std::make_unsigned_t<const K>(s) > 255) {
if (is_in_mask2(s)) {
return (idx = index_of(s)) != -1;
}
} else {
if (is_in_mask(s)) {
idx = index_of(s);
return true;
}
}
}
return false;
}
std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
size_t pl = pattern_.length();
if (pl >= BIT_SEARCH_TRESHHOLD) {
size_t idx;
while (offset < len) {
if (is_in_pattern(text[offset], idx)) {
return {offset, idx};
}
offset++;
}
} else {
while (offset < len) {
if (size_t idx = index_of(text[offset]); idx != -1) {
return {offset, idx};
}
offset++;
}
}
return {-1, -1};
}
};
} // namespace simstr
namespace std {
/*!
* @ingroup StrExprs
* @ru @brief Унарный оператор+ для преобразования стандартных строк в строковые выражения.
* @details Стандартные строки могут напрямую участвовать в строковых выражениях только когда
* другой операнд тоже является строковым выражением. Если другой операнд - не строковое выражение,
* используйте этот оператор, чтобы превратить `std::basic_string` или `std::basic_string_view`
* в строковое выражение.
* @en @brief Unary operator + for converting standard strings to string expressions.
* @details Standard strings can only participate directly in string expressions when
* the other operand is also a string expression. If the other operand is not a string expression,
* use this operator to turn `std::basic_string` or `std::basic_string_view`
* into string expression.
* @ru Пример: @en Example @~
* ```cpp
* std::string make_text(const std::string& text, int count, std::string_view what, std::string_view what_p = ""sv) {
* return +text + " " + count + " " + e_choice(what_p.empty(), what + e_if(count > 1, "s"), e_choice(count > 1, +what_p, +what));
* }
* ```
*/
template<simstr::StdStrSource T>
simstr::expr_stdstr<typename T::value_type, T> operator+(const T& str) {
return {str};
}
} // namespace std