mirror of https://github.com/orefkov/simstr.git
5377 lines
260 KiB
C++
5377 lines
260 KiB
C++
/*
|
||
* ver. 1.8.1
|
||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||
* Классы для работы со строками
|
||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||
* Classes for working with strings
|
||
*/
|
||
|
||
/*!
|
||
* @ru @mainpage Библиотека simstr.
|
||
* @include{doc} "../readme_ru.md"
|
||
* @page overview Обзор
|
||
* @includedoc{doc} "overview_ru.md"
|
||
* @en @mainpage Simstr lib.
|
||
* @include{doc} "../readme.md"
|
||
* @page overview Overview
|
||
* @includedoc{doc} "overview.md"
|
||
*/
|
||
#pragma once
|
||
|
||
#ifndef __has_declspec_attribute
|
||
#define __has_declspec_attribute(x) 0
|
||
#endif
|
||
const bool isWindowsOs = // NOLINT
|
||
#ifdef _WIN32
|
||
true
|
||
#else
|
||
false
|
||
#endif
|
||
;
|
||
|
||
#ifdef SIMSTR_IN_SHARED
|
||
#if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
|
||
#ifdef SIMSTR_EXPORT
|
||
#define SIMSTR_API __declspec(dllexport)
|
||
#else
|
||
#define SIMSTR_API __declspec(dllimport)
|
||
#endif
|
||
#elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
|
||
#define SIMSTR_API __attribute__((visibility("default")))
|
||
#else
|
||
#define SIMSTR_API
|
||
#endif
|
||
#else
|
||
#define SIMSTR_API
|
||
#endif
|
||
|
||
#define IN_FULL_SIMSTR
|
||
#include "strexpr.h"
|
||
|
||
#include <format>
|
||
#include <unordered_map>
|
||
#include <atomic>
|
||
#include <memory>
|
||
#include <string.h>
|
||
#include <iostream>
|
||
#include <cmath>
|
||
|
||
#ifdef _WIN32
|
||
#include <stdio.h>
|
||
#endif
|
||
|
||
#ifdef _MSC_VER
|
||
// warning C4201 : nonstandard extension used : nameless struct / union
|
||
#pragma warning(disable : 4201)
|
||
#endif
|
||
|
||
namespace simstr {
|
||
|
||
template<typename T>
|
||
struct unicode_traits {}; // NOLINT
|
||
|
||
template<>
|
||
struct unicode_traits<u8s> {
|
||
// Эти операции с utf-8 могут изменить длину строки
|
||
// Поэтому их специализации отличаются
|
||
// В функцию помимо текста и адреса буфера для записи передается размер буфера
|
||
// Возвращает длину получающейся строки.
|
||
// Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
|
||
// обработанные символы, для повторного возобновления работы,
|
||
// а для оставшихся символов считается нужный размер буфера.
|
||
// These utf-8 operations can change the length of the string
|
||
// Therefore their specializations are different
|
||
// In addition to the text and address of the buffer for writing, the buffer size is passed to the function
|
||
// Returns the length of the resulting string.
|
||
// If the resulting string does not fit into the allocated buffer, pointers are set to the last
|
||
// processed characters to resume work again,
|
||
// and for the remaining characters the required buffer size is calculated.
|
||
static SIMSTR_API size_t upper(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
|
||
static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
|
||
static SIMSTR_API size_t upper_len(const u8s* src, size_t len);
|
||
static SIMSTR_API size_t lower_len(const u8s* src, size_t len);
|
||
|
||
static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
|
||
|
||
static SIMSTR_API size_t hashia(const u8s* src, size_t l);
|
||
static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
|
||
};
|
||
|
||
template<>
|
||
struct unicode_traits<u16s> {
|
||
static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
|
||
static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
|
||
|
||
static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
|
||
static SIMSTR_API size_t hashia(const u16s* src, size_t l);
|
||
static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
|
||
};
|
||
|
||
template<>
|
||
struct unicode_traits<u32s> {
|
||
static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
|
||
static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
|
||
|
||
static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
|
||
static SIMSTR_API size_t hashia(const u32s* src, size_t s);
|
||
static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
|
||
};
|
||
|
||
template<>
|
||
struct unicode_traits<wchar_t> {
|
||
static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
|
||
unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
|
||
}
|
||
static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
|
||
unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
|
||
}
|
||
|
||
static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
|
||
return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
|
||
}
|
||
static size_t hashia(const wchar_t* src, size_t s) {
|
||
return unicode_traits<wchar_type>::hashia(to_w(src), s);
|
||
}
|
||
static size_t hashiu(const wchar_t* src, size_t s) {
|
||
return unicode_traits<wchar_type>::hashiu(to_w(src), s);
|
||
}
|
||
};
|
||
|
||
template<>
|
||
struct unicode_traits<char8_t> {
|
||
static size_t upper(const ubs*& src, size_t lenStr, ubs*& dest, size_t lenBuf) {
|
||
return unicode_traits<char>::upper((const u8s*&)src, lenStr, (u8s*&)dest, lenBuf);
|
||
}
|
||
static size_t lower(const ubs*& src, size_t len, ubs*& dest, size_t lenBuf) {
|
||
return unicode_traits<char>::lower((const u8s*&)src, len, (u8s*&)dest, lenBuf);
|
||
}
|
||
static size_t upper_len(const ubs* src, size_t len) {
|
||
return unicode_traits<char>::upper_len((const u8s*)src, len);
|
||
}
|
||
static size_t lower_len(const ubs* src, size_t len) {
|
||
return unicode_traits<char>::lower_len((const u8s*)src, len);
|
||
}
|
||
|
||
static int compareiu(const char8_t* text1, size_t len1, const char8_t* text2, size_t len2) {
|
||
return unicode_traits<char>::compareiu((const char*)text1, len1, (const char*)text2, len2);
|
||
}
|
||
static size_t hashia(const char8_t* src, size_t s) {
|
||
return unicode_traits<char>::hashia((const char*)src, s);
|
||
}
|
||
static size_t hashiu(const char8_t* src, size_t s) {
|
||
return unicode_traits<char>::hashiu((const char*)src, s);
|
||
}
|
||
};
|
||
|
||
#if defined(_MSC_VER) && _MSC_VER <= 1933
|
||
template<typename K, typename... Args>
|
||
using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
|
||
#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
|
||
template<typename K, typename... Args>
|
||
using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
|
||
#else
|
||
template<typename K, typename... Args>
|
||
using FmtString = std::basic_string_view<K>;
|
||
#endif
|
||
|
||
template<typename K>
|
||
SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
|
||
|
||
/*!
|
||
* @ru @brief Класс с дополнительными константными строковыми алгоритмами.
|
||
* @details Дополняет алгоритмы из str_src_algs теми, которые связаны с упрощённым юникодом и парсингом double.
|
||
* @tparam K - тип символов.
|
||
* @tparam StrRef - тип хранилища куска строки.
|
||
* @tparam Impl - конечный класс наследник.
|
||
* @en @brief A class with additional constant string algorithms.
|
||
* @details Supplements the algorithms from str_src_algs with those related to simplified Unicode and double parsing.
|
||
* @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_algs : public str_src_algs<K, StrRef, 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 uni = unicode_traits<K>;
|
||
using uns_type = std::make_unsigned_t<K>;
|
||
using my_type = Impl;
|
||
using base = str_src_algs<K, StrRef, Impl, Mutable>;
|
||
str_algs() = default;
|
||
|
||
int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
|
||
if (!len)
|
||
return _is_empty() ? 0 : 1;
|
||
return uni::compareiu(_str(), _len(), text, len);
|
||
}
|
||
/*!
|
||
* @ru @brief Сравнение строк посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF).
|
||
* @param text - другая строка.
|
||
* @return <0 эта строка меньше, ==0 - строки равны, >0 - эта строка больше.
|
||
* @en @brief Compare strings character by character without taking into account the case of Unicode characters of the first plane (<0xFFFF).
|
||
* @param text - another string.
|
||
* @return <0 this string is less, ==0 - strings are equal, >0 - this string is greater.
|
||
*/
|
||
int compare_iu(str_piece text) const noexcept { // NOLINT
|
||
return compare_iu(text.symbols(), text.length());
|
||
}
|
||
/*!
|
||
* @ru @brief Равна ли строка другой строке посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF).
|
||
* @param text - другая строка.
|
||
* @return равны ли строки.
|
||
* @en @brief Whether a string is equal to another string, character-by-character-insensitive, of the Unicode characters of the first plane (<0xFFFF).
|
||
* @param text - another string.
|
||
* @return whether the strings are equal.
|
||
*/
|
||
bool equal_iu(str_piece text) const noexcept { // NOLINT
|
||
return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
|
||
}
|
||
/*!
|
||
* @ru @brief Меньше ли строка другой строки посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF).
|
||
* @param text - другая строка.
|
||
* @return меньше ли строка.
|
||
* @en @brief Whether a string is smaller than another string, character-by-character-insensitive, of the Unicode characters of the first plane (<0xFFFF).
|
||
* @param text - another string.
|
||
* @return whether the string is smaller.
|
||
*/
|
||
bool less_iu(str_piece text) const noexcept { // NOLINT
|
||
return compare_iu(text.symbols(), text.length()) < 0;
|
||
}
|
||
// Начинается ли эта строка с указанной подстроки без учета unicode регистра
|
||
// Does this string begin with the specified substring, insensitive to unicode case
|
||
bool starts_with_iu(const K* prefix, size_t len) const noexcept {
|
||
return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
|
||
}
|
||
/*!
|
||
* @ru @brief Начинается ли строка с заданной подстроки без учёта регистра Unicode символов первой плоскости (<0xFFFF).
|
||
* @param prefix - подстрока.
|
||
* @en @brief Whether the string starts with the given substring, case-insensitive Unicode characters of the first plane (<0xFFFF).
|
||
* @param prefix - substring.
|
||
*/
|
||
bool starts_with_iu(str_piece prefix) const noexcept {
|
||
return starts_with_iu(prefix.symbols(), prefix.length());
|
||
}
|
||
// Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
|
||
// Whether the string ends with the specified substring, case insensitive UNICODE
|
||
constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
|
||
size_t myLen = _len();
|
||
return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
|
||
}
|
||
/*!
|
||
* @ru @brief Заканчивается ли строка указанной подстрокой без учёта регистра Unicode символов первой плоскости (<0xFFFF).
|
||
* @param suffix - подстрока.
|
||
* @en @brief Whether the string ends with the specified substring, case-insensitive Unicode characters of the first plane (<0xFFFF).
|
||
* @param suffix - substring.
|
||
*/
|
||
constexpr bool ends_with_iu(str_piece suffix) const noexcept {
|
||
return ends_with_iu(suffix.symbols(), suffix.length());
|
||
}
|
||
/*!
|
||
* @ru @brief Получить копию строки в верхнем регистре Unicode символов первой плоскости (<0xFFFF).
|
||
* @tparam R - желаемый тип строки, по умолчанию тот же, чей метод вызывался.
|
||
* @return R - копию строки в верхнем регистре.
|
||
* @en @brief Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
|
||
* @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() const {
|
||
return R::upperred_from(d());
|
||
}
|
||
/*!
|
||
* @ru @brief Получить копию строки в нижнем регистре Unicode символов первой плоскости (<0xFFFF).
|
||
* @tparam R - желаемый тип строки, по умолчанию тот же, чей метод вызывался.
|
||
* @return R - копию строки в нижнем регистре.
|
||
* @en @brief Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
|
||
* @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() const {
|
||
return R::lowered_from(d());
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать строку в double.
|
||
* @return std::optional<double>.
|
||
* @en @brief Convert string to double.
|
||
* @return std::optional<double>.
|
||
*/
|
||
template<bool SkipWS = true, bool AllowPlus = true>
|
||
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 {};
|
||
}
|
||
#ifdef __linux__
|
||
if constexpr(sizeof(K) == 1 && requires {std::from_chars(std::declval<const K*>(), std::declval<const K*>(), std::declval<double&>()); }) {
|
||
double d{};
|
||
if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
|
||
return d;
|
||
}
|
||
return {};
|
||
}
|
||
#endif
|
||
if constexpr (sizeof(K) == 1) {
|
||
return impl_to_double((const char*)ptr, (const char*)ptr + len);
|
||
} else if constexpr (sizeof(K) == 2) {
|
||
return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
|
||
} else {
|
||
return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
|
||
}
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать строку в double.
|
||
* @param t - переменная, в которую записывается результат.
|
||
* @en @brief Convert string to double.
|
||
* @param t - the variable into which the result is written.
|
||
*/
|
||
void as_number(double& t) const {
|
||
auto res = to_double();
|
||
t = res ? *res : std::nan("0");
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать строку в целое число.
|
||
* @details Так как `as_number(double& t)` перекрывает видимость `as_number` из базового класса,
|
||
* придётся добавить его ещё раз.
|
||
* @tparam T - тип числа, выводится из аргумента.
|
||
* @param t - переменная, в которую записывается результат.
|
||
* @en @brief Convert a string to an integer.
|
||
* @details Since `as_number(double& t)` overrides the visibility of `as_number` from the base class,
|
||
* will have to add it again.
|
||
* @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 {
|
||
base::as_number(t);
|
||
}
|
||
/*!
|
||
* @ru @brief Получить строку без префикса, если она начинается с него без учёта регистра Unicode символов до 0xFFFF.
|
||
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
|
||
* @param prefix - искомый префикс.
|
||
* @return constexpr std::optional<R> - если строка начинается с указанного префикса без учёта регистра символов до 0xFFFF,
|
||
* возвращает часть строки без этого префикса, иначе пустое значение.
|
||
* @en @brief Get a string without a prefix if it starts with it, insensitive to Unicode characters up to 0xFFFF.
|
||
* @tparam R - the desired type of string object, defaults to str_piece.
|
||
* @param prefix - the prefix to search for.
|
||
* @return constexpr std::optional<R> - if the string begins with the specified prefix, insensitive to characters up to 0xFFFF,
|
||
* returns the part of the string without this prefix, otherwise empty.
|
||
*/
|
||
template<typename R = str_piece>
|
||
constexpr std::optional<R> strip_prefix_iu(str_piece prefix) const {
|
||
if (starts_with_iu(prefix)) {
|
||
return R{operator()(prefix.length())};
|
||
}
|
||
return {};
|
||
}
|
||
/*!
|
||
* @ru @brief Получить строку без суффикса, если она заканчивается им без учёта регистра Unicode символов до 0xFFFF.
|
||
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
|
||
* @param suffix - искомый суффикс.
|
||
* @return constexpr std::optional<R> - если строка заканчивается указанным суффиксом без учёта регистра символов до 0xFFFF,
|
||
* возвращает часть строки без него, иначе пустое значение.
|
||
* @en @brief Get a string without a suffix if it ends with one in case-insensitive Unicode characters up to 0xFFFF.
|
||
* @tparam R - the desired type of string object, defaults to str_piece.
|
||
* @param suffix - the suffix you are looking for.
|
||
* @return constexpr std::optional<R> - if the string ends with the specified suffix, insensitive to characters up to 0xFFFF,
|
||
* returns part of the string without it, otherwise empty.
|
||
*/
|
||
template<typename R = str_piece>
|
||
constexpr std::optional<R> strip_suffix_iu(str_piece suffix) const {
|
||
if (ends_with_iu(suffix)) {
|
||
return R{operator()(0, -suffix.length())};
|
||
}
|
||
return {};
|
||
}
|
||
/*!
|
||
* @ru @brief Возвращает строку, в которой удалено начало строки, если она начинается с указанного префикса без учёта регистра Unicode символов до 0xFFFF.
|
||
* @tparam R - желаемый тип строки, по умолчанию str_src.
|
||
* @param prefix - искомый префикс.
|
||
* @param max_count - до сколько раз можно удалять префикс, 0 - без ограничений.
|
||
* @return R - Копию строки, в которой удалено начало, если она начинается с этого префикса без учёта регистра символов до 0xFFFF.
|
||
* @en @brief Returns a string with the beginning of the string removed if it begins with the specified prefix, case-insensitive Unicode characters up to 0xFFFF.
|
||
* @tparam R - desired string type, default str_src.
|
||
* @param prefix - the prefix to search for.
|
||
* @param max_count - up to how many times a prefix can be removed, 0 - no restrictions.
|
||
* @return R - A copy of the string with the beginning removed if it starts with this prefix, insensitive to 0xFFFF.
|
||
*/
|
||
template<typename R = str_piece>
|
||
constexpr R trimmed_prefix_iu(str_piece prefix, size_t max_count = 0) const {
|
||
str_piece res = *this;
|
||
while(res.starts_with_iu(prefix)) {
|
||
res = res(prefix.length());
|
||
if (--max_count == 0) {
|
||
break;
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
/*!
|
||
* @ru @brief Возвращает строку, в которой удалён конец строки, если она заканчивается указанным суффиксом без учёта регистра Unicode символов до 0xFFFF.
|
||
* @tparam R - желаемый тип строки, по умолчанию str_src.
|
||
* @param suffix - искомый суффикс.
|
||
* @param max_count - до сколько раз можно удалять суффикс, 0 - без ограничений.
|
||
* @return R - Копию строки, в которой удалён конец, если она заканчивается этим суффиксом без учёта регистра символов до 0xFFFF.
|
||
* @en @brief Returns a string with the end of the string removed if it ends with the specified suffix of case-insensitive Unicode characters up to 0xFFFF.
|
||
* @tparam R - desired string type, default str_src.
|
||
* @param suffix - the suffix you are looking for.
|
||
* @param max_count - up to how many times a suffix can be removed, 0 - no restrictions.
|
||
* @return R - A copy of the string with the end removed if it ends with this suffix, insensitive to 0xFFFF.
|
||
*/
|
||
template<typename R = str_piece>
|
||
constexpr R trimmed_suffix_iu(str_piece suffix) const {
|
||
str_piece res = *this;
|
||
while(res.ends_with_iu(suffix)) {
|
||
res = res(0, -suffix.length());
|
||
}
|
||
return res;
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Базовая структура с информацией о строке.
|
||
* Это структура для не владеющих строк.
|
||
* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
|
||
* в результате размер класса не увеличивается
|
||
* Basic structure with string information.
|
||
* This is the structure for non-owning strings.
|
||
* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
|
||
* as a result the class size does not increase
|
||
*/
|
||
|
||
/*!
|
||
* @ru @brief Простейший класс иммутабельной не владеющей строки.
|
||
* @details Аналог std::string_view. Содержит только указатель и длину.
|
||
* Как наследник от str_algs поддерживает все константные строковые методы.
|
||
* @tparam K - тип символов строки.
|
||
* @en @brief The simplest immutable non-owning string class.
|
||
* @details Similar to std::string_view. Contains only a pointer and a length.
|
||
* As a descendant of str_algs, it supports all constant string methods.
|
||
* @tparam K - the character type of the string.
|
||
*/
|
||
template<typename K>
|
||
struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
|
||
using symb_type = K;
|
||
using my_type = simple_str<K>;
|
||
|
||
const symb_type* str;
|
||
size_t len;
|
||
|
||
constexpr simple_str() = default;
|
||
|
||
constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор из строкового литерала.
|
||
* @en @brief Constructor from a string literal.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
constexpr simple_str(T&& v) noexcept : str((const K*)v), len(N - 1) {}
|
||
/*!
|
||
* @ru @brief Конструктор из указателя и длины.
|
||
* @en @brief Constructor from pointer and length.
|
||
*/
|
||
constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
|
||
/*!
|
||
*@ru @brief Конструктор из std::basic_string.
|
||
*@en @brief Constructor from std::basic_string.
|
||
*/
|
||
template<typename A>
|
||
constexpr simple_str(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 simple_str(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(simple_str<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(simple_str<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 string 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;
|
||
}
|
||
};
|
||
|
||
template<typename K>
|
||
struct simple_str_selector {
|
||
using type = simple_str<K>;
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Класс, заявляющий, что ссылается на нуль-терминированную строку.
|
||
* @tparam K - тип символов строки.
|
||
* @details Служит для показа того, что функция параметром хочет получить
|
||
* строку с нулем в конце, например, ей надо дальше передавать его в
|
||
* стороннее API. Без этого ей надо было бы либо указывать параметром
|
||
* конкретный класс строки, что лишает универсальности, либо приводило бы
|
||
* к постоянным накладным расходам на излишнее копирование строк во временный
|
||
* буфер. Источником нуль-терминированных строк могут быть строковые литералы
|
||
* при компиляции, либо классы, хранящие строки.
|
||
* @en @brief A class that claims to refer to a null-terminated string.
|
||
* @tparam K - the character type of the string.
|
||
* @details 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 simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
|
||
using symb_type = K;
|
||
using my_type = simple_str_nt<K>;
|
||
using base = simple_str<K>;
|
||
|
||
constexpr static const K empty_string[1] = {0};
|
||
|
||
simple_str_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 is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
|
||
constexpr explicit simple_str_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 simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор из указателя и длины.
|
||
* @en @brief Constructor from pointer and length.
|
||
*/
|
||
constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
|
||
|
||
template<StrType<K> T>
|
||
constexpr simple_str_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 simple_str_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 string.
|
||
* @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 simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
|
||
|
||
template<typename K>
|
||
using Splitter = SplitterBase<K, simple_str<K>>;
|
||
|
||
using ssa = simple_str<u8s>;
|
||
using ssb = simple_str<ubs>;
|
||
using ssw = simple_str<wchar_t>;
|
||
using ssu = simple_str<u16s>;
|
||
using ssuu = simple_str<u32s>;
|
||
using stra = simple_str_nt<u8s>;
|
||
using strb = simple_str_nt<ubs>;
|
||
using strw = simple_str_nt<wchar_t>;
|
||
using stru = simple_str_nt<u16s>;
|
||
using struu = simple_str_nt<u32s>;
|
||
|
||
template<typename Src, typename Dest>
|
||
struct utf_convert_selector;
|
||
|
||
template<typename Src>
|
||
struct utf_convert_selector<Src, Src> {
|
||
static size_t need_len(const Src* src, size_t srcLen) {
|
||
return srcLen;
|
||
}
|
||
static size_t convert(const Src* src, size_t srcLen, Src* dest) {
|
||
ch_traits<Src>::copy(dest, src, srcLen + 1);
|
||
return srcLen;
|
||
}
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u8s, u16s> {
|
||
static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u8s, u32s> {
|
||
static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u16s, u8s> {
|
||
static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u16s, u32s> {
|
||
static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u32s, u8s> {
|
||
static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
|
||
};
|
||
|
||
template<>
|
||
struct utf_convert_selector<u32s, u16s> {
|
||
static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
|
||
static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Базовый класс для строк, могущих конвертироваться из другого типа символов.
|
||
* @tparam K - тип символов.
|
||
* @tparam Impl - конечный класс.
|
||
* @details Конвертация выполняется через UTF преобразование.
|
||
* Считаем, что строки `char` - в UTF-8, `char16_t` - в UTF-16, `char32_t` - в UTF-32.
|
||
* `wchar_t` - под Windows UTF-16, в Linux - UTF-32.
|
||
* @en @brief Base class for strings that can be converted from another character type.
|
||
* @tparam K - character type.
|
||
* @tparam Impl - final class.
|
||
* @details Conversion is performed via UTF conversion.
|
||
* We assume that the strings `char` are in UTF-8, `char16_t` - in UTF-16, `char32_t` - in UTF-32.
|
||
* `wchar_t` - in Windows UTF-16, in Linux - UTF-32.
|
||
*/
|
||
template<typename K, typename Impl>
|
||
class from_utf_convertible {
|
||
protected:
|
||
from_utf_convertible() = default;
|
||
using my_type = Impl;
|
||
/*
|
||
Эти методы должен реализовать класс-наследник.
|
||
вызывается только при создании объекта
|
||
init(size_t size)
|
||
*/
|
||
template<typename O>
|
||
requires(!std::is_same_v<O, K>)
|
||
void init_from_utf_convertible(simple_str<O> init) {
|
||
using from_t = to_base_char_t<O>;
|
||
using to_t = to_base_char_t<K>;
|
||
|
||
using worker = utf_convert_selector<from_t, to_t>;
|
||
Impl* d = static_cast<Impl*>(this);
|
||
size_t len = init.length();
|
||
if (!len)
|
||
d->create_empty();
|
||
else {
|
||
size_t need = worker::need_len((const from_t*)init.symbols(), len);
|
||
K* str = d->init(need);
|
||
str[need] = 0;
|
||
worker::convert((const from_t*)init.symbols(), len, (to_t*)str);
|
||
}
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Строковое выражение для конвертации строк в разные виды UTF.
|
||
* @tparam From - Тип какой строки конвертируем.
|
||
* @tparam To - В какого типа строку конвертируем.
|
||
* @en @brief String expression to convert strings to different UTF types.
|
||
* @tparam From - The type of which string we are converting.
|
||
* @tparam To - What type of string we convert to.
|
||
*/
|
||
template<typename From, typename To> requires (!std::is_same_v<From, To>)
|
||
struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
|
||
using symb_type = To;
|
||
using from_t = to_base_char_t<From>;
|
||
using to_t = to_base_char_t<To>;
|
||
using worker = utf_convert_selector<from_t, to_t>;
|
||
|
||
simple_str<From> source_;
|
||
|
||
constexpr expr_utf(simple_str<From> source) : source_(source){}
|
||
|
||
size_t length() const noexcept {
|
||
return worker::need_len((const from_t*)source_.symbols(), source_.length());
|
||
}
|
||
To* place(To* ptr) const noexcept {
|
||
return ptr + worker::convert((const from_t*)source_.symbols(), source_.length(), (to_t*)ptr);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Возвращает строковое выражение, преобразующую строку из одного типа символов
|
||
* в другой тип, через UTF-конвертирование.
|
||
* @tparam To - тип строки, в которую надо конвертировать.
|
||
* @tparam From - тип строки, из которого надо конвертировать. Выводится из аргумента.
|
||
* @param from - строка, из которой надо конвертировать.
|
||
* @en @brief Returns a string expression that converts a string of one character type
|
||
* to another type, via UTF conversion.
|
||
* @tparam To - the type of string to convert to.
|
||
* @tparam From - the type of string to convert from. Derived from the argument.
|
||
* @param from - the string from which to convert.
|
||
*/
|
||
template<typename To, typename From> requires (!std::is_same_v<From, To>)
|
||
expr_utf<From, To> e_utf(simple_str<From> from) {
|
||
return {from};
|
||
}
|
||
|
||
template<is_one_of_char_v K, bool upper>
|
||
struct expr_change_case : expr_to_std_string<expr_change_case<K, upper>>{
|
||
using symb_type = K;
|
||
|
||
str_src<K> src_;
|
||
mutable size_t len_{};
|
||
|
||
template<StrSource S>
|
||
expr_change_case(S&& s) : src_(std::forward<S>(s)){}
|
||
|
||
constexpr size_t length() const noexcept {
|
||
if constexpr (sizeof(K) > 1) {
|
||
return src_.length();
|
||
} else {
|
||
if constexpr (upper) {
|
||
len_ = unicode_traits<K>::upper_len(src_.str, src_.len);
|
||
} else {
|
||
len_ = unicode_traits<K>::lower_len(src_.str, src_.len);
|
||
}
|
||
return len_;
|
||
}
|
||
}
|
||
constexpr K* place(K* ptr) const noexcept {
|
||
if constexpr (sizeof(K) > 1) {
|
||
if constexpr (upper) {
|
||
unicode_traits<K>::upper(src_.str, src_.len, ptr);
|
||
} else {
|
||
unicode_traits<K>::lower(src_.str, src_.len, ptr);
|
||
}
|
||
return ptr + src_.len;
|
||
} else {
|
||
const K* src = src_.str;
|
||
if constexpr (upper) {
|
||
unicode_traits<K>::upper(src, src_.len, ptr, len_);
|
||
} else {
|
||
unicode_traits<K>::lower(src, src_.len, ptr, len_);
|
||
}
|
||
return ptr;
|
||
}
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Генерирует строку на основе исходной, заменяя все строчные буквы первой плоскости Юникода на прописные.
|
||
* @tparam K - тип символов, выводится на основе исходной строки.
|
||
* @details Берёт исходную строку и копирует её, заменяя строчные буквы первой плоскости Юникода на прописные.
|
||
* В качестве исходной строки может браться любой строковый объект.
|
||
* @en @brief Generates a string based on the original one, replacing all lowercase letters of the first Unicode plane with uppercase ones.
|
||
* @tparam K - character type, inferred based on the source string.
|
||
* @details Takes the original string and copies it, replacing the lowercase letters of the first Unicode plane with uppercase ones.
|
||
* Any string object can be taken as the source string.
|
||
* @ru Пример @en Example @~
|
||
* ```cpp
|
||
* stringa upper = "Upper case version is: '" + e_upper(source_str) + "'.";
|
||
* ```
|
||
*/
|
||
template<is_one_of_char_v K>
|
||
struct e_upper : expr_change_case<K, true> {
|
||
using base = expr_change_case<K, true>;
|
||
using base::base;
|
||
};
|
||
|
||
template<StrSource S>
|
||
e_upper(S&&) -> e_upper<symb_type_from_src_t<S>>;
|
||
|
||
/*!
|
||
* @ru @brief Генерирует строку на основе исходной, заменяя все прописные буквы первой плоскости Юникода на строчные.
|
||
* @tparam K - тип символов, выводится на основе исходной строки.
|
||
* @details Берёт исходную строку и копирует её, заменяя прописные буквы первой плоскости Юникода на строчные.
|
||
* В качестве исходной строки может браться любой строковый объект.
|
||
* @ru @brief Generate a string from the original one, replacing all uppercase letters of the first Unicode plane with lowercase ones.
|
||
* @tparam K - character type, inferred based on the source string.
|
||
* @details Takes the original string and copies it, replacing the uppercase letters of the first Unicode plane with lowercase ones.
|
||
* Any string object can be taken as the source string.
|
||
* @ru Пример @en Example @~
|
||
* ```cpp
|
||
* stringa lower = "Lower case version is: '" + e_lower(source_str) + "'.";
|
||
* ```
|
||
*/
|
||
template<is_one_of_char_v K>
|
||
struct e_lower : expr_change_case<K, false> {
|
||
using base = expr_change_case<K, false>;
|
||
using base::base;
|
||
};
|
||
|
||
template<StrSource S>
|
||
e_lower(S&&) -> e_lower<symb_type_from_src_t<S>>;
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, который может сохранить строку.
|
||
* @en @brief A type concept that can store a string.
|
||
*/
|
||
template<typename A, typename K>
|
||
concept storable_str = requires {
|
||
A::is_str_storable == true;
|
||
std::is_same_v<typename A::symb_type, K>;
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, который может модифицировать хранимую строку.
|
||
* @en @brief A type concept that can modify a stored string.
|
||
*/
|
||
template<typename A, typename K>
|
||
concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, который не может модифицировать хранимую строку.
|
||
* @en @brief A type concept that cannot modify a stored string.
|
||
*/
|
||
template<typename A, typename K>
|
||
concept immutable_str = storable_str<A, K> && !mutable_str<A, K>;
|
||
|
||
/*!
|
||
* @ru @brief База для объектов, владеющих строкой.
|
||
* @tparam K - тип символов.
|
||
* @tparam Impl - конечный класс наследник.
|
||
* @tparam Allocator - тип аллокатора.
|
||
* @details По прежнему ничего не знает о том, где наследник хранит строку и её размер.
|
||
* Просто вызывает его методы для получения места, и заполняет его при необходимости.
|
||
* Работает только при создании объекта, не работает с модификацией строки после
|
||
* ее создания и гарантирует, что если вызываются эти методы, объект еще только
|
||
* создается, и какого-либо расшаривания данных еще не было.
|
||
*
|
||
* Эти методы должен реализовать класс-наследник, вызываются только при создании объекта
|
||
* - `K* init(size_t size)` - выделить место для строки указанного размера, вернуть адрес
|
||
* - `void create_empty()` - создать пустой объект
|
||
* Хотя тип аллокатора и задаётся параметром шаблона, делается это только для проброса
|
||
* его типа в конструкторы, методы аллокатора не вызываются. Если наследник не пользуется
|
||
* аллокатором, а сам в `init` как-то выделяет место, может указать типом аллокатора
|
||
* какой-либо пустой класс.
|
||
* @en @brief The base for the objects that own the string.
|
||
* @tparam K - character type.
|
||
* @tparam Impl - the final class is the successor.
|
||
* @tparam Allocator - type of allocator.
|
||
* @details Still knows nothing about where the heir stores the string and its size.
|
||
* Simply calls its methods to get the space, and fills it as needed.
|
||
* Works only when creating an object, does not work with string modification after
|
||
* its creation and ensures that if these methods are called, the object is only
|
||
* is being created and no data sharing has yet taken place.
|
||
*
|
||
* These methods must be implemented by the descendant class and are called only when an object is created
|
||
* - `K* init(size_t size)` - allocate space for a string of the specified size, return the address
|
||
* - `void create_empty()` - create an empty object
|
||
* Although the allocator type is specified by the template parameter, this is done only for forwarding
|
||
* of its type in constructors, allocator methods are not called. If the heir does not use
|
||
* an allocator, and in `init` it somehow allocates space, can indicate the type of the allocator
|
||
* any empty class.
|
||
*/
|
||
template<typename K, typename Impl, typename Allocator>
|
||
class str_storable : protected Allocator {
|
||
public:
|
||
using my_type = Impl;
|
||
using traits = ch_traits<K>;
|
||
using allocator_t = Allocator;
|
||
using s_str = simple_str<K>;
|
||
using s_str_nt = simple_str_nt<K>;
|
||
|
||
protected:
|
||
/*!
|
||
* @ru @brief Получить аллокатор.
|
||
* @en @brief Get the allocator.
|
||
*/
|
||
constexpr allocator_t& allocator() {
|
||
return *static_cast<Allocator*>(this);
|
||
}
|
||
constexpr const allocator_t& allocator() const {
|
||
return *static_cast<const Allocator*>(this);
|
||
}
|
||
|
||
using uni = unicode_traits<K>;
|
||
|
||
constexpr Impl& d() noexcept {
|
||
return *static_cast<Impl*>(this);
|
||
}
|
||
constexpr const Impl& d() const noexcept {
|
||
return *static_cast<const Impl*>(this);
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Создать пустой объект.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Create an empty object.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
|
||
|
||
/*!
|
||
* @ru @brief Инициализация из другого строкового объекта.
|
||
* @param other - другой строковый объект, simple_str.
|
||
* @en @brief Initialization from another string object.
|
||
* @param other - another string object, simple_str.
|
||
*/
|
||
constexpr void init_from_str_other(s_str other) {
|
||
if (other.length()) {
|
||
K* ptr = d().init(other.length());
|
||
traits::copy(ptr, other.symbols(), other.length());
|
||
ptr[other.length()] = 0;
|
||
} else
|
||
d().create_empty();
|
||
}
|
||
/*!
|
||
* @ru @brief Инициализация повторением строки.
|
||
* @param repeat - количество повторов.
|
||
* @param pattern - строка, которую надо повторить.
|
||
* @en @brief String repetition initialization.
|
||
* @param repeat - number of repetitions.
|
||
* @param pattern - the string to be repeated.
|
||
*/
|
||
constexpr void init_str_repeat(size_t repeat, s_str pattern) {
|
||
size_t l = pattern.length(), allLen = l * repeat;
|
||
if (allLen) {
|
||
K* ptr = d().init(allLen);
|
||
for (size_t i = 0; i < repeat; i++) {
|
||
traits::copy(ptr, pattern.symbols(), l);
|
||
ptr += l;
|
||
}
|
||
*ptr = 0;
|
||
} else
|
||
d().create_empty();
|
||
}
|
||
/*!
|
||
* @ru @brief Инициализация повторением символа.
|
||
* @param count - количество повторов.
|
||
* @param pad - символ, который надо повторить.
|
||
* @en @brief Character repetition initialization.
|
||
* @param count - number of repetitions.
|
||
* @param pad - the character to be repeated.
|
||
*/
|
||
constexpr void init_symb_repeat(size_t count, K pad) {
|
||
if (count) {
|
||
K* str = d().init(count);
|
||
traits::assign(str, count, pad);
|
||
str[count] = 0;
|
||
} else
|
||
d().create_empty();
|
||
}
|
||
/*!
|
||
* @ru @brief Инициализация из строкового выражения.
|
||
* @param expr - строковое выражение.
|
||
* @details Запрашивает у строкового выражения `length()`,
|
||
* выделяет память нужного размера, и вызывает метод `place()` для размещения
|
||
* результата в буфере.
|
||
* @en @brief Initialization from a string expression.
|
||
* @param expr - string expression.
|
||
* @details Queries the string expression `length()`,
|
||
* allocates memory of the required size, and calls the `place()` method to allocate
|
||
* result in buffer.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
constexpr void init_str_expr(const A& expr) {
|
||
size_t len = expr.length();
|
||
if (len)
|
||
*expr.place((typename A::symb_type*)d().init(len)) = 0;
|
||
else
|
||
d().create_empty();
|
||
}
|
||
/*!
|
||
* @ru @brief Инициализация из строкового источника с заменой.
|
||
* @param f - строковый объект, из которого берётся исходная строка.
|
||
* @param pattern - подстрока, которую надо заменить.
|
||
* @param repl - строка, на которую надо заменить.
|
||
* @param offset - начальная позиция для поиска подстрок.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @en @brief Initialization from string source with replacement.
|
||
* @param f - the string object from which the source string is taken.
|
||
* @param pattern - substring to be replaced.
|
||
* @param repl - the string to be replaced with.
|
||
* @param offset - starting position for searching substrings.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
*/
|
||
template<StrType<K> From>
|
||
void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
|
||
auto findes = f.find_all(pattern, offset, maxCount);
|
||
if (!findes.size()) {
|
||
new (this) my_type{f};
|
||
return;
|
||
}
|
||
size_t srcLen = f.length();
|
||
size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
|
||
|
||
if (!newSize) {
|
||
new (this) my_type{};
|
||
return;
|
||
}
|
||
|
||
K* ptr = d().init(newSize);
|
||
const K* src = f.symbols();
|
||
size_t from = 0;
|
||
for (const auto& s: findes) {
|
||
size_t copyLen = s - from;
|
||
if (copyLen) {
|
||
traits::copy(ptr, src + from, copyLen);
|
||
ptr += copyLen;
|
||
}
|
||
if (repl.len) {
|
||
traits::copy(ptr, repl.str, repl.len);
|
||
ptr += repl.len;
|
||
}
|
||
from = s + pattern.len;
|
||
}
|
||
srcLen -= from;
|
||
if (srcLen) {
|
||
traits::copy(ptr, src + from, srcLen);
|
||
ptr += srcLen;
|
||
}
|
||
*ptr = 0;
|
||
}
|
||
|
||
public:
|
||
|
||
inline static constexpr bool is_str_storable = true;
|
||
/*!
|
||
* @ru @brief Оператор преобразования в нуль-терминированную C-строку.
|
||
* @return const K* - указатель на начало строки.
|
||
* @en @brief Conversion operator to a null-terminated C string.
|
||
* @return const K* - pointer to the beginning of the string.
|
||
*/
|
||
constexpr operator const K*() const noexcept {
|
||
return d().symbols();
|
||
}
|
||
/*!
|
||
* @ru @brief Получить simple_str_nt, начиная с заданного символа.
|
||
* @param from - позиция начального символа, по умолчанию 0.
|
||
* @return simple_str_nt,
|
||
* @en @brief Get simple_str_nt starting at the given character.
|
||
* @param from - position of the starting character, default 0.
|
||
* @return simple_str_nt,
|
||
*/
|
||
constexpr s_str_nt to_nts(size_t from = 0) const {
|
||
size_t len = d().length();
|
||
if (from >= len) {
|
||
from = len;
|
||
}
|
||
return {d().symbols() + from, len - from};
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать в simple_str_nt.
|
||
* @return simple_str_nt.
|
||
* @en @brief Convert to simple_str_nt.
|
||
* @return simple_str_nt.
|
||
*/
|
||
constexpr operator s_str_nt() const {
|
||
return {d().symbols(), d().length()};
|
||
}
|
||
/*!
|
||
* @ru @brief Конкатенация строк из контейнера в одну строку.
|
||
* @param strings - контейнер со строками.
|
||
* @param delimiter - разделитель, добавляемый между строками.
|
||
* @param tail - добавить разделитель после последней строки.
|
||
* @param skip_empty - пропускать пустые строки без добавления разделителя.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details Функция служит для слияния контейнера строк в одну строку с разделителем.
|
||
* ```cpp
|
||
* std::vector<ssa> strings = get_strings();
|
||
* ssa delim = get_current_delimiter();
|
||
* auto line = lstringa<200>::join(strings, delimiter);
|
||
* ```
|
||
* Стоит отметить, что при заранее известном разделителе лучше пользоваться строковым выражением `e_join`.
|
||
* ```cpp
|
||
* std::vector<ssa> strings = get_strings();
|
||
* lstringa<200> line{e_join(strings, "/")};
|
||
* ```
|
||
* В этом случае компилятор может лучше оптимизировать код слияния строк.
|
||
* @en @brief Concatenate strings from the container into one string.
|
||
* @param strings - container with strings.
|
||
* @param delimiter - delimiter added between lines.
|
||
* @param tail - add a separator after the last string.
|
||
* @param skip_empty - skip empty lines without adding a separator.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details The function is used to merge a container of strings into one delimited string.
|
||
* ```cpp
|
||
* std::vector<ssa> strings = get_strings();
|
||
* ssa delim = get_current_delimiter();
|
||
* auto line = lstringa<200>::join(strings, delimiter);
|
||
* ```
|
||
* It is worth noting that if the separator is known in advance, it is better to use the string expression `e_join`.
|
||
* ```cpp
|
||
* std::vector<ssa> strings = get_strings();
|
||
* lstringa<200> line{e_join(strings, "/")};
|
||
* ```
|
||
* In this case, the compiler can better optimize the string merging code.
|
||
*/
|
||
template<typename T, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
|
||
my_type result(std::forward<Args>(args)...);
|
||
if (strings.size()) {
|
||
if (strings.size() == 1 && (!delimiter.length() || !tail)) {
|
||
result = strings.front();
|
||
} else {
|
||
size_t commonLen = 0;
|
||
for (const auto& t: strings) {
|
||
size_t len = t.length();
|
||
if (len > 0 || !skip_empty) {
|
||
if (commonLen > 0) {
|
||
commonLen += delimiter.len;
|
||
}
|
||
commonLen += len;
|
||
}
|
||
}
|
||
commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
|
||
if (commonLen) {
|
||
K* ptr = result.init(commonLen);
|
||
K* write = ptr;
|
||
for (const auto& t: strings) {
|
||
size_t copyLen = t.length();
|
||
if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
|
||
ch_traits<K>::copy(write, delimiter.str, delimiter.len);
|
||
write += delimiter.len;
|
||
}
|
||
ch_traits<K>::copy(write, t.symbols(), copyLen);
|
||
write += copyLen;
|
||
}
|
||
if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
|
||
ch_traits<K>::copy(write, delimiter.str, delimiter.len);
|
||
write += delimiter.len;
|
||
}
|
||
*write = 0;
|
||
} else {
|
||
result.create_empty();
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
/*!
|
||
* @ru @brief Создать строку, копию переданной в верхнем регистре символов ASCII.
|
||
* @param f - строка источник.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Create a string copy of the passed in uppercase ASCII characters.
|
||
* @param f - source string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
|
||
return my_type{e_ascii_upper<K>{f}, std::forward<Args>(args)...};
|
||
}
|
||
/*!
|
||
* @ru @brief Создать копию переданной строки в нижнем регистре символов ASCII.
|
||
* @param f - строка источник.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Create a copy of the passed string in lowercase ASCII characters.
|
||
* @param f - source string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
|
||
return my_type{e_ascii_lower<K>{f}, std::forward<Args>(args)...};
|
||
}
|
||
/*!
|
||
* @ru @brief Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF).
|
||
* @param f - строка источник.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details Регистр меняется упрощенными таблицами, где один code_point всегда меняется в один code_point
|
||
* (но для UTF-8 возможно, что длина в code unit'ах изменится).
|
||
* @en @brief Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
|
||
* @param f - source string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details Case is changed by simplified tables, where one code_point is always changed to one code_point
|
||
* (but for UTF-8 it is possible that the length in code units will change).
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type upperred_from(const From& f, Args&&... args) {
|
||
return my_type{e_upper<K>{f}, std::forward<Args>(args)...};
|
||
}
|
||
/*!
|
||
* @ru @brief Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF).
|
||
* @param f - строка источник.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details Регистр меняется упрощенными таблицами, где один code_point всегда меняется в один code_point
|
||
* (но для UTF-8 возможно, что длина в code unit'ах изменится).
|
||
* @en @brief Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
|
||
* @param f - source string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details Case is changed by simplified tables, where one code_point is always changed to one code_point
|
||
* (but for UTF-8 it is possible that the length in code units will change).
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type lowered_from(const From& f, Args&&... args) {
|
||
return my_type{e_lower<K>{f}, std::forward<Args>(args)...};
|
||
}
|
||
/*!
|
||
* @ru @brief Создать копию переданной строки с заменой подстрок.
|
||
* @param f - строка источник.
|
||
* @param pattern - подстрока, которую надо заменить.
|
||
* @param repl - строка, на которую надо заменить.
|
||
* @param offset - начальная позиция для поиска подстрок.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Create a copy of the passed string with substrings replaced.
|
||
* @param f - source string.
|
||
* @param pattern - substring to be replaced.
|
||
* @param repl - the string to be replaced with.
|
||
* @param offset - starting position for searching substrings.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
|
||
return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, управляющего памятью
|
||
* @en @brief Concept of a memory management type
|
||
*/
|
||
template<typename A>
|
||
concept AllocatableNoSized = requires(A& a, size_t size, void* void_ptr) {
|
||
{ a.allocate(size) } -> std::same_as<void*>;
|
||
{ a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, управляющего памятью
|
||
* @en @brief Concept of a memory management type
|
||
*/
|
||
template<typename A>
|
||
concept AllocatableSized = requires(A& a, size_t size, void* void_ptr) {
|
||
{ a.allocate(size) } -> std::same_as<void*>;
|
||
{ a.deallocate(void_ptr, size) } noexcept -> std::same_as<void>;
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Концепт типа, управляющего памятью
|
||
* @en @brief Concept of a memory management type
|
||
*/
|
||
template<typename A>
|
||
concept Allocatable = AllocatableNoSized<A> || AllocatableSized<A>;
|
||
|
||
struct printf_selector {
|
||
template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
|
||
static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
|
||
if constexpr (sizeof(K) == 1) {
|
||
#ifndef _WIN32
|
||
return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
|
||
#else
|
||
// Поддерживает позиционные параметры
|
||
// Supports positional parameters
|
||
return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
|
||
#endif
|
||
} else {
|
||
#ifndef _WIN32
|
||
return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
|
||
#else
|
||
// Поддерживает позиционные параметры
|
||
// Supports positional parameters
|
||
return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
|
||
#endif
|
||
}
|
||
}
|
||
template<typename K> requires (is_one_of_std_char_v<K>)
|
||
static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
|
||
if constexpr (std::is_same_v<K, u8s>) {
|
||
#ifndef _WIN32
|
||
return std::vsnprintf(buffer, count, format, args);
|
||
#else
|
||
// Поддерживает позиционные параметры
|
||
// Supports positional parameters
|
||
return _vsprintf_p(buffer, count, format, args);
|
||
#endif
|
||
} else {
|
||
#ifndef _WIN32
|
||
return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
|
||
#else
|
||
// Поддерживает позиционные параметры
|
||
// Supports positional parameters
|
||
return _vswprintf_p(buffer, count, format, args);
|
||
#endif
|
||
}
|
||
}
|
||
};
|
||
|
||
inline size_t grow2(size_t ret, size_t currentCapacity) {
|
||
return ret <= currentCapacity ? ret : ret * 2;
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Базовый класс работы с изменяемыми строками
|
||
* @tparam K - тип символов
|
||
* @tparam Impl - конечный тип наследника
|
||
* @details По прежнему ничего не знает о том, где наследник хранит строку и её размер.
|
||
* Просто вызывает его методы для получения места, и заполняет его при необходимости.
|
||
* Для работы класс-наследник должен реализовать методы:
|
||
* - `size_t length() const noexcept` - возвращает длину строки
|
||
* - `const K* symbols() const` - возвращает указатель на начало строки
|
||
* - `bool is_empty() const noexcept` - проверка, не пустая ли строка
|
||
* - `K* str() noexcept` - Неконстантный указатель на начало строки
|
||
* - `K* set_size(size_t size)` - Изменить размер строки, как больше, так и меньше.
|
||
* Содержимое строки нужно оставить.
|
||
* - `K* reserve_no_preserve(size_t size)` - выделить место под строку, старую можно не сохранять
|
||
* - `K* alloc_for_copy(size_t size)` - выделить место для копии строки заданного размера, пока не изменяя
|
||
* саму строку, можно вернуть текущий буфер, если место позволяет.
|
||
* - `set_from_copy(K* str, size_t size)` - присвоить строку из памяти, ранее выделенной в alloc_for_copy.
|
||
* Если место выделялось в текущем буфере, ничего не делать.
|
||
* - `size_t capacity() const noexcept` - вернуть текущую ёмкость строки, сколько может поместится без аллокации.
|
||
* @en @brief Base class for working with mutable strings
|
||
* @tparam K - character type
|
||
* @tparam Impl - the final type of the successor
|
||
* @details Still knows nothing about where the heir stores the string and its size.
|
||
* Simply calls its methods to get the space, and fills it as needed.
|
||
* 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` - returns a pointer to the beginning of the line
|
||
* - `bool is_empty() const noexcept` - checks whether the string is empty
|
||
* - `K* str() noexcept` - Non-const pointer to the beginning of the string
|
||
* - `K* set_size(size_t size)` - Change the size of the string, either larger or smaller.
|
||
* The contents of the string must be left.
|
||
* - `K* reserve_no_preserve(size_t size)` - allocate space for a line, you don’t have to save the old one
|
||
* - `K* alloc_for_copy(size_t size)` - allocate space for a copy of a string of a given size, without changing it yet
|
||
* the string itself, you can return the current buffer if space allows.
|
||
* - `set_from_copy(K* str, size_t size)` - assign a string from memory previously allocated in alloc_for_copy.
|
||
* If space was allocated in the current buffer, do nothing.
|
||
* - `size_t capacity() const noexcept` - return the current capacity of the string, as much as can fit without allocation.
|
||
*/
|
||
template<typename K, typename Impl>
|
||
class str_mutable {
|
||
public:
|
||
using my_type = Impl;
|
||
|
||
private:
|
||
Impl& d() {
|
||
return *static_cast<Impl*>(this);
|
||
}
|
||
const Impl& d() const {
|
||
return *static_cast<const Impl*>(this);
|
||
}
|
||
size_t _len() const noexcept {
|
||
return d().length();
|
||
}
|
||
const K* _str() const noexcept {
|
||
return d().symbols();
|
||
}
|
||
using str_piece = simple_str<K>;
|
||
using symb_type = K;
|
||
using traits = ch_traits<K>;
|
||
using uni = unicode_traits<K>;
|
||
using uns_type = std::make_unsigned_t<K>;
|
||
|
||
template<typename Op>
|
||
Impl& make_trim_op(const Op& op) {
|
||
str_piece me = d(), pos = op(me);
|
||
if (me.length() != pos.length()) {
|
||
if (me.symbols() != pos.symbols())
|
||
traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
|
||
d().set_size(pos.length());
|
||
}
|
||
return d();
|
||
}
|
||
|
||
template<auto Op>
|
||
Impl& commonChangeCase() {
|
||
size_t len = _len();
|
||
if (len)
|
||
Op(_str(), len, str());
|
||
return d();
|
||
}
|
||
// GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
|
||
// только частичную. Поэтому добавим неиспользуемый параметр шаблона.
|
||
// GCC still does not allow full specialization of a nested class within a class,
|
||
// only partial. Resources additive unused parameter template.
|
||
template<typename T, bool Dummy = true>
|
||
struct CaseTraits {
|
||
static Impl& upper(Impl& obj) {
|
||
return obj.template commonChangeCase<unicode_traits<K>::upper>();
|
||
}
|
||
static Impl& lower(Impl& obj) {
|
||
return obj.template commonChangeCase<unicode_traits<K>::lower>();
|
||
}
|
||
};
|
||
|
||
template<auto Op>
|
||
Impl& utf8CaseChange() {
|
||
// Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
|
||
// For utf-8, such an operation can change the length of the string, so we make different specializations for them
|
||
size_t len = _len();
|
||
if (len) {
|
||
u8s* writePos = str();
|
||
const u8s *startData = writePos, *readPos = writePos;
|
||
size_t newLen = Op(readPos, len, writePos, len);
|
||
if (newLen < len) {
|
||
// Строка просто укоротилась
|
||
// The string was simply shortened
|
||
d().set_size(newLen);
|
||
} else if (newLen > len) {
|
||
// Строка не влезла в буфер.
|
||
// The string did not fit into the buffer.
|
||
size_t readed = static_cast<size_t>(readPos - startData);
|
||
size_t writed = static_cast<size_t>(writePos - startData);
|
||
d().set_size(newLen);
|
||
startData = str(); // при изменении размера могло изменится | may change when resizing
|
||
readPos = startData + readed;
|
||
writePos = const_cast<u8s*>(startData) + writed;
|
||
Op(readPos, len - readed, writePos, newLen - writed);
|
||
}
|
||
}
|
||
return d();
|
||
}
|
||
template<bool Dummy>
|
||
struct CaseTraits<u8s, Dummy> {
|
||
static Impl& upper(Impl& obj) {
|
||
return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
|
||
}
|
||
static Impl& lower(Impl& obj) {
|
||
return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
|
||
}
|
||
};
|
||
|
||
template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
|
||
Impl& makeTrim(T&& pattern) {
|
||
return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
|
||
}
|
||
|
||
template<TrimSides S, bool withSpaces>
|
||
Impl& makeTrim(str_piece pattern) {
|
||
return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
|
||
}
|
||
|
||
public:
|
||
/*!
|
||
* @ru @brief Получить указатель на буфер строки.
|
||
* @return K* - указатель на буфер строки.
|
||
* @en @brief Get a pointer to the string buffer.
|
||
* @return K* - pointer to the string buffer.
|
||
*/
|
||
K* str() noexcept {
|
||
return d().str();
|
||
}
|
||
/*!
|
||
* @ru @brief Получить указатель на буфер строки.
|
||
* @return K* - указатель на буфер строки.
|
||
* @en @brief Get a pointer to the string buffer.
|
||
* @return K* - pointer to the string buffer.
|
||
*/
|
||
explicit operator K*() noexcept {
|
||
return str();
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить пробельные символы в начале и в конце строки.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove whitespace from the beginning and end of a string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim() {
|
||
return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить пробельные символы в начале строки.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove whitespace at the beginning of a string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_left() {
|
||
return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить пробельные символы в конце строки.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove whitespace from the end of a string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_right() {
|
||
return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, в начале и в конце строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove characters included in a string literal at the beginning and end of the string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimAll, false>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, в начале строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove characters included in a string literal at the beginning of the string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim_left(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimLeft, false>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, в конце строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove characters included in a string literal at the end of the string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim_right(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimRight, false>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале и в конце строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in a string literal, as well as whitespace, at the beginning and end of the string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim_with_spaces(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimAll, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in a string literal, as well as whitespace, at the beginning of a string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @en @brief* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim_left_with_spaces(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimLeft, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в строковый литерал, а также пробельные символы, в конце строки.
|
||
* @param pattern - строковый литерал, содержащий символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in a string literal, as well as whitespace, at the end of a string.
|
||
* @param pattern is a string literal containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
requires is_const_pattern<N>
|
||
Impl& trim_right_with_wpaces(T&& pattern) {
|
||
return makeTrim<TrimSides::TrimRight, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, в начале и в конце строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string at the beginning and end of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim(str_piece pattern) {
|
||
return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, в начале строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string at the beginning of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_left(str_piece pattern) {
|
||
return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, в конце строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string from the end of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_right(str_piece pattern) {
|
||
return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, а также пробельные символы, в начале и в конце строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string, as well as whitespace characters, at the beginning and end of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_with_spaces(str_piece pattern) {
|
||
return makeTrim<TrimSides::TrimAll, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, а также пробельные символы, в начале строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string, as well as whitespace, at the beginning of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_left_with_spaces(str_piece pattern) {
|
||
return makeTrim<TrimSides::TrimLeft, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить символы, входящие в переданную строку, а также пробельные символы, в конце строки.
|
||
* @param pattern - строка, содержащая символы, которые надо удалить.
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Remove characters included in the passed string, as well as whitespace at the end of the string.
|
||
* @param pattern - a string containing the characters to be removed.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& trim_right_with_spaces(str_piece pattern) {
|
||
return makeTrim<TrimSides::TrimRight, true>(pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать в верхний регистр ASCII символы.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Convert ASCII characters to uppercase.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& upper_only_ascii() {
|
||
K* ptr = str();
|
||
for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
|
||
K s = *ptr;
|
||
if (isAsciiLower(s))
|
||
*ptr = s & ~0x20;
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать в нижний регистр ASCII символы.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Convert ASCII characters to lowercase.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& lower_only_ascii() {
|
||
K* ptr = str();
|
||
for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
|
||
K s = *ptr;
|
||
if (isAsciiUpper(s))
|
||
*ptr = s | 0x20;
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать в верхний регистр Unicode символы первой плоскости (<0xFFFF).
|
||
* @details Регистр меняется упрощенными таблицами, где один code_point всегда меняется в один code_point
|
||
* (но для UTF-8 возможно, что длина в code unit'ах изменится).
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Convert first plane characters (<0xFFFF) to uppercase Unicode.
|
||
* @details Case is changed by simplified tables, where one code_point is always changed to one code_point
|
||
* (but for UTF-8 it is possible that the length in code units will change).
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& upper() {
|
||
// Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
|
||
// For utf-8, such an operation can change the length of the string, so we make different specializations for them
|
||
return CaseTraits<K>::upper(d());
|
||
}
|
||
/*!
|
||
* @ru @brief Преобразовать в нижний регистр Unicode символы первой плоскости (<0xFFFF).
|
||
* @details Регистр меняется упрощенными таблицами, где один code_point всегда меняется в один code_point
|
||
* (но для UTF-8 возможно, что длина в code unit'ах изменится).
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Convert first plane characters (<0xFFFF) to lowercase Unicode.
|
||
* @details Case is changed by simplified tables, where one code_point is always changed to one code_point
|
||
* (but for UTF-8 it is possible that the length in code units will change).
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& lower() {
|
||
// Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
|
||
// For utf-8, such an operation can change the length of the string, so we make different specializations for them
|
||
return CaseTraits<K>::lower(d());
|
||
}
|
||
|
||
private:
|
||
template<typename T>
|
||
Impl& changeImpl(size_t from, size_t len, T expr) {
|
||
size_t myLen = _len();
|
||
if (from > myLen) {
|
||
from = myLen;
|
||
}
|
||
if (from + len > myLen) {
|
||
len = myLen - from;
|
||
}
|
||
K* buffer = str();
|
||
size_t otherLen = expr.length();
|
||
if (len == otherLen) {
|
||
expr.place(buffer + from);
|
||
} else {
|
||
size_t tailLen = myLen - from - len;
|
||
if (len > otherLen) {
|
||
expr.place(buffer + from);
|
||
traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
|
||
d().set_size(myLen - (len - otherLen));
|
||
} else {
|
||
buffer = d().set_size(myLen + otherLen - len);
|
||
traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
|
||
expr.place(buffer + from);
|
||
}
|
||
}
|
||
return d();
|
||
}
|
||
|
||
template<typename T>
|
||
Impl& appendImpl(T expr) {
|
||
if (size_t len = expr.length(); len) {
|
||
size_t size = _len();
|
||
expr.place(d().set_size(size + len) + size);
|
||
}
|
||
return d();
|
||
}
|
||
|
||
template<typename T>
|
||
Impl& appendFromImpl(size_t pos, T expr) {
|
||
if (pos > _len())
|
||
pos = _len();
|
||
if (size_t len = expr.length())
|
||
expr.place(d().set_size(pos + len) + pos);
|
||
else
|
||
d().set_size(pos);
|
||
return d();
|
||
}
|
||
|
||
public:
|
||
inline static constexpr bool is_str_mutable = true;
|
||
/*!
|
||
* @ru @brief Добавить другую строку в конец строки.
|
||
* @param other - другая строка.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add another string to the end of the string.
|
||
* @param other - another string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& append(str_piece other) {
|
||
return appendImpl<str_piece>(other);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить строковое выражение в конец строки.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add a string expression to the end of the string.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& append(const A& expr) {
|
||
return appendImpl<const A&>(expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить другую строку в конец строки.
|
||
* @param other - другая строка.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add another string to the end of the string.
|
||
* @param other - another string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& operator+=(str_piece other) {
|
||
return appendImpl<str_piece>(other);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить строковое выражение в конец строки.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add a string expression to the end of the string.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& operator+=(const A& expr) {
|
||
return appendImpl<const A&>(expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить другую строку, начиная с заданной позиции.
|
||
* @param pos - позиция, с которой добавлять. Сначала строка укорачивается до заданного
|
||
* размера, а потом добавляется другая строка.
|
||
* @param other - другая строка.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details Если строка длиинее`pos`, то она укорачивается до этого размера, а потом добавляется `other`.
|
||
* @en @brief Add another string starting at the given position.
|
||
* @param pos - the position from which to add. First, the string is shortened to the specified value
|
||
* size, and then another string is added.
|
||
* @param other - another string.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details If the string is longer than `pos`, then it is shortened to this size, and then `other` is added.
|
||
*/
|
||
Impl& append_in(size_t pos, str_piece other) {
|
||
return appendFromImpl<str_piece>(pos, other);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить строковое выражение, начиная с заданной позиции.
|
||
* @param pos - позиция, с которой добавлять. Сначала строка укорачивается до заданного
|
||
* размера, а потом добавляется строковое выражение.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details Если строка длиннее`pos`, то она укорачивается до этого размера, а потом добавляется `expr`.
|
||
* @en @brief Add a string expression starting at the given position.
|
||
* @param pos - the position from which to add. First, the string is shortened to the specified value
|
||
* size, and then a string expression is added.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details If the string is longer than `pos`, then it is shortened to this size, and then `expr` is added.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& append_in(size_t pos, const A& expr) {
|
||
return appendFromImpl<const A&>(pos, expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Заменить кусок строки на другую строку.
|
||
* @param from - начальная позиция для замены.
|
||
* @param len - длина заменяемой части.
|
||
* @param other - строка, на которую эта часть меняется .
|
||
* @return Impl& - ссылку на себя же
|
||
* @en @brief Replace a piece of string with another string.
|
||
* @param from - starting position for replacement.
|
||
* @param len - length of the part to be replaced.
|
||
* @param other - the string this part is changed to.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& change(size_t from, size_t len, str_piece other) {
|
||
return changeImpl<str_piece>(from, len, other);
|
||
}
|
||
/*!
|
||
* @ru @brief Заменить кусок строки на строковое выражение.
|
||
* @param from - начальная позиция для замены.
|
||
* @param len - длина заменяемой части.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Replace a piece of string with a string expression.
|
||
* @param from - starting position for replacement.
|
||
* @param len - length of the part to be replaced.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& change(size_t from, size_t len, const A& expr) {
|
||
return changeImpl<const A&>(from, len, expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Вставить строку в указанную позицию.
|
||
* @param to - позиция для вставки.
|
||
* @param other - вставляемая строка.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Insert a string at the specified position.
|
||
* @param to - insertion position.
|
||
* @param other - the string to be inserted.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& insert(size_t to, str_piece other) {
|
||
return changeImpl<str_piece>(to, 0, other);
|
||
}
|
||
/*!
|
||
* @ru @brief Вставить строковое выражение в указанную позицию.
|
||
* @param to - позиция для вставки.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Insert a string expression at the specified position.
|
||
* @param to - insertion position.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& insert(size_t to, const A& expr) {
|
||
return changeImpl<const A&>(to, 0, expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Удалить часть строки.
|
||
* @param from - позиция, с которой удалить.
|
||
* @param len - длина удаляемой части.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Remove part of a string.
|
||
* @param from - the position from which to delete.
|
||
* @param len - length of the part to be deleted.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& remove(size_t from, size_t len) {
|
||
return changeImpl<const empty_expr<K>&>(from, len, {});
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить другую строку в начало строки.
|
||
* @param other - другая строка.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add another string to the beginning of the string.
|
||
* @param other - another string.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& prepend(str_piece other) {
|
||
return changeImpl<str_piece>(0, 0, other);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавить строковое выражение в начало строки.
|
||
* @param expr - строковое выражение.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Add a string expression to the beginning of a string.
|
||
* @param expr - string expression.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrExprForType<K> A>
|
||
Impl& prepend(const A& expr) {
|
||
return changeImpl<const A&>(0, 0, expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Заменить вхождения подстроки на другую строку.
|
||
* @param pattern - искомая подстрока.
|
||
* @param repl - строка замены.
|
||
* @param offset - начальная позиция для поиска.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Replace occurrences of a substring with another string.
|
||
* @param pattern - the substring to search for.
|
||
* @param repl - replacement string.
|
||
* @param offset - the starting position for the search.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
|
||
offset = d().find(pattern, offset);
|
||
if (offset == str::npos) {
|
||
return d();
|
||
}
|
||
if (!maxCount)
|
||
maxCount--;
|
||
size_t replLength = repl.length(), patternLength = pattern.length();
|
||
|
||
if (patternLength == replLength) {
|
||
// Заменяем inplace на подстроку такой же длины
|
||
// Replace inplace with a substring of the same length
|
||
K* ptr = str();
|
||
while (maxCount--) {
|
||
traits::copy(ptr + offset, repl.symbols(), replLength);
|
||
offset = d().find(pattern, offset + replLength);// replLength == patternLength
|
||
if (offset == str::npos)
|
||
break;
|
||
}
|
||
} else if (patternLength > replLength) {
|
||
// Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
|
||
// Replace with a shorter piece, the length of the text will decrease, go from left to right
|
||
K* ptr = str();
|
||
traits::copy(ptr + offset, repl.symbols(), replLength);
|
||
size_t posWrite = offset + replLength;
|
||
offset += patternLength;
|
||
|
||
while (--maxCount) {
|
||
size_t idx = d().find(pattern, offset);
|
||
if (idx == str::npos)
|
||
break;
|
||
size_t lenOfPiece = idx - offset;
|
||
traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
|
||
posWrite += lenOfPiece;
|
||
traits::copy(ptr + posWrite, repl.symbols(), replLength);
|
||
posWrite += replLength;
|
||
offset = idx + patternLength;
|
||
}
|
||
size_t tailLen = _len() - offset;
|
||
traits::move(ptr + posWrite, ptr + offset, tailLen);
|
||
d().set_size(posWrite + tailLen);
|
||
} else {
|
||
struct replace_grow_helper {
|
||
replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
|
||
: source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
|
||
my_type& source;
|
||
const str_piece pattern;
|
||
const str_piece repl;
|
||
size_t maxCount;
|
||
const size_t delta;
|
||
size_t all_delta{};
|
||
K* reserve_for_copy{};
|
||
size_t end_of_piece{};
|
||
size_t total_length{};
|
||
|
||
void replace(size_t offset) {
|
||
size_t found[16] = {offset};
|
||
maxCount--;
|
||
offset += pattern.length();
|
||
all_delta += delta;
|
||
size_t idx = 1;
|
||
for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
|
||
found[idx] = source.find(pattern, offset);
|
||
if (found[idx] == str::npos) {
|
||
break;
|
||
}
|
||
offset = found[idx] + pattern.length();
|
||
all_delta += delta;
|
||
}
|
||
if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
|
||
replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
|
||
}
|
||
// Теперь делаем свои замены
|
||
// Now we make our replacements
|
||
if (!reserve_for_copy) {
|
||
// Только начинаем
|
||
// Just getting started
|
||
end_of_piece = source.length();
|
||
total_length = end_of_piece + all_delta;
|
||
reserve_for_copy = source.alloc_for_copy(total_length);
|
||
}
|
||
K* dst_start = reserve_for_copy;
|
||
const K* src_start = source.symbols();
|
||
while(idx-- > 0) {
|
||
size_t pos = found[idx] + pattern.length();
|
||
size_t lenOfPiece = end_of_piece - pos;
|
||
ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
|
||
ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
|
||
all_delta -= delta;
|
||
end_of_piece = found[idx];
|
||
}
|
||
if (!all_delta && reserve_for_copy != src_start) {
|
||
ch_traits<K>::copy(dst_start, src_start, found[0]);
|
||
}
|
||
}
|
||
} helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
|
||
helper.replace(offset);
|
||
d().set_from_copy(helper.reserve_for_copy, helper.total_length);
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Скопировать строку-источник, заменив вхождения подстрок на другую строку.
|
||
* @param f - строка-источник.
|
||
* @param pattern - искомая подстрока.
|
||
* @param repl - строка замены.
|
||
* @param offset - начальная позиция для поиска.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Copy the source string, replacing occurrences of substrings with another string.
|
||
* @param f - source string.
|
||
* @param pattern - the substring to search for.
|
||
* @param repl - replacement string.
|
||
* @param offset - the starting position for the search.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<StrType<K> From>
|
||
Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
|
||
if (pattern.length() >= repl.length()) {
|
||
K* dst = d().reserve_no_preserve(f.length());
|
||
const K* src = f.symbols();
|
||
size_t delta = 0;
|
||
if (maxCount == 0) {
|
||
maxCount--;
|
||
}
|
||
size_t src_length = f.length(), start = 0;
|
||
while (maxCount--) {
|
||
offset = f.find(pattern, offset);
|
||
if (offset == str::npos) {
|
||
break;
|
||
}
|
||
size_t piece_len = offset - start;
|
||
if (piece_len) {
|
||
ch_traits<K>::copy(dst, src + start, piece_len);
|
||
dst += piece_len;
|
||
}
|
||
if (repl.length()) {
|
||
ch_traits<K>::copy(dst, repl.symbols(), repl.length());
|
||
dst += repl.length();
|
||
}
|
||
delta += pattern.length() - repl.length();
|
||
offset += pattern.length();
|
||
start = offset;
|
||
}
|
||
if (start < src_length) {
|
||
ch_traits<K>::copy(dst, src + start, src_length - start);
|
||
}
|
||
d().set_size(src_length - delta);
|
||
} else {
|
||
d() = f;
|
||
replace(pattern, repl, offset, maxCount);
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Заполнение буфера строки с помощью функтора.
|
||
* @param from - начальная позиция для заполнения.
|
||
* @param fillFunction - size_t(K*, size_t) функтор, получающий адрес буфера строки и его ёмкость,
|
||
* возвращающий необходимый размер строки.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details Функция вызывает функтор, передавая ему адрес буфера строки и его ёмкость.
|
||
* Функтор может изменять буфер в пределах выделенной ёмкости, и должен вернуть размер итоговой строки.
|
||
* Пока возвращаемый размер больше ёмкости (т.е. строка не может поместиться в буфер),
|
||
* выделятся память как минимум возвращенного размера, и функтор вызывается снова.
|
||
* До тех пор, пока возвращённый размер не будет помещаться в буфер строки.
|
||
* Этот размер и становится длиной строки.
|
||
* @en @brief Fill a string buffer using a functor.
|
||
* @param from - starting position to fill.
|
||
* @param fillFunction - size_t(K*, size_t) functor that receives the address of the string buffer and its capacity,
|
||
* returning the required string size.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details The function calls the functor, passing it the address of the string buffer and its capacity.
|
||
* The functor can modify the buffer within the allocated capacity, and must return the size of the resulting string.
|
||
* As long as the returned size is larger than capacity (i.e. the string cannot fit into the buffer),
|
||
* memory of at least the returned size is allocated and the functor is called again.
|
||
* Until the returned size fits into the string buffer.
|
||
* This size becomes the length of the string.
|
||
*/
|
||
template<typename Op>
|
||
Impl& fill(size_t from, const Op& fillFunction) {
|
||
size_t size = _len();
|
||
if (from > size)
|
||
from = size;
|
||
size_t capacity = d().capacity();
|
||
K* ptr = str();
|
||
capacity -= from;
|
||
for (;;) {
|
||
size_t needSize = static_cast<size_t>(fillFunction(ptr + from, capacity));
|
||
if (capacity >= needSize) {
|
||
d().set_size(from + needSize);
|
||
break;
|
||
}
|
||
ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
|
||
capacity = d().capacity() - from;
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Заполняет строку методом fill с нулевой позиции.
|
||
* @param fillFunction - функтор заполнения строки, size_t(K*, size_t).
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Fills a string with the fill method from position zero.
|
||
* @param fillFunction - string filling functor, size_t(K*, size_t).
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename Op>
|
||
requires std::is_invocable_v<Op, K*, size_t>
|
||
Impl& operator<<(const Op& fillFunction) {
|
||
return fill(0, fillFunction);
|
||
}
|
||
/*!
|
||
* @ru @brief Заполняет строку методом fill после конца строки.
|
||
* @param fillFunction - функтор заполнения строки, size_t(K*, size_t).
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Fills a string with the fill method after the end of the string.
|
||
* @param fillFunction - string filling functor, size_t(K*, size_t).
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename Op>
|
||
requires std::is_invocable_v<Op, K*, size_t>
|
||
Impl& operator<<=(const Op& fillFunction) {
|
||
return fill(_len(), fillFunction);
|
||
}
|
||
/*!
|
||
* @ru @brief Вызывает переданный функтор, передав ссылку на себя.
|
||
* @param fillFunction - фуктор void(my_type&).
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Calls the passed functor, passing a reference to itself.
|
||
* @param fillFunction - фуктор void(my_type&).
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename Op>
|
||
requires std::is_invocable_v<Op, my_type&>
|
||
Impl& operator<<(const Op& fillFunction) {
|
||
fillFunction(d());
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью sprintf вывод, начиная с указанной позиции.
|
||
* @param from - начальная позиция добавления.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для sprintf.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends sprintf formatted output starting at the specified position.
|
||
* @param from - starting position of adding.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for sprintf.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& printf_from(size_t from, const K* format, T&&... args) {
|
||
size_t size = _len();
|
||
if (from > size)
|
||
from = size;
|
||
size_t capacity = d().capacity();
|
||
K* ptr = str();
|
||
capacity -= from;
|
||
|
||
int result = 0;
|
||
// Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
|
||
// а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
|
||
// Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
|
||
// Here's a dirty hack for u8s and wide_char. u8s version of snprintf immediately returns the size of the required buffer if it is small
|
||
// and swprintf returns -1. Under Windows, both options xxx_p also return -1.
|
||
// Therefore, for them you need to stupidly increase the buffer at random until it fits
|
||
if constexpr (sizeof(K) == 1 && !isWindowsOs) {
|
||
result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
|
||
if (result > (int)capacity) {
|
||
ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
|
||
result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
|
||
}
|
||
} else {
|
||
for (;;) {
|
||
result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
|
||
if (result < 0) {
|
||
// Не хватило буфера или ошибка конвертации.
|
||
// Попробуем увеличить буфер в два раза
|
||
// Not enough buffer or conversion error.
|
||
// Let's try to double the buffer
|
||
capacity *= 2;
|
||
ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
|
||
} else
|
||
break;
|
||
}
|
||
}
|
||
if (result < 0)
|
||
d().set_size(static_cast<size_t>(traits::length(_str())));
|
||
else
|
||
d().set_size(from + result);
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Форматирует строку помощью sprintf.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для sprintf.
|
||
* @return Impl& - ссылку на себя же
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Formats a string using sprintf.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for sprintf.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& printf(const K* format, T&&... args) {
|
||
return printf_from(0, format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью sprintf вывод в конец строки.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для sprintf.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends sprintf formatted output to the end of the string.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for sprintf.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the row buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& append_printf(const K* format, T&&... args) {
|
||
return printf_from(_len(), format, std::forward<T>(args)...);
|
||
}
|
||
|
||
struct writer {
|
||
my_type* store;
|
||
K* ptr;
|
||
const K* end;
|
||
size_t max_write;
|
||
size_t writed = 0;
|
||
inline static K pad;
|
||
K& operator*() const {
|
||
return *ptr;
|
||
}
|
||
writer& operator++() {
|
||
if (writed < max_write) {
|
||
++ptr;
|
||
if (ptr == end) {
|
||
size_t l = ptr - store->begin();
|
||
store->set_size(l);
|
||
ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
|
||
end = store->end();
|
||
}
|
||
} else {
|
||
ptr = &pad;
|
||
}
|
||
return *this;
|
||
}
|
||
writer operator++(int) {
|
||
auto ret = *this;
|
||
operator++();
|
||
return ret;
|
||
}
|
||
|
||
writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
|
||
writer() = default;
|
||
writer(const writer&) = delete;
|
||
writer& operator=(const writer&) noexcept = delete;
|
||
writer(writer&&) noexcept = default;
|
||
writer& operator=(writer&&) noexcept = default;
|
||
using difference_type = int;
|
||
};
|
||
using fmt_type = to_std_char_t<K>;
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью std::format вывод, начиная с указанной позиции.
|
||
* @param from - начальная позиция добавления.
|
||
* @param format - форматная строка, константная.
|
||
* @param ...args - аргументы для std::format.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends std::format-formatted output starting at the specified position.
|
||
* @param from - starting position of adding.
|
||
* @param format - format string, constant.
|
||
* @param ...args - arguments for std::format.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
|
||
size_t size = _len();
|
||
if (from > size)
|
||
from = size;
|
||
size_t capacity = d().capacity();
|
||
K* ptr = str();
|
||
|
||
auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
|
||
std::forward<decltype(format)>(format), std::forward<T>(args)...);
|
||
d().set_size(result.ptr - _str());
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью std::vformat вывод, начиная с указанной позиции.
|
||
* @param from - начальная позиция добавления.
|
||
* @param max_write - максимальное количество записываемых символов.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для std::vformat.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends std::vformat formatted output starting at the specified position.
|
||
* @param from - starting position of adding.
|
||
* @param max_write - the maximum number of characters to write.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for std::vformat.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
|
||
size_t size = _len();
|
||
if (from > size)
|
||
from = size;
|
||
size_t capacity = d().capacity();
|
||
K* ptr = str();
|
||
|
||
if constexpr (std::is_same_v<K, u8s>) {
|
||
auto result = std::vformat_to(
|
||
writer{d(), ptr + from, ptr + capacity, max_write},
|
||
std::basic_string_view<K>{format.symbols(), format.length()},
|
||
std::make_format_args(args...));
|
||
d().set_size(result.ptr - _str());
|
||
} else {
|
||
auto result = std::vformat_to(
|
||
writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
|
||
std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
|
||
std::make_wformat_args(std::forward<T>(args)...));
|
||
d().set_size(result.ptr - _str());
|
||
}
|
||
return d();
|
||
}
|
||
/*!
|
||
* @ru @brief Форматирует строку с помощью std::format.
|
||
* @param pattern - форматная строка, константная.
|
||
* @param ...args - аргументы для std::format.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Formats a string using std::format.
|
||
* @param pattern - format string, constant.
|
||
* @param ...args - arguments for std::format.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
|
||
return format_from(0, pattern, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью std::format вывод в конец строки.
|
||
* @param format - форматная строка, константная.
|
||
* @param ...args - аргументы для std::format.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends std::format-formatted output to the end of the string.
|
||
* @param format - format string, constant.
|
||
* @param ...args - arguments for std::format.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
|
||
return format_from(_len(), format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Форматирует строку с помощью std::vformat.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для std::vformat.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Formats a string using std::vformat.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for std::vformat.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& vformat(str_piece format, T&&... args) {
|
||
return vformat_from(0, -1, format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью std::vformat вывод в конец строки.
|
||
* @param format - форматная строка.
|
||
* @param ... - аргументы для std::vformat.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends std::vformat-formatted output to the end of the string.
|
||
* @param format - format string.
|
||
* @param ... - arguments for std::vformat.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& append_vformatted(str_piece format, T&&... args) {
|
||
return vformat_from(_len(), -1, format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Форматирует строку с помощью std::vformat не более указанного размера.
|
||
* @param max_write - максимальное количество записываемых символов.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для std::vformat.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Formats a string using std::vformat up to the specified size.
|
||
* @param max_write - the maximum number of characters to write.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for std::vformat.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
|
||
return vformat_from(0, max_write, format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Добавляет отформатированный с помощью std::vformat вывод в конец строки, записывая не более указанного количества символов.
|
||
* @param max_write - максимальное количество записываемых символов.
|
||
* @param format - форматная строка.
|
||
* @param ...args - аргументы для std::vformat.
|
||
* @return Impl& - ссылку на себя же
|
||
* @details При необходимости автоматически увеличивает размер буфера строки.
|
||
* @en @brief Appends std::vformat-formatted output to the end of the line, writing no more than the specified number of characters.
|
||
* @param max_write - the maximum number of characters to write.
|
||
* @param format - format string.
|
||
* @param ...args - arguments for std::vformat.
|
||
* @return Impl& - a reference to yourself.
|
||
* @details Automatically increases the string buffer size if necessary.
|
||
*/
|
||
template<typename... T> requires (is_one_of_std_char_v<K>)
|
||
Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
|
||
return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
|
||
}
|
||
/*!
|
||
* @ru @brief Вызов функтора со строкой и переданными аргументами.
|
||
* @param fillFunction - функтор, принимающий первым параметром ссылку на строку.
|
||
* @param ...args - аргументы, передаваемые в функтор.
|
||
* @return Impl& - ссылку на себя же.
|
||
* @en @brief Call a functor with a string and passed arguments.
|
||
* @param fillFunction - a functor that takes a string reference as its first parameter.
|
||
* @param ...args - arguments passed to the functor.
|
||
* @return Impl& - a reference to yourself.
|
||
*/
|
||
template<typename Op, typename... Args>
|
||
Impl& with(const Op& fillFunction, Args&&... args) {
|
||
fillFunction(d(), std::forward<Args>(args)...);
|
||
return d();
|
||
}
|
||
};
|
||
|
||
template<typename K>
|
||
struct SharedStringData {
|
||
std::atomic_size_t ref_; // Счетчик ссылок | Reference count
|
||
inline static constexpr size_t mask = (size_t(-1)) >> 1, check = ~mask;
|
||
|
||
SharedStringData() {
|
||
ref_ = 1;
|
||
}
|
||
SharedStringData(size_t capacity) {
|
||
ref_ = 1 | check;
|
||
reinterpret_cast<size_t*>(this)[-1] = (capacity + 1) * sizeof(K) + sizeof(*this) + sizeof(size_t);
|
||
}
|
||
K* str() const {
|
||
return (K*)(this + 1);
|
||
}
|
||
void incr() {
|
||
ref_.fetch_add(1, std::memory_order_relaxed);
|
||
}
|
||
void decr(AllocatableNoSized auto& allocator) {
|
||
size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
|
||
if (val == 1) {
|
||
allocator.deallocate(this);
|
||
}
|
||
}
|
||
void decr(AllocatableSized auto& allocator, size_t len) {
|
||
size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
|
||
if ((val & mask) == 1) {
|
||
if (val & check) {
|
||
// Это блок от перемещённой lstring, перед ним записан размер выделенной памяти.
|
||
// This is a block from the moved lstring, the size of the allocated memory is written in front of it.
|
||
size_t* ptr = reinterpret_cast<size_t*>(this);
|
||
ptr--;
|
||
allocator.deallocate(ptr, *ptr);
|
||
} else {
|
||
allocator.deallocate(this, (len + 1) * sizeof(K) + sizeof(*this));
|
||
}
|
||
}
|
||
}
|
||
static SharedStringData* create(size_t l, Allocatable auto& allocator) {
|
||
size_t size = sizeof(SharedStringData) + (l + 1) * sizeof(K);
|
||
return new (allocator.allocate(size)) SharedStringData();
|
||
}
|
||
static SharedStringData* from_str(const K* p) {
|
||
return (SharedStringData*)p - 1;
|
||
}
|
||
K* place(K* p, size_t len) {
|
||
ch_traits<K>::copy(p, str(), len);
|
||
return p + len;
|
||
}
|
||
};
|
||
|
||
// Дефолтный аллокатор для строк, может работать статически
|
||
// Default allocator for strings, can work statically
|
||
class string_common_allocator {
|
||
public:
|
||
void* allocate(size_t bytes) {
|
||
return ::operator new(bytes);
|
||
}
|
||
void deallocate(void* address) noexcept {
|
||
::operator delete(address);
|
||
}
|
||
};
|
||
|
||
string_common_allocator default_string_allocator_selector(...);
|
||
// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
|
||
// объявите функцию
|
||
// ваш_тип_аллокатора default_string_allocator_selector(int);
|
||
// If you want to set your default allocator for strings, before including sstring.h
|
||
// declare a function
|
||
// your_allocator_type default_string_allocator_selector(int);
|
||
using allocator_string = decltype(default_string_allocator_selector(int(0)));
|
||
|
||
template<typename K, Allocatable Allocator>
|
||
class sstring;
|
||
|
||
/*
|
||
* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
|
||
* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
|
||
* Укажем ему явно.
|
||
* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
|
||
* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
|
||
* Let's tell him explicitly.
|
||
*/
|
||
|
||
/*!
|
||
* @ru @brief Класс мутабельной, владеющей строки. Содержит внутренний буфер для строк заданного размера.
|
||
* @tparam K - тип символа.
|
||
* @tparam N - размер внутреннего строкового буфера не менее N.
|
||
* @tparam forShared - аллоцировать внешний буфер в формате, совместимом с sstring.
|
||
* @tparam Allocator - тип аллокатора.
|
||
* @details "Локальная" строка. Хранит в себе указатель на символы и длину строки, а за ней либо сами данные до N
|
||
* символов + нуль, либо если данные длиннее N, то размер выделенного буфера.
|
||
* При этом, если планируется потом результат переместить в sstring, то для динамического буфера
|
||
* выделяется +n байтов, чтобы потом не копировать данные.
|
||
* @en @brief The mutable, owning string class. Contains an internal buffer for text of a given size.
|
||
* @tparam K - symbol type.
|
||
* @tparam N - the size of the internal string buffer is at least N.
|
||
* @tparam forShared - allocate an external buffer in a format compatible with sstring.
|
||
* @tparam Allocator - allocator type.
|
||
* @details "Local" string. Stores a pointer to characters and the length of the string, followed by either the data itself up to N
|
||
* characters + zero, or if the data is longer than N, then the size of the allocated buffer.
|
||
* At the same time, if you plan to later move the result to sstring, then for a dynamic buffer
|
||
* +n bytes are allocated so as not to copy the data later.
|
||
*/
|
||
template<typename K, size_t N, bool forShared = false, Allocatable Allocator = allocator_string>
|
||
class decl_empty_bases lstring :
|
||
public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
|
||
public str_mutable<K, lstring<K, N, forShared, Allocator>>,
|
||
public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
|
||
public null_terminated<K, lstring<K, N, forShared, Allocator>>,
|
||
public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
|
||
public:
|
||
using symb_type = K;
|
||
using my_type = lstring<K, N, forShared, Allocator>;
|
||
using allocator_t = Allocator;
|
||
|
||
enum : size_t {
|
||
/// @ru Размер внутреннего буфера в символах @en Size of internal buffer
|
||
LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
|
||
};
|
||
|
||
protected:
|
||
enum : size_t {
|
||
extra = forShared ? sizeof(SharedStringData<K>) + (AllocatableSized<Allocator> ? sizeof(size_t) : 0) : 0,
|
||
};
|
||
|
||
using base_algs = str_algs<K, simple_str<K>, my_type, true>;
|
||
using base_storable = str_storable<K, my_type, Allocator>;
|
||
using base_mutable = str_mutable<K, my_type>;
|
||
using base_utf = from_utf_convertible<K, my_type>;
|
||
using traits = ch_traits<K>;
|
||
using s_str = base_storable::s_str;
|
||
|
||
friend base_storable;
|
||
friend base_mutable;
|
||
friend base_utf;
|
||
friend class sstring<K, Allocator>;
|
||
|
||
K* data_;
|
||
size_t size_;
|
||
|
||
union {
|
||
size_t capacity_;
|
||
K local_[LocalCapacity + 1];
|
||
};
|
||
|
||
constexpr void create_empty() {
|
||
data_ = local_;
|
||
size_ = 0;
|
||
local_[0] = 0;
|
||
}
|
||
constexpr static size_t calc_capacity(size_t s) {
|
||
const int al = alignof(std::max_align_t) < 16 ? 16 : alignof(std::max_align_t);
|
||
size_t real_need = (s + 1) * sizeof(K) + extra;
|
||
size_t aligned_alloced = (real_need + al - 1) / al * al;
|
||
return (aligned_alloced - extra) / sizeof(K) - 1;
|
||
}
|
||
|
||
constexpr K* init(size_t s) {
|
||
size_t need_cap = s;
|
||
if (need_cap > LocalCapacity) {
|
||
need_cap = calc_capacity(s);
|
||
data_ = alloc_place(need_cap);
|
||
capacity_ = need_cap;
|
||
} else {
|
||
data_ = local_;
|
||
}
|
||
size_ = s;
|
||
return str();
|
||
}
|
||
// Методы для себя | Methods for yourself
|
||
constexpr bool is_alloced() const noexcept {
|
||
return data_ != local_;
|
||
}
|
||
|
||
constexpr void dealloc() {
|
||
if (is_alloced()) {
|
||
if constexpr (AllocatableNoSized<Allocator>) {
|
||
base_storable::allocator().deallocate(to_real_address(data_));
|
||
} else {
|
||
base_storable::allocator().deallocate(to_real_address(data_), (capacity_ + 1) * sizeof(K) + extra);
|
||
}
|
||
data_ = local_;
|
||
}
|
||
}
|
||
|
||
constexpr static K* to_real_address(void* ptr) {
|
||
return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
|
||
}
|
||
constexpr static K* from_real_address(void* ptr) {
|
||
return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
|
||
}
|
||
|
||
constexpr K* alloc_place(size_t newSize) {
|
||
return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
|
||
}
|
||
// Вызывается при replace, когда меняют на более длинную замену
|
||
// Called on replace when changing to a longer replacement
|
||
constexpr K* alloc_for_copy(size_t newSize) {
|
||
if (capacity() >= newSize) {
|
||
// Замена войдёт в текущий буфер
|
||
// Replacement will go into the current buffer
|
||
return data_;
|
||
}
|
||
return alloc_place(calc_capacity(newSize));
|
||
}
|
||
// Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
|
||
// Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
|
||
constexpr void set_from_copy(K* ptr, size_t newSize) {
|
||
if (ptr != data_) {
|
||
// Да, копировали в новый буфер
|
||
// Yes, copied to a new buffer
|
||
dealloc();
|
||
data_ = ptr;
|
||
capacity_ = calc_capacity(newSize);
|
||
}
|
||
size_ = newSize;
|
||
data_[newSize] = 0;
|
||
}
|
||
constexpr void copy_from_another(K* buf, const K* src, size_t size) {
|
||
// Реальный размер буфера всегда кратен sizeof(void*), поэтому копируя по sizeof(void*) байтов, мы не выйдем за пределы буфера
|
||
// The actual buffer size is always a multiple of sizeof(void*), so by copying sizeof(void*) bytes at a time, we won't go beyond the buffer's limits.
|
||
size_t need_copy_bytes = (size + 1) * sizeof(K);
|
||
size_t cnt = (need_copy_bytes + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*);
|
||
traits::copy(buf, src, cnt / sizeof(K));
|
||
}
|
||
public:
|
||
/*!
|
||
* @ru @brief Создать пустой объект.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Create an empty object.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
|
||
constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
|
||
: base_storable(std::forward<Args>(args)...) {
|
||
create_empty();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор из другого строкового объекта.
|
||
* @param other - другой строковый объект, simple_str.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief A constructor from another string object.
|
||
* @param other - another string object, simple_str.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_from_str_other(other);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения строки.
|
||
* @param repeat - количество повторов.
|
||
* @param pattern - строка, которую надо повторить.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief String repetition constructor.
|
||
* @param repeat - number of repetitions.
|
||
* @param pattern - the string to be repeated.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_str_repeat(repeat, pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения символа.
|
||
* @param count - количество повторов.
|
||
* @param pad - символ, который надо повторить.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Character repetition constructor.
|
||
* @param count - number of repetitions.
|
||
* @param pad - the character to be repeated.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_symb_repeat(count, pad);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового выражения.
|
||
* @param expr - строковое выражение.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details Конструктор запрашивает у строкового выражения `length()`,
|
||
* выделяет память нужного размера, и вызывает метод `place()` для размещения
|
||
* результата в буфере.
|
||
* @en @brief Constructor from a string expression.
|
||
* @param expr - string expression.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details The constructor queries the string expression `length()`,
|
||
* allocates memory of the required size, and calls the `place()` method to allocate
|
||
* result in buffer.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_str_expr(expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового источника с заменой.
|
||
* @param f - строковый объект, из которого берётся исходная строка.
|
||
* @param pattern - подстрока, которую надо заменить.
|
||
* @param repl - строка, на которую надо заменить.
|
||
* @param offset - начальная позиция для поиска подстрок.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Constructor from string source with replacement.
|
||
* @param f - the string object from which the source string is taken.
|
||
* @param pattern - substring to be replaced.
|
||
* @param repl - the string to be replaced with.
|
||
* @param offset - starting position for searching substrings.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
|
||
: base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_replaced(f, pattern, repl, offset, maxCount);
|
||
}
|
||
|
||
constexpr lstring() {
|
||
create_empty();
|
||
}
|
||
|
||
constexpr ~lstring() {
|
||
dealloc();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Копирование из другой строки такого же типа.
|
||
* @param other - другая строка.
|
||
* @en @brief Copy from another string of the same type.
|
||
* @param other - another string.
|
||
*/
|
||
constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
|
||
struct copy{uint64_t p[2];};
|
||
constexpr size_t short_str = sizeof(copy) / sizeof(K);
|
||
if constexpr (LocalCapacity >= short_str - 1) {
|
||
if (other.size_ < short_str) {
|
||
data_ = local_;
|
||
size_ = other.size_;
|
||
*(copy*)local_ = *(const copy*)other.local_;
|
||
return;
|
||
}
|
||
}
|
||
if (size_t size = other.size_) {
|
||
copy_from_another(init(size), other.symbols(), size);
|
||
} else {
|
||
create_empty();
|
||
}
|
||
}
|
||
/*!
|
||
* @ru @brief Копирование из другой строки такого же типа, но с другим аллокатором.
|
||
* @param other - другая строка.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Copy from another string of the same type, but with a different allocator.
|
||
* @param other - another string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
|
||
constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
if (other.size_) {
|
||
copy_from_another(init(other.size_), other.symbols(), other.size_);
|
||
} else {
|
||
create_empty();
|
||
}
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового литерала.
|
||
* @param value - строковый литерал.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief String literal constructor.
|
||
* @param value - string literal.
|
||
* @param ...args - parameter for initialization allocator.
|
||
*/
|
||
template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
if constexpr (I > 1) {
|
||
K* ptr = init(I - 1);
|
||
traits::copy(ptr, (const K*)value, I - 1);
|
||
ptr[I - 1] = K{};
|
||
} else {
|
||
create_empty();
|
||
}
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор перемещения из строки такого же типа.
|
||
* @param other - другая строка.
|
||
* @en @brief Constructor for moving from a string of the same type.
|
||
* @param other - another string.
|
||
*/
|
||
constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
|
||
if (other.size_) {
|
||
size_ = other.size_;
|
||
if (other.is_alloced()) {
|
||
data_ = other.data_;
|
||
other.data_ = other.local_;
|
||
capacity_ = other.capacity_;
|
||
} else {
|
||
data_ = local_;
|
||
copy_from_another(data_, other.local_, size_);
|
||
}
|
||
other.size_ = 0;
|
||
other.local_[0] = 0;
|
||
} else {
|
||
create_empty();
|
||
}
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор заполнения с помощью функтора (см. str_mutable::fill).
|
||
* @param op - функтов заполнения.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief A fill constructor using a functor (see str_mutable::fill).
|
||
* @param op - filling functions.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename Op, typename... Args>
|
||
requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
|
||
lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
create_empty();
|
||
this->operator<<(op);
|
||
}
|
||
template<typename O>
|
||
requires(!std::is_same_v<O, K>)
|
||
lstring(simple_str<O> init) {
|
||
this->init_from_utf_convertible(init);
|
||
}
|
||
|
||
template<typename O, typename I, bool M>
|
||
requires(!std::is_same_v<O, K>)
|
||
lstring(const str_algs<O, simple_str<O>, I, M>& init) {
|
||
this->init_from_utf_convertible(init.to_str());
|
||
}
|
||
|
||
// copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
|
||
// Поэтому реализуем копирующее и перемещающее присваивание отдельно
|
||
// copy and swap for assignments is not very applicable here, since for strings with a large local buffer, an extra copy, even by moving, will be expensive
|
||
// Therefore, we implement the copy and move assignment separately
|
||
|
||
/*!
|
||
* @ru @brief Оператор присваивания копией из строки такого же типа.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Copy assignment operator from a string of the same type.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
my_type& operator=(const my_type& other) {
|
||
// Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
|
||
// Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
|
||
if (&other != this) {
|
||
traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
|
||
size_ = other.size_;
|
||
}
|
||
return *this;
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присваивания перемещением из строки такого же типа.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator by moving from a string of the same type.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
my_type& operator=(my_type&& other) noexcept {
|
||
// Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
|
||
// Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
|
||
if (&other != this) {
|
||
dealloc();
|
||
if (other.is_alloced()) {
|
||
data_ = other.data_;
|
||
capacity_ = other.capacity_;
|
||
} else {
|
||
traits::copy(data_, other.local_, other.size_ + 1);
|
||
}
|
||
base_storable::allocator() = std::move(other.allocator());
|
||
size_ = other.size_;
|
||
other.create_empty();
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
my_type& assign(const K* other, size_t len) {
|
||
if (len) {
|
||
bool isIntersect = other >= data_ && other + len <= data_ + size_;
|
||
if (isIntersect) {
|
||
// Особый случай, нам пытаются присвоить кусок нашей же строки.
|
||
// Просто переместим текст в буфере, и установим новый размер
|
||
// A special case, they are trying to assign us a piece of our own string.
|
||
// Just move the text in the buffer and set a new size
|
||
if (other > data_) {
|
||
traits::move(data_, other, len);
|
||
}
|
||
} else {
|
||
traits::copy(reserve_no_preserve(len), other, len);
|
||
}
|
||
}
|
||
size_ = len;
|
||
data_[size_] = 0;
|
||
return *this;
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присваивания из simple_str.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator from simple_str.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
my_type& operator=(simple_str<K> other) {
|
||
return assign(other.str, other.len);
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присваивания строкового литерала.
|
||
* @param other - строковый литерал, копируется в буфер строки.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief String literal assignment operator.
|
||
* @param other - string literal, copied to the string buffer.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t S = const_lit_for<K, T>::Count>
|
||
my_type& operator=(T&& other) {
|
||
return assign((const K*)other, S - 1);
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присваивания строкового выражения.
|
||
* @param expr - строковое выражение, материализуемое в буфер строки.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @details Если в строковом выражении что-либо ссылается на части этой же строки, то результат не определён.
|
||
* @en @brief String expression appending operator.
|
||
* @param expr - a string expression materialized into the string buffer.
|
||
* @return my_type& - a reference to yourself.
|
||
* @details If anything in a string expression refers to parts of the same string, then the result is undefined.
|
||
*/
|
||
my_type& operator=(const StrExprForType<K> auto& expr) {
|
||
size_t newLen = expr.length();
|
||
if (newLen) {
|
||
expr.place(reserve_no_preserve(newLen));
|
||
}
|
||
size_ = newLen;
|
||
data_[size_] = 0;
|
||
return *this;
|
||
}
|
||
/// @ru Длина строки. @en String length.
|
||
constexpr size_t length() const noexcept {
|
||
return size_;
|
||
}
|
||
/// @ru Указатель на константные символы. @en Pointer to constant characters.
|
||
constexpr const K* symbols() const noexcept {
|
||
return data_;
|
||
}
|
||
/// @ru Указатель на буфер строки. @en Pointer to a string buffer.
|
||
constexpr K* str() noexcept {
|
||
return data_;
|
||
}
|
||
/// @ru Пустая ли строка. @en Is the string empty?
|
||
constexpr bool is_empty() const noexcept {
|
||
return size_ == 0;
|
||
}
|
||
/// @ru Пустая ли строка, для совместимости с std::string. @en Whether the string is empty, for compatibility with std::string.
|
||
constexpr bool empty() const noexcept {
|
||
return size_ == 0;
|
||
}
|
||
/// @ru Текущая ёмкость буфера строки. @en Current row buffer capacity.
|
||
constexpr size_t capacity() const noexcept {
|
||
return is_alloced() ? capacity_ : LocalCapacity;
|
||
}
|
||
/*!
|
||
* @ru @brief Выделить буфер, достаточный для размещения newSize символов плюс завершающий ноль.
|
||
* @param newSize - новый размер строки.
|
||
* @return K* - указатель на буфер.
|
||
* @details Содержимое буфера не определено, и не гарантируется сохранение старого содержимого.
|
||
* Размер строки устанавливается в newSize.
|
||
* @en @brief Allocate a buffer large enough to hold newSize characters plus a terminating null.
|
||
* @param newSize - new string size.
|
||
* @return K* - pointer to the buffer.
|
||
* @details The contents of the buffer are undefined and the old contents are not guaranteed to be retained.
|
||
* The string size is set to newSize.
|
||
*/
|
||
constexpr K* reserve_no_preserve(size_t newSize) {
|
||
if (newSize > capacity()) {
|
||
newSize = calc_capacity(newSize);
|
||
K* newData = alloc_place(newSize);
|
||
dealloc();
|
||
data_ = newData;
|
||
capacity_ = newSize;
|
||
}
|
||
return data_;
|
||
}
|
||
/*!
|
||
* @ru @brief Выделить буфер, достаточный для размещения newSize символов плюс завершающий ноль.
|
||
* @param newSize - новый размер строки.
|
||
* @return K* - указатель на буфер.
|
||
* @details Содержимое строки сохраняется. При увеличении буфера размер выделяется не больше запрошенного.
|
||
* Размер строки устанавливается в newSize.
|
||
* @en @brief Allocate a buffer large enough to hold newSize characters plus a terminating null.
|
||
* @param newSize - new string size.
|
||
* @return K* - pointer to the buffer.
|
||
* @details The contents of the string are preserved. When increasing the buffer, the size allocated is no larger than the requested one.
|
||
* The string size is set to newSize.
|
||
*/
|
||
constexpr K* reserve(size_t newSize) {
|
||
if (newSize > capacity()) {
|
||
newSize = calc_capacity(newSize);
|
||
K* newData = alloc_place(newSize);
|
||
traits::copy(newData, data_, size_);
|
||
dealloc();
|
||
data_ = newData;
|
||
capacity_ = newSize;
|
||
}
|
||
return data_;
|
||
}
|
||
/*!
|
||
* @ru @brief Устанавливает размер текущей строки, при необходимости выделяя место.
|
||
* @param newSize - новый размер строки.
|
||
* @return K* - указатель на буфер.
|
||
* @details Содержимое строки сохраняется. При увеличении буфера размер выделяется не менее чем 2 старого размера буфера.
|
||
* Размер строки устанавливается в newSize.
|
||
* @en @brief Sets the size of the current string, allocating space if necessary.
|
||
* @param newSize - new string size.
|
||
* @return K* - pointer to the buffer.
|
||
* @details The contents of the string are preserved. When increasing the buffer size, at least 2 times the old buffer size are allocated.
|
||
* The string size is set to newSize.
|
||
*/
|
||
constexpr K* set_size(size_t newSize) {
|
||
size_t cap = capacity();
|
||
if (newSize > cap) {
|
||
size_t needPlace = newSize;
|
||
if (needPlace < (cap + 1) * 2) {
|
||
needPlace = (cap + 1) * 2 - 1;
|
||
}
|
||
reserve(needPlace);
|
||
}
|
||
size_ = newSize;
|
||
data_[newSize] = 0;
|
||
return data_;
|
||
}
|
||
/*!
|
||
* @ru @brief Узнать, локальный или внешний буфер используется для символов.
|
||
* @en @brief Find out whether a local or external buffer is used for characters.
|
||
*/
|
||
constexpr bool is_local() const noexcept {
|
||
return !is_alloced();
|
||
}
|
||
/*!
|
||
* @ru @brief Определить длину строки.
|
||
* Ищет символ 0 в буфере строки до его ёмкости, после чего устанавливает длину строки по найденному 0.
|
||
* @en @brief Determine the length of the string.
|
||
* Searches for the character 0 in the string buffer to its capacity, and then sets the length of the string to the found 0.
|
||
*/
|
||
constexpr void define_size() {
|
||
size_t cap = capacity();
|
||
for (size_t i = 0; i < cap; i++) {
|
||
if (data_[i] == 0) {
|
||
size_ = i;
|
||
return;
|
||
}
|
||
}
|
||
size_ = cap;
|
||
data_[size_] = 0;
|
||
}
|
||
/*!
|
||
* @ru @brief Уменьшает размер внешнего буфера до минимально возможного для хранения строки.
|
||
* Если строка уместится во внутренний буфер - копирует её в него и освобождает внешний буфер.
|
||
* @en @brief Reduces the size of the external buffer to the smallest possible size to hold the string.
|
||
* If the string fits into the internal buffer, it copies it into it and frees the external buffer.
|
||
*/
|
||
constexpr void shrink_to_fit() {
|
||
size_t need_capacity = calc_capacity(size_);
|
||
if (is_alloced() && capacity_ > need_capacity) {
|
||
K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
|
||
traits::copy(newData, data_, size_ + 1);
|
||
dealloc();
|
||
data_ = newData;
|
||
|
||
if (size_ > LocalCapacity) {
|
||
capacity_ = need_capacity;
|
||
}
|
||
}
|
||
}
|
||
/// @ru Делает строку пустой, не меняя буфер строки. @en Makes a string empty without changing the string buffer.
|
||
constexpr void clear() {
|
||
set_size(0);
|
||
}
|
||
/// @ru Делает строку пустой и освобождает внешний буфер, если он был. @en Makes the string empty and frees the external buffer, if there was one.
|
||
constexpr void reset() {
|
||
dealloc();
|
||
local_[0] = 0;
|
||
size_ = 0;
|
||
}
|
||
};
|
||
|
||
template<size_t N = 15>
|
||
using lstringa = lstring<u8s, N>;
|
||
template<size_t N = 15>
|
||
using lstringb = lstring<ubs, N>;
|
||
template<size_t N = 15>
|
||
using lstringw = lstring<wchar_t, N>;
|
||
template<size_t N = 15>
|
||
using lstringu = lstring<u16s, N>;
|
||
template<size_t N = 15>
|
||
using lstringuu = lstring<u32s, N>;
|
||
|
||
template<size_t N = 15>
|
||
using lstringsa = lstring<u8s, N, true>;
|
||
template<size_t N = 15>
|
||
using lstringsb = lstring<ubs, N, true>;
|
||
template<size_t N = 15>
|
||
using lstringsw = lstring<wchar_t, N, true>;
|
||
template<size_t N = 15>
|
||
using lstringsu = lstring<u16s, N, true>;
|
||
template<size_t N = 15>
|
||
using lstringsuu = lstring<u32s, N, true>;
|
||
|
||
|
||
template<typename T, typename K = typename const_lit<T>::symb_type>
|
||
auto getLiteralType(T&&) {
|
||
return K{};
|
||
};
|
||
|
||
template<size_t Arch, size_t L>
|
||
inline constexpr const size_t _local_count = 0;
|
||
|
||
template<>
|
||
inline constexpr const size_t _local_count<8, 1> = 23;
|
||
template<>
|
||
inline constexpr const size_t _local_count<8, 2> = 15;
|
||
template<>
|
||
inline constexpr const size_t _local_count<8, 4> = 7;
|
||
template<>
|
||
inline constexpr const size_t _local_count<4, 1> = 15;
|
||
template<>
|
||
inline constexpr const size_t _local_count<4, 2> = 11;
|
||
template<>
|
||
inline constexpr const size_t _local_count<4, 4> = 5;
|
||
|
||
template<typename T>
|
||
constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
|
||
|
||
/*!
|
||
* @ru @brief Класс иммутабельной владеющей строки.
|
||
* @tparam K - тип символов.
|
||
* @tparam Allocator - тип аллокатора.
|
||
* @details "shared" строка.
|
||
* Класс с small string optimization плюс разделяемый иммутабельный буфер строки.
|
||
* Так как буфер строки в этом классе иммутабельный, то:
|
||
* Во-первых, нет нужды хранить размер выделенного буфера, мы его всё-равно не будем изменять.
|
||
* Во-вторых, появляется ещё один тип строки - строка, инициализированная строковым литералом.
|
||
* Для неё просто сохраняем указатель на символы, и не считаем ссылки.
|
||
* Таким образом, инициализация строкового объекта в программе литералом - ничего никуда не копирует -
|
||
* ни в себя, ни в динамическую память, и не стоит дороже по сравнению с инициализацией
|
||
* сырого указателя на строку, и даже ещё оптимальнее, так как ещё и сразу подставляет размер,
|
||
* а не вычисляет его в рантайме.
|
||
* ```cpp
|
||
* stringa text = "text or very very very long text"; // ничего не стоит!
|
||
* stringa copy = anotherString; // Стоит только копирование байтов самого объекта плюс возможно один атомарный инкремент
|
||
* ```
|
||
* В случае разделяемого буфера размер строки всё-равно храним не в общем буфере, а в каждом объекте.
|
||
* Из-за SSO места всё-равно хватает, а в память лезть за длиной придётся меньше.
|
||
* Например, подсчитать сумму длин строк в векторе - пройдётся только по памяти в векторе.
|
||
*
|
||
* Размеры для x64:
|
||
* - для u8s - 24 байта, хранит строки до 23 символов + 0
|
||
* - для u16s - 32 байта, хранит строки до 15 символов + 0
|
||
* - для u32s - 32 байта, хранит строки до 7 символов + 0
|
||
* @en @brief Immutable owning string class.
|
||
* @tparam K - character type.
|
||
* @tparam Allocator - allocator type.
|
||
* @details "shared" string.
|
||
* Class with small string optimization plus a shared immutable string buffer.
|
||
* Since the string buffer in this class is immutable, then:
|
||
* Firstly, there is no need to store the size of the allocated buffer; we will not change it anyway.
|
||
* Secondly, another type of string appears - a string initialized with a string literal.
|
||
* For it, we simply save a pointer to symbols and do not count references.
|
||
* Thus, initializing a string object in a program with a literal does not copy anything anywhere -
|
||
* neither into itself nor into dynamic memory, and does not cost more than initialization
|
||
* a raw pointer to a string, and even more optimal, since it also immediately substitutes the size,
|
||
* but does not calculate it at runtime.
|
||
* ```cpp
|
||
* stringa text = "text or very very very long text"; // costs nothing!
|
||
* string copy = anotherString; // All you need to do is copy the bytes of the object itself, plus possibly one atomic increment
|
||
* ```
|
||
* In the case of a shared buffer, the size of the string is still stored not in the shared buffer, but in each object.
|
||
* Because of SSO, there is still enough space, and you will have to go to memory less for the length.
|
||
* For example, calculating the sum of the lengths of strings in a vector will only go through the memory in the vector.
|
||
*
|
||
* Sizes for x64:
|
||
* - for u8s - 24 bytes, stores strings up to 23 characters + 0
|
||
* - for u16s - 32 bytes, stores strings of up to 15 characters + 0
|
||
* - for u32s - 32 bytes, stores strings of up to 7 characters + 0
|
||
*/
|
||
template<typename K, Allocatable Allocator = allocator_string>
|
||
class decl_empty_bases sstring :
|
||
public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
|
||
public str_storable<K, sstring<K, Allocator>, Allocator>,
|
||
public null_terminated<K, sstring<K, Allocator>>,
|
||
public from_utf_convertible<K, sstring<K, Allocator>> {
|
||
public:
|
||
using symb_type = K;
|
||
using uns_type = std::make_unsigned_t<K>;
|
||
using my_type = sstring<K, Allocator>;
|
||
using allocator_t = Allocator;
|
||
|
||
enum { LocalCount = local_count<K> };
|
||
|
||
protected:
|
||
using base_algs = str_algs<K, simple_str<K>, my_type, false>;
|
||
using base_storable = str_storable<K, my_type, Allocator>;
|
||
using base_utf = from_utf_convertible<K, my_type>;
|
||
using traits = ch_traits<K>;
|
||
using uni = unicode_traits<K>;
|
||
using s_str = base_storable::s_str;
|
||
|
||
friend base_storable;
|
||
friend base_utf;
|
||
|
||
enum Types { Local, Constant, Shared };
|
||
|
||
union {
|
||
// Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
|
||
// пишется, сколько символов ещё можно вписать. Когда строка занимает всё
|
||
// возможное место, то localRemain становится 0, type в этом случае тоже 0,
|
||
// и в итоге после символов строки получается 0, как и надо!
|
||
// When we have a short string, it lies in the object itself, and in localRemain
|
||
// writes how many more characters can be entered. When a string takes up everything
|
||
// possible location, then localRemain becomes 0, type in this case is also 0,
|
||
// and as a result, after the characters of the string we get 0, as it should!
|
||
struct {
|
||
K buf_[LocalCount]; // Локальный буфер строки | Local string buffer
|
||
uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
|
||
uns_type type_ : 2;
|
||
};
|
||
struct {
|
||
union {
|
||
// Указатель на конcтантную строку | Pointer to a constant string
|
||
const K* cstr_;
|
||
// Указатель на строку, перед которой лежит SharedStringData
|
||
// Pointer to the string preceded by SharedStringData
|
||
const K* sstr_;
|
||
};
|
||
size_t bigLen_; // Длина не локальной строки | Non-local string length
|
||
};
|
||
};
|
||
|
||
constexpr void create_empty() {
|
||
type_ = Local;
|
||
localRemain_ = LocalCount;
|
||
buf_[0] = 0;
|
||
}
|
||
constexpr K* init(size_t s) {
|
||
if (s > LocalCount) {
|
||
type_ = Shared;
|
||
localRemain_ = 0;
|
||
bigLen_ = s;
|
||
sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
|
||
return (K*)sstr_;
|
||
} else {
|
||
type_ = Local;
|
||
localRemain_ = LocalCount - s;
|
||
return buf_;
|
||
}
|
||
}
|
||
|
||
void dealloc() {
|
||
if (type_ == Shared) {
|
||
if constexpr (AllocatableNoSized<Allocator>) {
|
||
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
|
||
} else {
|
||
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator(), bigLen_);
|
||
}
|
||
}
|
||
}
|
||
public:
|
||
|
||
sstring() {
|
||
create_empty();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор пустой строки.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Constructor for the empty string.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
|
||
sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
|
||
: base_storable(std::forward<Args>(args)...) {
|
||
create_empty();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор из другого строкового объекта.
|
||
* @param other - другой строковый объект, simple_str.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief A constructor from another string object.
|
||
* @param other - another string object, simple_str.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_from_str_other(other);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения строки.
|
||
* @param repeat - количество повторов.
|
||
* @param pattern - строка, которую надо повторить.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief String repetition constructor.
|
||
* @param repeat - number of repetitions.
|
||
* @param pattern - the string to be repeated.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_str_repeat(repeat, pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения символа.
|
||
* @param count - количество повторов.
|
||
* @param pad - символ, который надо повторить.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Character repetition constructor.
|
||
* @param count - number of repetitions.
|
||
* @param pad - the character to be repeated.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_symb_repeat(count, pad);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового выражения.
|
||
* @param expr - строковое выражение.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details Конструктор запрашивает у строкового выражения `length()`,
|
||
* выделяет память нужного размера, и вызывает метод `place()` для размещения
|
||
* результата в буфере.
|
||
* @en @brief Constructor from a string expression.
|
||
* @param expr - string expression.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details The constructor queries the string expression `length()`,
|
||
* allocates memory of the required size, and calls the `place()` method to allocate
|
||
* result in buffer.
|
||
*/
|
||
template<typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_str_expr(expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового источника с заменой.
|
||
* @param f - строковый объект, из которого берётся исходная строка.
|
||
* @param pattern - подстрока, которую надо заменить.
|
||
* @param repl - строка, на которую надо заменить.
|
||
* @param offset - начальная позиция для поиска подстрок.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @en @brief Constructor from string source with replacement.
|
||
* @param f - the string object from which the source string is taken.
|
||
* @param pattern - substring to be replaced.
|
||
* @param repl - the string to be replaced with.
|
||
* @param offset - starting position for searching substrings.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
*/
|
||
template<StrType<K> From, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
|
||
: base_storable(std::forward<Args>(args)...) {
|
||
base_storable::init_replaced(f, pattern, repl, offset, maxCount);
|
||
}
|
||
|
||
static const sstring<K> empty_str;
|
||
/// @ru Деструктор строки. @en String destructor.
|
||
constexpr ~sstring() {
|
||
dealloc();
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор копирования строки.
|
||
* @param other - копируемая строка.
|
||
* @en @brief String copy constructor.
|
||
* @param other - the string to be copied.
|
||
*/
|
||
constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
|
||
memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
|
||
if (type_ == Shared)
|
||
SharedStringData<K>::from_str(sstr_)->incr();
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор перемещения.
|
||
* @param other - перемещаемая строка.
|
||
* @en @brief Move constructor.
|
||
* @param other - the string to be moved.
|
||
*/
|
||
constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
|
||
memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
|
||
other.create_empty();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Конструктор перемещения из lstring с совместимым с sstring внешним буфером.
|
||
* @param src - перемещаемая строка.
|
||
* @details В случае, если символы в lstring лежат во внешнем аллоцированном буфере,
|
||
* просто забираем указатель на буфер, он нам подойдёт.
|
||
* @en @brief A move constructor from lstring with an sstring-compatible external buffer.
|
||
* @param src - the string to be moved.
|
||
* @details If the characters in lstring are in an external allocated buffer,
|
||
* we just take the pointer to the buffer, it will suit us.
|
||
*/
|
||
template<size_t N>
|
||
constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
|
||
size_t size = src.length();
|
||
if (size) {
|
||
if (src.is_alloced()) {
|
||
// Там динамический буфер, выделенный с запасом для SharedStringData и size_t.
|
||
// There is a dynamic buffer allocated with a reserve for SharedStringData and size_t.
|
||
K* str = src.str();
|
||
if (size > LocalCount) {
|
||
// Просто присвоим его себе.
|
||
// Let's just assign it to ourselves.
|
||
sstr_ = str;
|
||
bigLen_ = size;
|
||
type_ = Shared;
|
||
localRemain_ = 0;
|
||
if constexpr (AllocatableNoSized<Allocator>) {
|
||
new (SharedStringData<K>::from_str(str)) SharedStringData<K>;
|
||
} else {
|
||
new (SharedStringData<K>::from_str(str)) SharedStringData<K>(src.capacity_);
|
||
}
|
||
} else {
|
||
// Скопируем локально
|
||
// Copy locally
|
||
type_ = Local;
|
||
localRemain_ = LocalCount - size;
|
||
traits::copy(buf_, str, size + 1);
|
||
// Освободим тот буфер, у локальной строки буфер не разделяется с другими
|
||
// Let's free that buffer; a local string's buffer is not shared with others
|
||
src.dealloc();
|
||
}
|
||
} else {
|
||
// Копируем из локального буфера
|
||
// Copy from local buffer
|
||
K* str = init(src.size_);
|
||
traits::copy(str, src.symbols(), size + 1);
|
||
}
|
||
src.create_empty();
|
||
} else
|
||
create_empty();
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Инициализация из строкового литерала.
|
||
* @param s - строковый литерал.
|
||
* @param ...args - параметры для инициализации аллокатора.
|
||
* @details В этом случае просто запоминаем указатель на строку и её длину.
|
||
* @en @brief Initialize from a string literal.
|
||
* @param s - string literal.
|
||
* @param ...args - parameters for initializing the allocator.
|
||
* @details In this case, we simply remember the pointer to the string and its length.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
|
||
requires std::is_constructible_v<allocator_t, Args...>
|
||
sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||
type_ = Constant;
|
||
localRemain_ = 0;
|
||
cstr_ = (const K*)s;
|
||
bigLen_ = N - 1;
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Инициализация из строкового источника с другим типом символов. Конвертирует через UTF.
|
||
* @tparam O - тип жругих символов.
|
||
* @en @brief Initialization from a string source with a different character type. Converts via UTF.
|
||
* @tparam O - type of other characters. */
|
||
template<typename O> requires(!std::is_same_v<O, K>)
|
||
sstring(simple_str<O> init) {
|
||
this->init_from_utf_convertible(init);
|
||
}
|
||
|
||
template<typename O, typename I, bool M> requires(!std::is_same_v<O, K>)
|
||
sstring(const str_algs<O, simple_str<O>, I, M>& init) {
|
||
this->init_from_utf_convertible(init.to_str());
|
||
}
|
||
|
||
constexpr void swap(my_type&& other) noexcept {
|
||
char buf[sizeof(buf_) + sizeof(K)];
|
||
memcpy(buf, buf_, sizeof(buf));
|
||
memcpy(buf_, other.buf_, sizeof(buf));
|
||
memcpy(other.buf_, buf, sizeof(buf));
|
||
|
||
std::swap(base_storable::allocator(), other.allocator());
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения другой строки того же типа.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator to another string of the same type.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
constexpr my_type& operator=(my_type other) noexcept {
|
||
swap(std::move(other));
|
||
return *this;
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения другой строки другого типа.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator to another string of a different type.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
constexpr my_type& operator=(simple_str<K> other) {
|
||
return operator=(my_type{other, base_storable::allocator()});
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения строкового литерала.
|
||
* @param other - строковый литера.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief String literal assignment operator.
|
||
* @param other - string character.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
template<typename T, size_t N = const_lit_for<K, T>::Count>
|
||
constexpr my_type& operator=(T&& other) {
|
||
return operator=(my_type{std::forward<T>(other), base_storable::allocator()});
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения другой строки типа lstring.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator to another string of type lstring.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
template<size_t N, bool forShared, typename A>
|
||
constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
|
||
return operator=(my_type{other.to_str(), base_storable::allocator()});
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения перемещаемой строки типа lstring с совместимым буфером.
|
||
* @param other - другая строка.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @en @brief Assignment operator to a movable string of type lstring with a compatible buffer.
|
||
* @param other - another string.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
template<size_t N>
|
||
constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
|
||
return operator=(my_type{std::move(other)});
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор присвоения строкового выражения.
|
||
* @param expr - строковое выражения.
|
||
* @return my_type& - ссылку на себя же.
|
||
* @details В строковом выражение допустимо ссылаться на части этой же строки, так как сначала создаётся копия.
|
||
* @en @brief String expression assignment operator.
|
||
* @param expr - string expression.
|
||
* @return my_type& - a reference to yourself.
|
||
* @details In a string expression, it is possible to refer to parts of the same string, since a copy is created first.
|
||
*/
|
||
constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
|
||
return operator=(my_type{expr, base_storable::allocator()});
|
||
}
|
||
/*!
|
||
* @ru @brief Сделать строку пустой.
|
||
* @return my_type& - ссылку на себя же
|
||
* @en @brief Make the string empty.
|
||
* @return my_type& - a reference to yourself.
|
||
*/
|
||
constexpr my_type& make_empty() noexcept {
|
||
dealloc();
|
||
create_empty();
|
||
return *this;
|
||
}
|
||
/// @ru Указатель на символы строки. @en Pointer to characters in the string.
|
||
constexpr const K* symbols() const noexcept {
|
||
return type_ == Local ? buf_ : cstr_;
|
||
}
|
||
/// @ru Длина строки. @en string length.
|
||
constexpr size_t length() const noexcept {
|
||
return type_ == Local ? LocalCount - localRemain_ : bigLen_;
|
||
}
|
||
/// @ru Пустая ли строка. @en Is the string empty?
|
||
constexpr bool is_empty() const noexcept {
|
||
return length() == 0;
|
||
}
|
||
/// @ru Пустая ли строка, для совместимости с std::string. @en Whether the string is empty, for compatibility with std::string.
|
||
constexpr bool empty() const noexcept {
|
||
return is_empty();
|
||
}
|
||
/*!
|
||
* @ru @brief Получить строку, отформатированную с помощью `std::sprintf`.
|
||
* @param pattern - форматная строка.
|
||
* @param ...args - аргументы для `sprintf`.
|
||
* @return my_type.
|
||
* @details Для Windows поддерживаются posix позиционные аргументы, используется `_sprintf_p`.
|
||
* @en @brief Get a string formatted with `std::sprintf`.
|
||
* @param pattern - format string.
|
||
* @param ...args - arguments for `sprintf`.
|
||
* @return my_type.
|
||
* @details On Windows, posix positional arguments are supported, using `_sprintf_p`.
|
||
*/
|
||
template<typename... T>
|
||
static my_type printf(const K* pattern, T&&... args) {
|
||
return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
|
||
}
|
||
/*!
|
||
* @ru @brief Получить строку, отформатированную с помощью `std::format`.
|
||
* @param fmtString - константная форматная строка.
|
||
* @param ...args - аргументы для `std::format`.
|
||
* @return my_type.
|
||
* @en @brief Get a string formatted with `std::format`.
|
||
* @param fmtString - constant format string.
|
||
* @param ...args - arguments for `std::format`.
|
||
* @return my_type.
|
||
*/
|
||
template<typename... T>
|
||
static my_type format(const FmtString<to_std_char_t<K>, T...>& fmtString, T&&... args) {
|
||
return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
|
||
}
|
||
/*!
|
||
* @ru @brief Получить строку, отформатированную с помощью `std::vformat`.
|
||
* @param fmtString - форматная строка.
|
||
* @param ...args - аргументы для `std::vformat`.
|
||
* @return my_type.
|
||
* @en @brief Get a string formatted with `std::vformat`.
|
||
* @param fmtString - format string.
|
||
* @param ...args - arguments for `std::vformat`.
|
||
* @return my_type.
|
||
*/
|
||
template<typename... T>
|
||
static my_type vformat(simple_str<K> fmtString, T&&... args) {
|
||
return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
|
||
}
|
||
};
|
||
|
||
template<typename K, Allocatable Allocator>
|
||
inline const sstring<K> sstring<K, Allocator>::empty_str{};
|
||
|
||
struct no_alloc{};
|
||
|
||
template<typename K, size_t N>
|
||
class decl_empty_bases cestring :
|
||
public str_algs<K, simple_str<K>, cestring<K, N>, true>,
|
||
public str_storable<K, cestring<K, N>, no_alloc>,
|
||
public null_terminated<K, lstring<K, N>>
|
||
//, public from_utf_convertible<K, lstring<K, N, forShared, Allocator>>
|
||
{
|
||
public:
|
||
using symb_type = K;
|
||
using my_type = cestring<K, N>;
|
||
|
||
enum : size_t {
|
||
/// @ru Размер внутреннего буфера в символах @en Size of internal buffer
|
||
LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
|
||
};
|
||
|
||
protected:
|
||
|
||
using base_algs = str_algs<K, simple_str<K>, my_type, true>;
|
||
using base_storable = str_storable<K, my_type, no_alloc>;
|
||
//using base_utf = from_utf_convertible<K, my_type>;
|
||
using traits = ch_traits<K>;
|
||
using s_str = base_storable::s_str;
|
||
|
||
friend base_storable;
|
||
//friend base_utf;
|
||
const K* cstr_{};
|
||
size_t size_{};
|
||
bool is_cstr_{};
|
||
K local_[LocalCapacity + 1]{};
|
||
|
||
constexpr void create_empty() {
|
||
is_cstr_ = false;
|
||
size_ = 0;
|
||
local_[0] = 0;
|
||
}
|
||
|
||
constexpr K* init(size_t s) {
|
||
size_ = s;
|
||
if (size_ > LocalCapacity) {
|
||
throw std::bad_alloc{};
|
||
}
|
||
is_cstr_ = false;
|
||
return local_;
|
||
}
|
||
public:
|
||
/// @ru Длина строки. @en String length.
|
||
constexpr size_t length() const noexcept {
|
||
return size_;
|
||
}
|
||
/// @ru Указатель на константные символы. @en Pointer to constant characters.
|
||
constexpr const K* symbols() const noexcept {
|
||
return is_cstr_ ? cstr_ : local_;
|
||
}
|
||
/// @ru Пустая ли строка. @en Is the string empty?
|
||
constexpr bool is_empty() const noexcept {
|
||
return size_ == 0;
|
||
}
|
||
/// @ru Пустая ли строка, для совместимости с std::string. @en Whether the string is empty, for compatibility with std::string.
|
||
constexpr bool empty() const noexcept {
|
||
return size_ == 0;
|
||
}
|
||
/// @ru Текущая ёмкость буфера строки. @en Current row buffer capacity.
|
||
constexpr size_t capacity() const noexcept {
|
||
return LocalCapacity;
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор пустой строки.
|
||
* @en @brief Constructor for the empty string.
|
||
*/
|
||
constexpr cestring() noexcept = default;
|
||
|
||
/*!
|
||
* @ru @brief Конструктор из другого строкового объекта.
|
||
* @param other - другой строковый объект, simple_str.
|
||
* @en @brief A constructor from another string object.
|
||
* @param other - another string object, simple_str.
|
||
*/
|
||
constexpr cestring(s_str other) : base_storable() {
|
||
base_storable::init_from_str_other(other);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения строки.
|
||
* @param repeat - количество повторов.
|
||
* @param pattern - строка, которую надо повторить.
|
||
* @en @brief String repetition constructor.
|
||
* @param repeat - number of repetitions.
|
||
* @param pattern - the string to be repeated.
|
||
*/
|
||
constexpr cestring(size_t repeat, s_str pattern) : base_storable() {
|
||
base_storable::init_str_repeat(repeat, pattern);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор повторения символа.
|
||
* @param count - количество повторов.
|
||
* @param pad - символ, который надо повторить.
|
||
* @en @brief Character repetition constructor.
|
||
* @param count - number of repetitions.
|
||
* @param pad - the character to be repeated.
|
||
*/
|
||
constexpr cestring(size_t count, K pad) : base_storable() {
|
||
base_storable::init_symb_repeat(count, pad);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового выражения.
|
||
* @param expr - строковое выражение.
|
||
* @details Конструктор запрашивает у строкового выражения `length()`,
|
||
* выделяет память нужного размера, и вызывает метод `place()` для размещения
|
||
* результата в буфере.
|
||
* @en @brief Constructor from a string expression.
|
||
* @param expr - string expression.
|
||
* @details The constructor queries the string expression `length()`,
|
||
* allocates memory of the required size, and calls the `place()` method to allocate
|
||
* result in buffer.
|
||
*/
|
||
constexpr cestring(const StrExprForType<K> auto& expr) : base_storable() {
|
||
base_storable::init_str_expr(expr);
|
||
}
|
||
/*!
|
||
* @ru @brief Конструктор из строкового источника с заменой.
|
||
* @param f - строковый объект, из которого берётся исходная строка.
|
||
* @param pattern - подстрока, которую надо заменить.
|
||
* @param repl - строка, на которую надо заменить.
|
||
* @param offset - начальная позиция для поиска подстрок.
|
||
* @param maxCount - максимальное количество замен, 0 - без ограничений.
|
||
* @en @brief Constructor from string source with replacement.
|
||
* @param f - the string object from which the source string is taken.
|
||
* @param pattern - substring to be replaced.
|
||
* @param repl - the string to be replaced with.
|
||
* @param offset - starting position for searching substrings.
|
||
* @param maxCount - maximum number of replacements, 0 - no restrictions.
|
||
*/
|
||
template<StrType<K> From>
|
||
constexpr cestring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0)
|
||
: base_storable() {
|
||
base_storable::init_replaced(f, pattern, repl, offset, maxCount);
|
||
}
|
||
|
||
/// @ru Деструктор строки. @en String destructor.
|
||
constexpr ~cestring() {}
|
||
|
||
/*!
|
||
* @ru @brief Инициализация из строкового литерала.
|
||
* @param s - строковый литерал.
|
||
* @details В этом случае просто запоминаем указатель на строку и её длину.
|
||
* @en @brief Initialize from a string literal.
|
||
* @param s - string literal.
|
||
* @details In this case, we simply remember the pointer to the string and its length.
|
||
*/
|
||
template<typename T, size_t M = const_lit_for<K, T>::Count>
|
||
constexpr cestring(T&& s) : base_storable(), cstr_((const K*)s), size_(M - 1), is_cstr_(true), local_{0} {}
|
||
};
|
||
|
||
template<typename K>
|
||
consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
|
||
if constexpr (std::is_same_v<K, u8s>)
|
||
return s8;
|
||
if constexpr (std::is_same_v<K, ubs>)
|
||
return sb;
|
||
if constexpr (std::is_same_v<K, uws>)
|
||
return sw;
|
||
if constexpr (std::is_same_v<K, u16s>)
|
||
return s16;
|
||
if constexpr (std::is_same_v<K, u32s>)
|
||
return s32;
|
||
}
|
||
|
||
#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
|
||
|
||
template<typename K, template<typename C> typename H>
|
||
struct StoreType {
|
||
simple_str<K> str;
|
||
size_t hash;
|
||
char node[sizeof(sstring<K>)];
|
||
|
||
const simple_str_nt<K>& to_nt() const noexcept {
|
||
return static_cast<const simple_str_nt<K>&>(str);
|
||
}
|
||
const sstring<K>& to_str() const noexcept {
|
||
return *reinterpret_cast<const sstring<K>*>(node);
|
||
}
|
||
bool operator==(const StoreType& other) const {
|
||
return hash == other.hash && typename H<K>::eql{}(*this, other);
|
||
}
|
||
};
|
||
|
||
template<bool Wide>
|
||
struct fnv_const {
|
||
static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
|
||
static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
|
||
};
|
||
|
||
template<>
|
||
struct fnv_const<false> {
|
||
static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
|
||
static inline constexpr size_t prime = static_cast<size_t>(16777619U);
|
||
};
|
||
|
||
using fnv = fnv_const<sizeof(size_t) == 8>;
|
||
|
||
template<typename K>
|
||
inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
|
||
size_t h = fnv::basis;
|
||
for (size_t i = 0; i < l; i++) {
|
||
h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
|
||
}
|
||
return h;
|
||
};
|
||
|
||
template<typename K>
|
||
inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
|
||
size_t h = fnv::basis;
|
||
for (size_t i = 0; i < l; i++) {
|
||
std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
|
||
h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
|
||
}
|
||
return h;
|
||
};
|
||
|
||
template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
|
||
inline constexpr size_t fnv_hash(T&& value) {
|
||
size_t h = fnv::basis;
|
||
for (size_t i = 0; i < N - 1; i++) {
|
||
h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
|
||
}
|
||
return h;
|
||
};
|
||
|
||
template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
|
||
inline constexpr size_t fnv_hash_ia(T&& value) {
|
||
size_t h = fnv::basis;
|
||
for (size_t i = 0; i < N - 1; i++) {
|
||
std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
|
||
h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
|
||
}
|
||
return h;
|
||
};
|
||
|
||
template<typename K>
|
||
inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
|
||
return fnv_hash(ptr, l);
|
||
};
|
||
|
||
template<typename K>
|
||
inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
|
||
return fnv_hash_ia(ptr, l);
|
||
};
|
||
|
||
template<typename K>
|
||
struct str_exact;
|
||
|
||
static_assert(std::is_trivially_copyable_v<StoreType<u8s, str_exact>>, "Store type must be trivially copyable");
|
||
|
||
|
||
template<typename K, typename V>
|
||
std::allocator<std::pair<K, V>> default_hashstrmap_allocator_selector(...);
|
||
// Если вы хотите задать свой дефолтный аллокатор для hashStrmap, перед включение sstring.h
|
||
// объявите функцию
|
||
// template<typename K, typename V>
|
||
// ваш_тип_аллокатора default_hashstrmap_allocator_selector(int);
|
||
// If you want to set your default allocator for hashStrMap, before including sstring.h
|
||
// declare a function
|
||
// template<typename K, typename V>
|
||
// your_allocator_type default_hashstrmap_allocator_selector(int);
|
||
template<typename K, template<typename C> typename H, typename V>
|
||
using allocator_hashstrmap = decltype(default_hashstrmap_allocator_selector<const StoreType<K, H>, V>(int(0)));
|
||
|
||
/*!
|
||
* @ru @brief Контейнер для более эффективного поиска по строковым ключам.
|
||
* @details Используется для хранения и поиска ключей любого строкового типа.
|
||
* Как unordered_map, но чуть лучше.
|
||
* В качестве ключей хранит simple_str вместе с посчитанным хешем и пустым местом для sstring.
|
||
* После вставки создает в этом пустом месте sstring, чтобы simple_str было на что ссылаться.
|
||
* Позволяет использовать для вставки и поиска любые simstr строковые объекты, создавая из них объект
|
||
* sstring только при реальной вставке.
|
||
* Начиная с С++20 в unordered_map появилась возможность для гетерогенного поиска по ключу с типом,
|
||
* отличным от типа хранящегося ключа, но это требует определённых танцев с типами хэша и сравнения.
|
||
* Поиск же со вставкой (try_emplace) по типу, отличному от типа ключа, появляется в стандарте только с C++26.
|
||
* Имеется реализация хеширования и сравнения для вариантов:
|
||
* - с учётом регистра символов
|
||
* - без учёта регистра символов, только ASCII
|
||
* - без учёта регистра символов, упрощённый Unicode (до 0xFFFF).
|
||
* Хэширование выполняется алгоритмом FNV-1A.
|
||
* Автоматическая конвертация simstr строковых объектов в тип хранящегося ключа (т.е. с посчитанным хешем),
|
||
* а также сохранение sstring в ключе при вставке - выполняется для методов try_emplace, emplace, at,
|
||
* find, operator[], erase. При использовании других методов поиска вам нужно самим обеспечить правильно
|
||
* посчитанный хэш в ключе StoreType. Методами вставки помимо перечисленных не пользоваться, они не
|
||
* сохранят sstring в ключе.
|
||
*
|
||
* Основное, на чём хотелось бы заострить внимание - само назначение strHashMap, для чего оно изначально придумывалось.
|
||
* Цель была не в том, чтобы как-то побить по производительности/памяти unordered_map (иначе зачем делать strHashMap на
|
||
* базе unordered_map?) Основная проблема, которая решалась - это то, что у нас несколько вариантов строковых объектов,
|
||
* и хотелось бы иметь возможность использовать для вставки и поиска ключей любой из типов строковых объектов.
|
||
* У нас есть simple_str, simple_str_nt, sstring, а lstring<N> вообще на каждое N это новый тип объекта.
|
||
* В std всё было просто - там только string, и соответственно, пользовались unordered_map<string, Что-то там>.
|
||
* И между прочим, в std столкнулись с той же проблемой: ключи const char*, а с C++17 и string_view (это как наш simple_str),
|
||
* искать в unordered_map<string, Type> - не очень хорошо, приходится каждый раз ключ преобразовывать в string, хотя можно
|
||
* обойтись и без этого.
|
||
* Поэтому начиная с С++20 в unordered_map таки добавили возможность гетерогенного поиска - на
|
||
* https://en.cppreference.com/w/cpp/container/unordered_map/find это синтаксисы (3) и (4).
|
||
* Правда, для этого требуются ритуальные танцы с бубном - требуется особый тип для хеширования (который, понятно, в
|
||
* стандарт уже не входит и его надо реализовывать самому), который умеет хешировать разные типы ключей,
|
||
* и содержит typename `is_transparent`. Собственно, на той же странице с ним пример и приведён.
|
||
* Часть проблемы это закрыло - поиска, но проблему вставки не решало - для вставки в unordered_map<string, Type>
|
||
* по прежнему требуется только string. И только в будущем С++26 мы ждём возможности гетерогенной вставки в виде
|
||
* try_emplace (https://en.cppreference.com/w/cpp/container/unordered_map/try_emplace) в синтаксисе (6), который
|
||
* позволит принимать ключи других типов и преобразовывать их в нужный тип только если вставка реально осуществляется.
|
||
* В своей строковой библиотеке я столкнулся с этой проблемой ещё до С++17, и так я как не настолько крут, чтобы
|
||
* влиять на стандарт, просто создал свой класс - наследник от unordered_map, с обёртками вокруг методов вставки/поиска.
|
||
* Ну и дополнительно добавил ещё два варианта хеширования/сравнения: без учёта регистра ASCII, без учёта регистра simple unicode.
|
||
* Таким образом, получился класс, который расширяет стандартный unordered_map возможностями по работе со строковыми ключами разных
|
||
* типов, позволяющий избежать лишних ненужных преобразований ключей при поиске и вставке.
|
||
* То, что при этом может улучшится производительность - не цель, а побочный эффект - приятный, если он есть,
|
||
* но и не смертельный, если его нет.
|
||
* @en @brief Container for more efficient searching by string keys.
|
||
* @details Used to store and lookup keys of any string type.
|
||
* Like unordered_map, but a little better.
|
||
* Stores simple_str as keys along with the calculated hash and an empty space for sstring.
|
||
* After insertion, creates an sstring in this empty space so that simple_str has something to refer to.
|
||
* Allows you to use any simstr string objects for insertion and search, creating an object from them
|
||
* sstring only for real insertion.
|
||
* Since C++20, unordered_map has the ability to perform heterogeneous search by key with type,
|
||
* different from the type of the key being stored, but this requires some dancing with hash types and comparisons.
|
||
* Search with insertion (try_emplace) by a type other than the key type appears in the standard only with C++26.
|
||
* There is a hashing and comparison implementation for the options:
|
||
* - case sensitive
|
||
* - case insensitive, ASCII only
|
||
* - case insensitive, simplified Unicode (up to 0xFFFF).
|
||
* Hashing is performed by the FNV-1A algorithm.
|
||
* Automatic conversion of simstr string objects to the type of the stored key (i.e. with a calculated hash),
|
||
* and also saving sstring in the key when inserting - performed for the try_emplace, emplace, at, methods
|
||
* find, operator[], erase. When using other search methods, you need to provide the correct
|
||
* calculated hash in the StoreType key. Do not use insertion methods other than those listed; they are not
|
||
* will save sstring in the key.
|
||
*
|
||
* The main thing I would like to focus on is the very purpose of strHashMap, for which it was originally invented.
|
||
* The goal was not to somehow beat unordered_map in terms of performance/memory (otherwise why make strHashMap on
|
||
* unordered_map database?) The main problem that was solved is that we have several options for string objects,
|
||
* and I would like to be able to use any type of string object to insert and search for keys.
|
||
* We have simple_str, simple_str_nt, sstring, and lstring<N> in general for every N is a new object type.
|
||
* In std everything was simple - there was only string, and accordingly, we used unordered_map<string, Something there>.
|
||
* And by the way, in std we encountered the same problem: the keys are const char*, and with C++17 and string_view (this is like our simple_str),
|
||
* searching in unordered_map<string, Type> is not very good, you have to convert the key to string every time, although you can
|
||
* do without this.
|
||
* Therefore, starting from C++20, the possibility of heterogeneous search was added to unordered_map - on
|
||
* https://en.cppreference.com/w/cpp/container/unordered_map/find these are syntaxes (3) and (4).
|
||
* True, this requires ritual dances with a tambourine - a special type is required for hashing (which, of course, in
|
||
* the standard is no longer included and you need to implement it yourself), which can hash different types of keys,
|
||
* and contains typename `is_transparent`. In fact, there is an example of it on the same page.
|
||
* This solved part of the problem - search, but did not solve the insertion problem - for inserting into unordered_map<string, Type>
|
||
* still only requires string. And only in the future C++26 we expect the possibility of heterogeneous insertion in the form
|
||
* try_emplace (https://en.cppreference.com/w/cpp/container/unordered_map/try_emplace) in syntax (6), which
|
||
* will allow you to accept keys of other types and convert them to the desired type only if the insertion is actually carried out.
|
||
* In my string library, I encountered this problem even before C++17, and so I’m not so cool as to
|
||
* influence the standard, I simply created my own class - a descendant of unordered_map, with wrappers around the insertion/search methods.
|
||
* Well, I also added two more hashing/comparison options: case-insensitive ASCII, case-insensitive simple unicode.
|
||
* Thus, we have a class that extends the standard unordered_map with the ability to work with string keys of different
|
||
* types, allowing you to avoid unnecessary unnecessary key conversions when searching and inserting.
|
||
* The fact that performance may improve is not the goal, but a side effect - pleasant, if there is one,
|
||
* but not fatal if it is not there.
|
||
*/
|
||
template<typename K, typename T, template<typename C> typename HE = str_exact, typename A = allocator_hashstrmap<K, HE, T>>
|
||
class hashStrMap : public std::unordered_map<StoreType<K, HE>, T, typename HE<K>::hash, typename HE<K>::eql, A> {
|
||
protected:
|
||
using InStore = StoreType<K, HE>;
|
||
|
||
public:
|
||
using hasher = typename HE<K>::hash;
|
||
using comparator = typename HE<K>::eql;
|
||
using my_type = hashStrMap<K, T, HE, A>;
|
||
using hash_t = std::unordered_map<StoreType<K, HE>, T, hasher, comparator, A>;
|
||
|
||
hashStrMap() = default;
|
||
hashStrMap(const my_type& other) : hash_t(other) {
|
||
for (const auto& [k, v] : *this) {
|
||
InStore& stored = const_cast<InStore&>(k);
|
||
sstring<K> tmp = *(sstring<K>*)stored.node;
|
||
new (stored.node) sstring<K>(std::move(tmp));
|
||
stored.str.str = stored.to_str().symbols();
|
||
}
|
||
}
|
||
~hashStrMap() {
|
||
for (auto& k: *this)
|
||
((sstring<K>*)k.first.node)->~sstring();
|
||
}
|
||
|
||
hashStrMap(my_type&& o) = default;
|
||
|
||
my_type& operator=(const my_type& other) {
|
||
hash_t::operator=(other);
|
||
for (const auto& [k, v] : *this) {
|
||
InStore& stored = const_cast<InStore&>(k);
|
||
sstring<K> tmp = *(sstring<K>*)stored.node;
|
||
new (stored.node) sstring<K>(std::move(tmp));
|
||
stored.str.str = stored.to_str().symbols();
|
||
}
|
||
return *this;
|
||
};
|
||
my_type& operator=(my_type&&) = default;
|
||
|
||
hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
|
||
for (const auto& e: init)
|
||
emplace(e.first, e.second);
|
||
}
|
||
|
||
using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
|
||
|
||
hashStrMap(init_str&& init) {
|
||
for (const auto& e: init)
|
||
emplace(e.first, e.second);
|
||
}
|
||
|
||
// При входе хэш должен быть уже посчитан
|
||
// When entering, the hash must already be calculated
|
||
template<typename... ValArgs>
|
||
auto try_emplace(const InStore& key, ValArgs&&... args) {
|
||
auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
|
||
if (it.second) {
|
||
InStore& stored = const_cast<InStore&>(it.first->first);
|
||
new (stored.node) sstring<K>(key.str);
|
||
stored.str.str = stored.to_str().symbols();
|
||
}
|
||
return it;
|
||
}
|
||
|
||
static InStore toStoreType(simple_str<K> key) {
|
||
return {key, hasher{}(key)};
|
||
}
|
||
|
||
template<typename Key, typename... ValArgs>
|
||
requires(std::is_convertible_v<Key, simple_str<K>>)
|
||
auto try_emplace(Key&& key, ValArgs&&... args) {
|
||
auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
|
||
if (it.second) {
|
||
InStore& stored = const_cast<InStore&>(it.first->first);
|
||
new (stored.node) sstring<K>(std::forward<Key>(key));
|
||
stored.str.str = stored.to_str().symbols();
|
||
}
|
||
return it;
|
||
}
|
||
|
||
template<typename... ValArgs>
|
||
auto emplace(const InStore& key, ValArgs&&... args) {
|
||
auto it = try_emplace(key, std::forward<ValArgs>(args)...);
|
||
if (!it.second) {
|
||
it.first->second = T(std::forward<ValArgs>(args)...);
|
||
}
|
||
return it;
|
||
}
|
||
|
||
template<typename Key, typename... ValArgs>
|
||
requires(std::is_convertible_v<Key, simple_str<K>>)
|
||
auto emplace(Key&& key, ValArgs&&... args) {
|
||
auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
|
||
if (!it.second) {
|
||
it.first->second = T(std::forward<ValArgs>(args)...);
|
||
}
|
||
return it;
|
||
}
|
||
|
||
auto& operator[](const InStore& key) {
|
||
return try_emplace(key).first->second;
|
||
}
|
||
|
||
template<typename Key>
|
||
requires(std::is_convertible_v<Key, simple_str<K>>)
|
||
auto& operator[](Key&& key) {
|
||
return try_emplace(std::forward<Key>(key)).first->second;
|
||
}
|
||
|
||
decltype(auto) at(const InStore& key) {
|
||
return hash_t::at(key);
|
||
}
|
||
decltype(auto) at(const InStore& key) const {
|
||
return hash_t::at(key);
|
||
}
|
||
|
||
decltype(auto) at(simple_str<K> key) {
|
||
return hash_t::at(toStoreType(key));
|
||
}
|
||
decltype(auto) at(simple_str<K> key) const {
|
||
return hash_t::at(toStoreType(key));
|
||
}
|
||
|
||
auto find(const InStore& key) const {
|
||
return hash_t::find(key);
|
||
}
|
||
|
||
auto find(simple_str<K> key) const {
|
||
return find(toStoreType(key));
|
||
}
|
||
|
||
auto find(const InStore& key) {
|
||
return hash_t::find(key);
|
||
}
|
||
|
||
auto find(simple_str<K> key) {
|
||
return find(toStoreType(key));
|
||
}
|
||
|
||
auto erase(typename hash_t::const_iterator it) {
|
||
if (it != hash_t::end()) {
|
||
((sstring<K>*)it->first.node)->~sstring();
|
||
}
|
||
return hash_t::erase(it);
|
||
}
|
||
|
||
auto erase(const InStore& key) {
|
||
auto it = hash_t::find(key);
|
||
if (it != hash_t::end()) {
|
||
((sstring<K>*)it->first.node)->~sstring();
|
||
hash_t::erase(it);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
auto erase(simple_str<K> key) {
|
||
return erase(toStoreType(key));
|
||
}
|
||
|
||
bool lookup(simple_str<K> txt, T& val) const {
|
||
auto it = find(txt);
|
||
if (it != hash_t::end()) {
|
||
val = it->second;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void clear() {
|
||
for (auto& k: *this)
|
||
((sstring<K>*)k.first.node)->~sstring();
|
||
hash_t::clear();
|
||
}
|
||
bool contains(const InStore& key) const {
|
||
return hash_t::find(key) != this->end();
|
||
}
|
||
|
||
bool contains(simple_str<K> key) const {
|
||
return find(toStoreType(key)) != this->end();
|
||
}
|
||
};
|
||
|
||
template<typename K>
|
||
struct str_exact {
|
||
struct eql {
|
||
bool operator()(const StoreType<K, str_exact>& _Left, const StoreType<K, str_exact>& _Right) const {
|
||
return _Left.hash == _Right.hash && _Left.str == _Right.str;
|
||
}
|
||
};
|
||
struct hash { // hash functor for basic_string
|
||
size_t operator()(simple_str<K> _Keyval) const {
|
||
return fnv_hash(_Keyval.symbols(), _Keyval.length());
|
||
}
|
||
size_t operator()(const StoreType<K, str_exact>& _Keyval) const {
|
||
return _Keyval.hash;
|
||
}
|
||
};
|
||
};
|
||
|
||
template<typename K>
|
||
struct str_eqlia {
|
||
struct eql {
|
||
bool operator()(const StoreType<K, str_eqlia>& _Left, const StoreType<K, str_eqlia>& _Right) const {
|
||
return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
|
||
}
|
||
};
|
||
struct hash {
|
||
size_t operator()(simple_str<K> _Keyval) const {
|
||
return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
|
||
}
|
||
size_t operator()(const StoreType<K, str_eqlia>& _Keyval) const {
|
||
return _Keyval.hash;
|
||
}
|
||
};
|
||
};
|
||
|
||
template<typename K>
|
||
struct str_eqliu {
|
||
struct eql {
|
||
bool operator()(const StoreType<K, str_eqliu>& _Left, const StoreType<K, str_eqliu>& _Right) const {
|
||
return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
|
||
}
|
||
};
|
||
struct hash {
|
||
size_t operator()(simple_str<K> _Keyval) const {
|
||
using bc = to_base_char_t<K>;
|
||
return unicode_traits<bc>::hashiu((const bc*)_Keyval.symbols(), _Keyval.length());
|
||
}
|
||
size_t operator()(const StoreType<K, str_eqliu>& _Keyval) const {
|
||
return _Keyval.hash;
|
||
}
|
||
};
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Для построения длинных динамических строк конкатенацией мелких кусочков.
|
||
* @details Выделяет по мере надобности отдельные блоки заданного размера (или кратного ему для больших вставок),
|
||
* чтобы избежать релокации длинных строк. После построения можно слить в одну строку.
|
||
* Как показали замеры, если сливать потом в одну строку, работает медленнее, чем lstring +=,
|
||
* но экономнее по памяти. Если не сливать в одну строку, а дальше перебирать буфера - быстрее.
|
||
* Сам является строковым выражением.
|
||
* @en @brief For constructing long dynamic strings by concatenating small pieces.
|
||
* @details Selects individual blocks of a given size (or a multiple of it for large inserts) as needed.
|
||
* to avoid relocation of long strings. After construction, you can merge it into one string.
|
||
* As measurements have shown, if you then merge it into one line, it works slower than lstring +=,
|
||
* but more economical in memory. If you don't merge it into one line, and then iterate through buffers, it's faster.
|
||
* Itself is a string expression.
|
||
*/
|
||
template<typename K>
|
||
class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
|
||
using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
|
||
std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
|
||
K* write{}; // Текущая позиция записи | Current write position
|
||
size_t len{}; // Общая длина | Total length
|
||
size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
|
||
size_t align{1024};
|
||
|
||
public:
|
||
using my_type = chunked_string_builder<K>;
|
||
using symb_type = K;
|
||
chunked_string_builder() = default;
|
||
chunked_string_builder(size_t a) : align(a){};
|
||
chunked_string_builder(const my_type&) = delete;
|
||
chunked_string_builder(my_type&& other) noexcept
|
||
: chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
|
||
other.len = other.remain = 0;
|
||
other.write = nullptr;
|
||
}
|
||
my_type& operator=(my_type other) noexcept {
|
||
chunks.swap(other.chunks);
|
||
write = other.write;
|
||
len = other.len;
|
||
remain = other.remain;
|
||
align = other.align;
|
||
other.len = other.remain = 0;
|
||
other.write = nullptr;
|
||
return *this;
|
||
}
|
||
|
||
/// @ru Добавление порции данных. @en Adding a piece of data.
|
||
my_type& operator<<(simple_str<K> data) {
|
||
if (data.len) {
|
||
len += data.len;
|
||
if (data.len <= remain) {
|
||
// Добавляемые данные влезают в выделенный блок, просто скопируем их
|
||
// The added data fits into the selected block, just copy it
|
||
ch_traits<K>::copy(write, data.str, data.len);
|
||
write += data.len; // Сдвинем позицию записи | Let's move the recording position
|
||
chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
|
||
remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
|
||
} else {
|
||
// Не влезают | They don't fit
|
||
if (remain) {
|
||
// Сначала запишем сколько влезет
|
||
// First, write down as much as we can
|
||
ch_traits<K>::copy(write, data.str, remain);
|
||
data.len -= remain;
|
||
data.str += remain;
|
||
chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
|
||
}
|
||
// Выделим новый блок и впишем в него данные
|
||
// Рассчитаем размер блока, кратного заданному выравниванию
|
||
// Select a new block and write data into it
|
||
// Calculate the block size that is a multiple of the given alignment
|
||
size_t blockSize = (data.len + align - 1) / align * align;
|
||
chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
|
||
write = chunks.back().first.get();
|
||
ch_traits<K>::copy(write, data.str, data.len);
|
||
write += data.len;
|
||
remain = blockSize - data.len;
|
||
}
|
||
}
|
||
return *this;
|
||
}
|
||
/// @ru Добавление строкового выражения. @en Adding a string expression.
|
||
my_type& operator<<(const StrExprForType<K> auto& expr) {
|
||
size_t l = expr.length();
|
||
if (l) {
|
||
if (l < remain) {
|
||
write = expr.place(write);
|
||
chunks.back().second += l;
|
||
len += l;
|
||
remain -= l;
|
||
} else if (!remain) {
|
||
size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
|
||
chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
|
||
write = expr.place(chunks.back().first.get());
|
||
len += l;
|
||
remain = blockSize - l;
|
||
} else {
|
||
auto store = std::make_unique<K[]>(l);
|
||
expr.place(store.get());
|
||
return operator<<({store.get(), l});
|
||
}
|
||
}
|
||
return *this;
|
||
}
|
||
/// @ru Добавление символа. @en Adding a symbol.
|
||
template<typename T>
|
||
my_type& operator<<(T data)
|
||
requires std::is_same_v<T, K>
|
||
{
|
||
return operator<<(expr_char<K>(data));
|
||
}
|
||
/// @ru Длина сохранённого текста. @en Length of the saved text.
|
||
constexpr size_t length() const noexcept {
|
||
return len;
|
||
}
|
||
/// @ru Сбрасывает содержимое, но при этом не удаляет первый буфер, чтобы потом избежать аллокации. @en Resets the contents, but does not delete the first buffer in order to avoid allocation later.
|
||
void reset() {
|
||
if (chunks.empty()) {
|
||
return;
|
||
}
|
||
if (chunks.size() > 1) {
|
||
remain = 0;
|
||
chunks.resize(1);
|
||
}
|
||
remain += chunks[0].second;
|
||
chunks[0].second = 0;
|
||
len = 0;
|
||
write = chunks[0].first.get();
|
||
}
|
||
|
||
constexpr K* place(K* p) const noexcept {
|
||
for (const auto& block: chunks) {
|
||
ch_traits<K>::copy(p, block.first.get(), block.second);
|
||
p += block.second;
|
||
}
|
||
return p;
|
||
}
|
||
/*!
|
||
* @ru @brief Применяет функтор к каждому сохранённому буферу.
|
||
* @tparam Op - тип функтора, функция вида (const K* ptr, size_t len).
|
||
* @param o - функтор.
|
||
* @en @brief Applies a functor to each stored buffer.
|
||
* @tparam Op - type of the functor, function type (const K* ptr, size_t len).
|
||
* @param o is a functor.
|
||
*/
|
||
template<typename Op>
|
||
void out(const Op& o) const {
|
||
for (const auto& block: chunks)
|
||
o(block.first.get(), block.second);
|
||
}
|
||
/*!
|
||
* @ru @brief Проверяет, расположен ли весь текст одним непрерывным куском в памяти.
|
||
* @en @brief Checks whether all text is located in one contiguous chunk in memory.
|
||
*/
|
||
bool is_continuous() const {
|
||
if (chunks.size()) {
|
||
const K* ptr = chunks.front().first.get();
|
||
for (const auto& chunk: chunks) {
|
||
if (chunk.first.get() != ptr)
|
||
return false;
|
||
ptr += chunk.second;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
/*!
|
||
* @ru @brief Получить указатель на начало первого буфера.
|
||
* Имеет смысл применять только если is_continuous true.
|
||
* @en @brief Get a pointer to the beginning of the first buffer.
|
||
* It makes sense to apply only if is_continuous true.
|
||
*/
|
||
const K* begin() const {
|
||
return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
|
||
}
|
||
/*!
|
||
* @ru @brief Очистить объект, освободив все выделенные буфера.
|
||
* @en @brief Clear the object, freeing all allocated buffers.
|
||
*/
|
||
void clear() {
|
||
chunks.clear();
|
||
write = nullptr;
|
||
len = 0;
|
||
remain = 0;
|
||
}
|
||
/*!
|
||
* @ru @brief Объект, позволяющий последовательно копировать содержимое в буфер заданного размера.
|
||
* @en @brief An object that allows you to sequentially copy content into a buffer of a given size.
|
||
*/
|
||
struct portion_store {
|
||
typename decltype(chunks)::const_iterator it, end;
|
||
size_t writedFromCurrentChunk;
|
||
/*!
|
||
* @ru @brief Проверить, что данные ещё не кончились.
|
||
* @en @brief Check that the data has not yet run out.
|
||
*/
|
||
bool is_end() {
|
||
return it == end;
|
||
}
|
||
/*!
|
||
* @ru @brief Сохранить очередную порцию данных в буфер.
|
||
* @param buffer - указатель на буфер для сохранения данных.
|
||
* @param size - размер буфера.
|
||
* @return size_t - количество скопированных СИМВОЛОВ (не байтов).
|
||
* @en @brief Save the next portion of data to the buffer.
|
||
* @param buffer - pointer to the buffer for storing data.
|
||
* @param size - buffer size.
|
||
* @return size_t - the number of CHARACTERS (not bytes) copied.
|
||
*/
|
||
size_t store(K* buffer, size_t size) {
|
||
size_t writed = 0;
|
||
while (size && !is_end()) {
|
||
size_t remain = it->second - writedFromCurrentChunk;
|
||
size_t write = std::min(size, remain);
|
||
ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
|
||
writed += write;
|
||
remain -= write;
|
||
size -= write;
|
||
if (!remain) {
|
||
++it;
|
||
writedFromCurrentChunk = 0;
|
||
} else
|
||
writedFromCurrentChunk += write;
|
||
}
|
||
return writed;
|
||
}
|
||
};
|
||
/*!
|
||
* @ru @brief Получить portion_store, через который можно последовательно извлекать данные во внешний буфер.
|
||
* @return portion_store.
|
||
* @en @brief Get a portion_store through which data can be sequentially retrieved into an external buffer.
|
||
* @return portion_store.
|
||
*/
|
||
portion_store get_portion() const {
|
||
return {chunks.begin(), chunks.end(), 0};
|
||
}
|
||
/*!
|
||
* @ru @brief Получить внутренние буфера с данными.
|
||
* @return const auto&.
|
||
* @en @brief Get internal data buffers.
|
||
* @return const auto&.
|
||
*/
|
||
const auto& data() const {
|
||
return chunks;
|
||
}
|
||
};
|
||
|
||
using stringa = sstring<u8s>;
|
||
using stringb = sstring<ubs>;
|
||
using stringw = sstring<wchar_t>;
|
||
using stringu = sstring<u16s>;
|
||
using stringuu = sstring<u32s>;
|
||
static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char строк, регистрозависимый поиск.
|
||
* @en @brief Type of hash dictionary for char strings, case sensitive search.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u8s, str_exact, T>>
|
||
using hashStrMapA = hashStrMap<u8s, T, str_exact, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char строк, регистронезависимый поиск для ASCII символов.
|
||
* @en @brief Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u8s, str_eqlia, T>>
|
||
using hashStrMapAIA = hashStrMap<u8s, T, str_eqlia, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
|
||
* @en @brief Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u8s, str_eqliu, T>>
|
||
using hashStrMapAIU = hashStrMap<u8s, T, str_eqliu, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char8_t строк, регистрозависимый поиск.
|
||
* @en @brief Type of hash dictionary for char8_t strings, case sensitive search.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<ubs, str_exact, T>>
|
||
using hashStrMapB = hashStrMap<ubs, T, str_exact, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char8_t строк, регистронезависимый поиск для ASCII символов.
|
||
* @en @brief Type of hash dictionary for char8_t strings, case-insensitive lookup for ASCII characters.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<ubs, str_eqlia, T>>
|
||
using hashStrMapBIA = hashStrMap<ubs, T, str_eqlia, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char8_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
|
||
* @en @brief Hash dictionary type for char8_t strings, case-insensitive search for Unicode characters up to 0xFFFF.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<ubs, str_eqliu, T>>
|
||
using hashStrMapBIU = hashStrMap<ubs, T, str_eqliu, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для wchar_t строк, регистрозависимый поиск.
|
||
* @en @brief Hash dictionary type for wchar_t strings, case sensitive search.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<wchar_t, str_exact, T>>
|
||
using hashStrMapW = hashStrMap<wchar_t, T, str_exact, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для ASCII символов.
|
||
* @en @brief Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqlia, T>>
|
||
using hashStrMapWIA = hashStrMap<wchar_t, T, str_eqlia, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
|
||
* @en @brief Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqliu, T>>
|
||
using hashStrMapWIU = hashStrMap<wchar_t, T, str_eqliu, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char16_t строк, регистрозависимый поиск.
|
||
* @en @brief Hash dictionary type for char16_t strings, case sensitive search.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u16s, str_exact, T>>
|
||
using hashStrMapU = hashStrMap<u16s, T, str_exact, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char16_t строк, регистронезависимый поиск для ASCII символов.
|
||
* @en @brief Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u16s, str_eqlia, T>>
|
||
using hashStrMapUIA = hashStrMap<u16s, T, str_eqlia, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char16_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
|
||
* @en @brief Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFFF.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u16s, str_eqliu, T>>
|
||
using hashStrMapUIU = hashStrMap<u16s, T, str_eqliu, A>;
|
||
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char32_t строк, регистрозависимый поиск.
|
||
* @en @brief Hash dictionary type for char32_t strings, case sensitive search.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u32s, str_exact, T>>
|
||
using hashStrMapUU = hashStrMap<u32s, T, str_exact, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char32_t строк, регистронезависимый поиск для ASCII символов.
|
||
* @en @brief Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u32s, str_eqlia, T>>
|
||
using hashStrMapUUIA = hashStrMap<u32s, T, str_eqlia, A>;
|
||
/*!
|
||
* @ru @brief Тип хеш-словаря для char32_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
|
||
* @en @brief Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFFF.
|
||
*/
|
||
template<typename T, typename A = allocator_hashstrmap<u32s, str_eqliu, T>>
|
||
using hashStrMapUUIU = hashStrMap<u32s, T, str_eqliu, A>;
|
||
|
||
inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
|
||
|
||
inline namespace literals {
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в simple_str_nt.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return simple_str_nt.
|
||
* @en @brief Operator literal in simple_str_nt.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return simple_str_nt.
|
||
*/
|
||
SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
|
||
return simple_str_nt<u8s>{ptr, l};
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор литерал в simple_str_nt.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return simple_str_nt.
|
||
* @en @brief Operator literal in simple_str_nt.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return simple_str_nt.
|
||
*/
|
||
SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
|
||
return simple_str_nt<ubs>{ptr, l};
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор литерал в simple_str_nt.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return simple_str_nt.
|
||
* @en @brief Operator literal in simple_str_nt.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return simple_str_nt.
|
||
*/
|
||
SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
|
||
return simple_str_nt<uws>{ptr, l};
|
||
}
|
||
/*!
|
||
* @ru @brief Оператор литерал в simple_str_nt.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return simple_str_nt.
|
||
* @en @brief Operator literal in simple_str_nt.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return simple_str_nt.
|
||
*/
|
||
SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
|
||
return simple_str_nt<u16s>{ptr, l};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в simple_str_nt.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return simple_str_nt.
|
||
* @en @brief Operator literal in simple_str_nt.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return simple_str_nt.
|
||
*/
|
||
SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
|
||
return simple_str_nt<u32s>{ptr, l};
|
||
}
|
||
|
||
template<typename K> using HashKey = StoreType<K, str_exact>;
|
||
template<typename K> using HashKeyIA = StoreType<K, str_eqlia>;
|
||
template<typename K> using HashKeyIU = StoreType<K, str_eqliu>;
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем с учётом регистра.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-sensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
|
||
return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра ASCII.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-insensitive ASCII hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
|
||
return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра simple unicode.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a simple unicode case-insensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
|
||
return HashKeyIU<u8s>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{ptr, l})};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем с учётом регистра.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-sensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKey<ubs> operator""_h(const ubs* ptr, size_t l) {
|
||
return HashKey<ubs>{{ptr, l}, fnv_hash_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра ASCII.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-insensitive ASCII hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKeyIA<ubs> operator""_ia(const ubs* ptr, size_t l) {
|
||
return HashKeyIA<ubs>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра simple unicode.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a simple unicode case-insensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
inline HashKeyIU<ubs> operator""_iu(const ubs* ptr, size_t l) {
|
||
return HashKeyIU<ubs>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{(const u8s*)ptr, l})};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем с учётом регистра.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-sensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
|
||
return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра ASCII.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-insensitive ASCII hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
|
||
return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра simple unicode.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a simple unicode case-insensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
|
||
return HashKeyIU<u16s>{{ptr, l}, str_eqliu<u16s>::hash{}(simple_str<u16s>{ptr, l})};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем с учётом регистра.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-sensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
|
||
return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра ASCII.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-insensitive ASCII hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
|
||
return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра simple unicode.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a simple unicode case-insensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
|
||
return HashKeyIU<u32s>{{ptr, l}, str_eqliu<u32s>::hash{}(simple_str<u32s>{ptr, l})};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем с учётом регистра.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-sensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
|
||
return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра ASCII.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a case-insensitive ASCII hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
|
||
return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор литерал в ключ для hashStrMap с посчитанным в compile time хешем без учёта регистра simple unicode.
|
||
* @param ptr - указатель на строку.
|
||
* @param l - длина строки.
|
||
* @return StoreType.
|
||
* @en @brief Key literal operator for hashStrMap with a simple unicode case-insensitive hash calculated at compile time.
|
||
* @param ptr - pointer to a string.
|
||
* @param l - string length.
|
||
* @return StoreType.
|
||
*/
|
||
inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
|
||
return HashKeyIU<uws>{{ptr, l}, str_eqliu<uws>::hash{}(simple_str<uws>{ptr, l})};
|
||
}
|
||
} // namespace literals
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток simple_str.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Stream output operator simple_str.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::ostream& operator<<(std::ostream& stream, ssa text) {
|
||
return stream << std::string_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток simple_str.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Stream output operator simple_str.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::wostream& operator<<(std::wostream& stream, ssw text) {
|
||
return stream << std::wstring_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток simple_str.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Stream output operator simple_str.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
|
||
return stream << std::wstring_view{from_w(text.symbols()), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток sstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator for outputting sstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
|
||
return stream << std::string_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток sstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator for outputting sstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
|
||
return stream << std::wstring_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток sstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator for outputting sstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
|
||
return stream << std::wstring_view{from_w(text.symbols()), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток lstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator to output lstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
template<size_t N, bool S, simstr::Allocatable A>
|
||
inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
|
||
return stream << std::string_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток lstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator to output lstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
template<size_t N, bool S, simstr::Allocatable A>
|
||
inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
|
||
return stream << std::wstring_view{text.symbols(), text.length()};
|
||
}
|
||
|
||
/*!
|
||
* @ru @brief Оператор вывода в поток lstring.
|
||
* @param stream - поток вывода.
|
||
* @param text - текст.
|
||
* @return std::ostream&.
|
||
* @en @brief Operator to output lstring to stream.
|
||
* @param stream - output stream.
|
||
* @param text - text.
|
||
* @return std::ostream&.
|
||
*/
|
||
template<size_t N, bool S, simstr::Allocatable A>
|
||
inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
|
||
return stream << std::wstring_view{from_w(text.symbols()), text.length()};
|
||
}
|
||
|
||
} // namespace simstr
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str.
|
||
*/
|
||
template<typename K>
|
||
struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str<K> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str_nt.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str_nt.
|
||
*/
|
||
template<typename K>
|
||
struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа sstring.
|
||
* @en @brief Formatter to use in std::format for values of type string.
|
||
*/
|
||
template<typename K>
|
||
struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа lstring.
|
||
* @en @brief Formatter to use in std::format for values of type lstring.
|
||
*/
|
||
template<typename K, size_t N, bool S, typename A>
|
||
struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str<char8_t>.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str<char8_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str_nt<char8_t>.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str_nt<char8_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа sstring<char8_t>.
|
||
* @en @brief Formatter to use in std::format for values of type string<char8_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа lstring<char8_t>.
|
||
* @en @brief Formatter to use in std::format for values of type lstring<char8_t>.
|
||
*/
|
||
template<size_t N, bool S, typename A>
|
||
struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str<char16_t/char32_t>.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str<char16_t/char32_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа simple_str_nt<char16_t/char32_t>.
|
||
* @en @brief Formatter to use in std::format for values of type simple_str_nt<char16_t/char32_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа sstring<char16_t/char32_t>.
|
||
* @en @brief Formatter to use in std::format for values of type string<char16_t/char32_t>.
|
||
*/
|
||
template<>
|
||
struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|
||
|
||
/*!
|
||
* @ru @brief Форматтер для использования в std::format значений типа lstring<char16_t/char32_t>.
|
||
* @en @brief Formatter to use in std::format for values of type lstring<char16_t/char32_t>.
|
||
*/
|
||
template<size_t N, bool S, typename A>
|
||
struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
|
||
// Define format() by calling the base class implementation with the wrapped value
|
||
template<typename FormatContext>
|
||
auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
|
||
return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
|
||
}
|
||
};
|