Compare commits

...

7 Commits

Author SHA1 Message Date
Aleksandr Orefkov e9eaa6c4cb Fix noexcept in find_or_throw 2026-08-05 14:36:02 +03:00
Aleksandr Orefkov 00a4a008a9 1.9.1
- Добавлены методы get, prefix, suffix
- Методы substr и simple_str() объявлены deprecated.
- Added methods get, prefix, suffix
- The substr and simple_str() methods are deprecated.
2026-07-31 16:08:33 +03:00
Aleksandr Orefkov 86deccbac3 1.8.2
Добавлено несколько методов в hashStrMap.
Added several methods to hashStrMap.
2026-07-24 16:24:40 +03:00
Aleksandr Orefkov a779aa7320 Fix allocator use. 2026-03-23 10:38:10 +03:00
Aleksandr Orefkov e9e717c8bd 1.8.1
- Added the ability to use allocators with size specification when deallocating.
---
- Добавлена возможность использовать аллокаторы с указанием размера при деалокации.
2026-03-22 14:58:45 +03:00
Aleksandr Orefkov 0861d365f5 1.7.3
- Added several string functions.
---
- Добавлено несколько строковых функций.
2026-03-08 17:53:28 +03:00
Aleksandr Orefkov 91a2f798e3 Update description 2026-03-01 11:15:47 +03:00
22 changed files with 7230 additions and 6607 deletions

View File

@ -5,7 +5,7 @@ include(FetchContent)
project(
simstr
VERSION 1.7.2
VERSION 1.9.1
DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX
@ -131,6 +131,9 @@ if(SIMSTR_BUILD_TESTS)
if(TARGET gtest)
target_compile_features(gtest PUBLIC cxx_std_23)
target_compile_features(gtest_main PUBLIC cxx_std_23)
if (CLANG_COMPILER)
target_compile_options(gtest PRIVATE -Wno-error=character-conversion)
endif(CLANG_COMPILER)
endif()
endif()

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Бенчмарки
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -422,8 +422,11 @@ void StdFormatSize(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
size_t len = std::formatted_size("abcdefghihklmopqr {:#010o} end", i);
std::string str(len, 0);
std::string str;
str.resize_and_overwrite(len, [&](char* p, size_t) {
std::format_to_n(str.data(), len, "abcdefghihklmopqr {:#010o} end", i);
return len;
});
benchmark::DoNotOptimize(str);
}
}

View File

@ -185,7 +185,7 @@
<body>
<div class="head">
<h3>SimStr 1.7.1 Benchmark</h3>
<h3>SimStr 1.8.1 Benchmark</h3>
<span><a href="https://orefkov.github.io/simstr/results.html" target="blank">All results</a></span>
<span><a href="https://github.com/orefkov/simstr/blob/main/bench/bench_str.cpp" target="blank">Sources for
benchmarks</a></span>

View File

@ -38,7 +38,7 @@ struct result_info {
}
// Текущее положение поставим сразу за cpuinfo и откинем завершающие переводы строк
// We will put the current position immediately after cpuinfo and discard the final line feeds
current_text_ = current_text_(cpu_info_.end() - current_text_.begin() + 1).trimmed_right("\n");
current_text_ = current_text_.get(cpu_info_.end() - current_text_.begin() + 1, to_end).trimmed_right("\n");
}
};
@ -96,11 +96,11 @@ results_vector get_results_infos() {
// В начале имени файла может идти число и дефис, для сортировки, уберём их
// At the beginning of the file name there can be a number and a hyphen, for sorting, remove them
if (auto delimiter = fileName.find('-'); delimiter + 1 > 1) {
if (fileName(0, delimiter).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
if (fileName.prefix(delimiter).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
fileName.remove_prefix(delimiter + 1);
}
}
results.emplace_back(get_file_content(lstringa<128>{dirForResults + f}), fileName(0, -suffix.length()));
results.emplace_back(get_file_content(lstringa<128>{dirForResults + f}), fileName.get(0, from_end(suffix.length())));
}
}
return results;
@ -117,7 +117,7 @@ void write_platforms_cpu(out_t& out, const results_vector& results) {
for (const auto& r : results) {
size_t rp = r.cpu_info_.find('(') + 1, lp = r.cpu_info_.find(')', rp);
ssa shortCpuInfo = r.cpu_info_.from_to(rp, lp);
ssa cpuInfo = r.cpu_info_(lp + 2);
ssa cpuInfo = r.cpu_info_.get(lp + 2, to_end);
out += e_subst(R"--(
<li><span class="platform">{}</span><span class="tooltip">{}<span class="tooltiptext">{}</span></span>&nbsp;Include in charts: <input type="checkbox" id="pl{}" checked onchange="buildCharts()"/></li>)--",
r.platform_, shortCpuInfo, cpuInfo, counter++);
@ -167,7 +167,7 @@ ssa extract_name_result(ssa line, ssa& result) {
line.len = ns;
if (inNs) {
size_t end = line.find_last(' ');
result = line(end + 1);
result = line.get(end + 1, to_end);
if (auto rp = line.find("/repeats"); rp != str::npos) {
line.len = rp;
} else {
@ -182,7 +182,7 @@ ssa extract_name_result(ssa line, ssa& result) {
ssa extract_comment(ssa commentsText, ssa benchmarkName) {
size_t idx = commentsText.find_end(lstringa<120>{"- " + benchmarkName + "\n"});
if (idx != str::npos) {
if (commentsText[idx] != '\n' && commentsText(idx, 2) != "- ") {
if (commentsText[idx] != '\n' && commentsText.get(idx, 2) != "- ") {
return commentsText.from_to(idx, commentsText.find("\n\n", idx));
}
}
@ -206,7 +206,7 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
}();
size_t delim = benchName.find_last('/');
if (delim != str::npos && benchName(delim + 1).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
if (delim != str::npos && benchName.get(delim + 1, to_end).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
benchName.len = delim;
}
auto [it, not_exist] = textes.try_emplace(benchName, stringa{}, 0);
@ -218,7 +218,7 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
std::cerr << "Can not found benchmark function name for " << benchName << std::endl;
return {stra::empty_str, 0};
}
start = sourceText(0, start).find('(', sourceText.find_last('\n', start - 1));
start = sourceText.prefix(start).find('(', sourceText.find_last('\n', start - 1));
if (start == str::npos) {
std::cerr << "Can not found benchmark function name for " << benchName << std::endl;
return {stra::empty_str, 0};
@ -237,7 +237,7 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
}
start = sourceText.find_last('\n', start);
size_t templ_start = sourceText.find_last('\n', start) + 1;
if (sourceText(templ_start).starts_with("template")) {
if (sourceText.get(templ_start, to_end).starts_with("template")) {
start = templ_start;
} else {
start++;
@ -261,7 +261,7 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
throw std::runtime_error{"Not found end of func"};
}
lstringa<2048> text{sourceText.from_to(beginLine, end + indent.length() + 1), indent, "\n"};
func_it->second.first = repl_html_symbols(text(1));
func_it->second.first = repl_html_symbols(text.get(1, to_end));
} else {
end = sourceText.find("\n}\n", start);
func_it->second.first = repl_html_symbols(sourceText.from_to(start, end + 2));
@ -274,7 +274,7 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
}
void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText, ssa commentsText) {
ssa releaseVersion = sourceText(sourceText.find_end("\n * ver. ")).until("\n").trimmed();
ssa releaseVersion = sourceText.get(sourceText.find_end("\n * ver. "), to_end).until("\n").trimmed();
if (!releaseVersion) {
std::cerr << "Not found release version in sources" << std::endl;
throw std::runtime_error{"Not found release version in sources"};
@ -292,7 +292,7 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
while(!splitters[0].is_done()) {
ssa line = splitters[0].next(), benchName, result;
if (auto rm = line.find("_mean"); rm != str::npos) {
benchName = extract_name_result(line, result)(0, rm);
benchName = extract_name_result(line, result).prefix(rm);
auto [source, line_num] = extract_source_for_benchmark(benchName, sourceText);
auto comment = extract_comment(commentsText, benchName);
// Нужно вывести название бенча и коммент
@ -312,12 +312,12 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
throw std::runtime_error{"Not expected end of file"};
}
line = splitters[idx].next();
ssa rbench_name = extract_name_result(line, result)(0, rm);
ssa rbench_name = extract_name_result(line, result).prefix(rm);
if (rbench_name != benchName) {
while (line.find("_mean") == str::npos && !splitters[idx].is_done()) {
line = splitters[idx].next();
}
rbench_name = extract_name_result(line, result)(0, rm);
rbench_name = extract_name_result(line, result).prefix(rm);
if (rbench_name != benchName) {
std::cerr << "In results for " << results[idx].platform_ << " benchmark '" << rbench_name
<< "' does not match with other results" << std::endl;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.7.2
PROJECT_NUMBER = 1.9.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a
@ -2928,7 +2928,7 @@ MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# makes dot run faster, but since only newer versions of dot (>1.8.20) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.7.2
PROJECT_NUMBER = 1.9.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a
@ -2928,7 +2928,7 @@ MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# makes dot run faster, but since only newer versions of dot (>1.8.20) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.

View File

@ -1,6 +1,6 @@
/*
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* ver. 1.7.2
* ver. 1.9.1
*/
#pragma once

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Классы для работы со строками
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -373,6 +373,87 @@ public:
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;
}
};
/*
@ -694,7 +775,6 @@ protected:
Эти методы должен реализовать класс-наследник.
вызывается только при создании объекта
init(size_t size)
set_size(size_t size)
*/
template<typename O>
requires(!std::is_same_v<O, K>)
@ -760,6 +840,94 @@ 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.
@ -798,12 +966,9 @@ concept immutable_str = storable_str<A, K> && !mutable_str<A, K>;
* Эти методы должен реализовать класс-наследник, вызываются только при создании объекта
* - `K* init(size_t size)` - выделить место для строки указанного размера, вернуть адрес
* - `void create_empty()` - создать пустой объект
* - `K* set_size(size_t size)` - перевыделить место для строки, если при создании не угадали
* нужный размер и место нужно больше или меньше.
* Содержимое строки нужно оставить.
* Хотя тип аллокатора и задаётся параметром шаблона, делается это только для проброса
* его типа в конструкторы, методы аллокатора не вызываются. Если наследник не пользуется
* аллокатором, а сам в `init` и `set_size` как-то выделяет место, может указать типом аллокатора
* аллокатором, а сам в `init` как-то выделяет место, может указать типом аллокатора
* какой-либо пустой класс.
* @en @brief The base for the objects that own the string.
* @tparam K - character type.
@ -818,12 +983,9 @@ concept immutable_str = storable_str<A, K> && !mutable_str<A, K>;
* 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
* - `K* set_size(size_t size)` - re-allocate space for the string if you didnt guess correctly when creating
* the size you need and the space you need is larger or smaller.
* The contents of the string must be left.
* 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` and `set_size` it somehow allocates space, can indicate the type of the allocator
* 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>
@ -987,74 +1149,6 @@ protected:
*ptr = 0;
}
template<StrType<K> From, typename Op1, typename... Args>
requires std::is_constructible_v<allocator_t, Args...>
static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
my_type result{std::forward<Args>(args)...};
size_t len = f.length();
if (len) {
const K* source = f.symbols();
K* destination = result.init(len);
while(len--) {
*destination++ = opMakeNeedCase(*source++);
}
*destination = 0;
}
return result;
}
// GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
// Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
// GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
// So let's add a dummy template parameter to make the specialization for u8s right in the class.
template<typename T, bool Dummy = true>
struct ChangeCase {
template<typename From, typename Op1, typename... Args>
requires std::is_constructible_v<allocator_t, Args...>
static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
my_type result{std::forward<Args>(args)...};
size_t len = f.length();
if (len) {
opChangeCase(f.symbols(), len, result.init(len));
}
return result;
}
};
// Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
// For utf8 we will make a separate specification, since changing the register may change the length of the string
template<bool Dummy>
struct ChangeCase<u8s, Dummy> {
template<typename From, typename Op1, typename... Args>
requires std::is_constructible_v<allocator_t, Args...>
static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
my_type result{std::forward<Args>(args)...};
;
size_t len = f.length();
if (len) {
const K* ptr = f.symbols();
K* pWrite = result.init(len);
const u8s* source = ptr;
u8s* dest = pWrite;
size_t newLen = opChangeCase(source, len, dest, len);
if (newLen < len) {
// Строка просто укоротилась
// The string was simply shortened
result.set_size(newLen);
} else if (newLen > len) {
// Строка не влезла в буфер.
// The string did not fit into the buffer.
size_t readed = static_cast<size_t>(source - ptr);
size_t writed = static_cast<size_t>(dest - pWrite);
pWrite = result.set_size(newLen);
dest = pWrite + writed;
opChangeCase(source, len - readed, dest, newLen - writed);
}
pWrite[newLen] = 0;
}
return result;
}
};
public:
inline static constexpr bool is_str_storable = true;
@ -1183,7 +1277,7 @@ public:
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 changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
return my_type{e_ascii_upper<K>{f}, std::forward<Args>(args)...};
}
/*!
* @ru @brief Создать копию переданной строки в нижнем регистре символов ASCII.
@ -1196,7 +1290,7 @@ public:
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 changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
return my_type{e_ascii_lower<K>{f}, std::forward<Args>(args)...};
}
/*!
* @ru @brief Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF).
@ -1213,7 +1307,7 @@ public:
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 ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
return my_type{e_upper<K>{f}, std::forward<Args>(args)...};
}
/*!
* @ru @brief Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF).
@ -1230,7 +1324,7 @@ public:
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 ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
return my_type{e_lower<K>{f}, std::forward<Args>(args)...};
}
/*!
* @ru @brief Создать копию переданной строки с заменой подстрок.
@ -1260,11 +1354,28 @@ public:
* @en @brief Concept of a memory management type
*/
template<typename A>
concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
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) {
@ -2496,28 +2607,47 @@ public:
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(Allocatorable auto& allocator) {
void decr(AllocatableNoSized auto& allocator) {
size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
if (val == 1) {
allocator.deallocate(this);
}
}
static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
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<K>* from_str(const K* p) {
return (SharedStringData<K>*)p - 1;
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);
@ -2530,10 +2660,10 @@ struct SharedStringData {
class string_common_allocator {
public:
void* allocate(size_t bytes) {
return new char[bytes];
return ::operator new(bytes);
}
void deallocate(void* address) noexcept {
delete [] static_cast<char*>(address);
::operator delete(address);
}
};
@ -2546,7 +2676,7 @@ string_common_allocator default_string_allocator_selector(...);
// your_allocator_type default_string_allocator_selector(int);
using allocator_string = decltype(default_string_allocator_selector(int(0)));
template<typename K, Allocatorable Allocator>
template<typename K, Allocatable Allocator>
class sstring;
/*
@ -2578,7 +2708,7 @@ class sstring;
* 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, Allocatorable Allocator = allocator_string>
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>>,
@ -2597,7 +2727,7 @@ public:
protected:
enum : size_t {
extra = forShared ? sizeof(SharedStringData<K>) : 0,
extra = forShared ? sizeof(SharedStringData<K>) + (AllocatableSized<Allocator> ? sizeof(size_t) : 0) : 0,
};
using base_algs = str_algs<K, simple_str<K>, my_type, true>;
@ -2651,7 +2781,11 @@ protected:
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_;
}
}
@ -3123,7 +3257,7 @@ public:
}
/*!
* @ru @brief Определить длину строки.
* Ищет символ 0 в буфере строки до его ёмкости, после чего устаналивает длину строки по найденному 0.
* Ищет символ 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.
*/
@ -3149,7 +3283,7 @@ public:
if (is_alloced() && capacity_ > need_capacity) {
K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
traits::copy(newData, data_, size_ + 1);
base_storable::allocator().deallocate(to_real_address(data_));
dealloc();
data_ = newData;
if (size_ > LocalCapacity) {
@ -3268,7 +3402,7 @@ constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
* - 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, Allocatorable Allocator = allocator_string>
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>,
@ -3340,49 +3474,15 @@ protected:
}
}
K* set_size(size_t newSize) {
// Вызывается при создании строки при необходимости изменить размер.
// Других ссылок на shared buffer нет.
// Called when a string is created and needs to be resized.
// There are no other references to the shared buffer.
size_t size = length();
if (newSize != size) {
if (type_ == Constant) {
bigLen_ = newSize;
} else {
if (newSize <= LocalCount) {
void dealloc() {
if (type_ == Shared) {
SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
traits::copy(buf_, sstr_, newSize);
str_buf->decr(base_storable::allocator());
}
type_ = Local;
localRemain_ = LocalCount - newSize;
} else {
if (type_ == Shared) {
if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
traits::copy(newStr, sstr_, newSize);
if constexpr (AllocatableNoSized<Allocator>) {
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
sstr_ = newStr;
}
} else if (type_ == Local) {
K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
if (size)
traits::copy(newStr, buf_, size);
sstr_ = newStr;
type_ = Shared;
localRemain_ = 0;
}
bigLen_ = newSize;
} else {
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator(), bigLen_);
}
}
}
K* str = type_ == Local ? buf_ : (K*)sstr_;
str[newSize] = 0;
return str;
}
public:
sstring() {
@ -3490,9 +3590,7 @@ public:
static const sstring<K> empty_str;
/// @ru Деструктор строки. @en String destructor.
constexpr ~sstring() {
if (type_ == Shared) {
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
}
dealloc();
}
/*!
* @ru @brief Конструктор копирования строки.
@ -3531,8 +3629,8 @@ public:
size_t size = src.length();
if (size) {
if (src.is_alloced()) {
// Там динамический буфер, выделенный с запасом для SharedStringData.
// There is a dynamic buffer allocated with a reserve for SharedStringData.
// Там динамический буфер, выделенный с запасом для SharedStringData и size_t.
// There is a dynamic buffer allocated with a reserve for SharedStringData and size_t.
K* str = src.str();
if (size > LocalCount) {
// Просто присвоим его себе.
@ -3541,7 +3639,11 @@ public:
bigLen_ = size;
type_ = Shared;
localRemain_ = 0;
new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
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
@ -3684,8 +3786,7 @@ public:
* @return my_type& - a reference to yourself.
*/
constexpr my_type& make_empty() noexcept {
if (type_ == Shared)
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
dealloc();
create_empty();
return *this;
}
@ -3751,7 +3852,7 @@ public:
}
};
template<typename K, Allocatorable Allocator>
template<typename K, Allocatable Allocator>
inline const sstring<K> sstring<K, Allocator>::empty_str{};
struct no_alloc{};
@ -4264,8 +4365,7 @@ public:
}
auto erase(const InStore& key) {
auto it = hash_t::find(key);
if (it != hash_t::end()) {
if (auto it = hash_t::find(key); it != hash_t::end()) {
((sstring<K>*)it->first.node)->~sstring();
hash_t::erase(it);
return 1;
@ -4277,15 +4377,151 @@ public:
return erase(toStoreType(key));
}
bool lookup(simple_str<K> txt, T& val) const {
auto it = find(txt);
if (it != hash_t::end()) {
/*!
* @ru @brief Поиск и извлечение значения.
* @param key - искомый ключ.
* @param val - ссылка на приёмник значения.
* @details Если ключ существует, присваивает переданной ссылке хранимое значение и возвращает true.
* Иначе возвращает false.
* @en @brief Finding and retrieving the value.
* @param key - the key you are looking for.
* @param val - reference to the value receiver.
* @details If the key exists, assigns the stored value to the passed reference and returns true.
* Otherwise returns false.
*/
template<typename Dst> requires std::is_assignable_v<T, Dst>
bool lookup(simple_str<K> key, Dst& val) const {
if (auto it = find(key); it != hash_t::end()) {
val = it->second;
return true;
}
return false;
}
/*!
* @ru @brief Поиск и извлечение значения.
* @param key - искомый ключ.
* @details Если ключ существует, присваивает переданной ссылке хранимое значение и возвращает true.
* Иначе возвращает false.
* @en @brief Finding and retrieving the value.
* @param key - the key you are looking for.
* @param val - reference to the value receiver.
* @details If the key exists, assigns the stored value to the passed reference and returns true.
* Otherwise returns false.
*/
template<typename Dst> requires std::is_assignable_v<T, Dst>
bool lookup(const InStore& key, Dst& val) const {
if (auto it = find(key); it != hash_t::end()) {
val = it->second;
return true;
}
return false;
}
/*!
* @ru @brief Поиск и извлечение значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает std::optional указанного типа с копией значения.
* Иначе возвращает std::nullopt.
* @en @brief Finding and retrieving the value.
* @param key - the key you are looking for.
* @details If the key exists, return a std::optional of the specified type with a copy of the value.
* Otherwise returns std::nullopt.
*/
template<typename Dst = T> requires std::is_constructible_v<Dst, T>
std::optional<Dst> get(simple_str<K> key) const {
if (auto it = find(key); it != hash_t::end()) {
return it->second;
}
return {};
}
/*!
* @ru @brief Поиск и извлечение значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает std::optional указанного типа с копией значения.
* Иначе возвращает std::nullopt.
* @en @brief Finding and retrieving the value.
* @param s - the key you are looking for.
* @details If the key exists, return a std::optional of the specified type with a copy of the value.
* Otherwise returns std::nullopt.
*/
template<typename Dst = T> requires std::is_constructible_v<Dst, T>
std::optional<Dst> get(const InStore& key) const {
if (auto it = find(key); it != hash_t::end()) {
return it->second;
}
return {};
}
/*!
* @ru @brief Поиск существующего значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает указатель на хранимое значение.
* Иначе возвращает nullptr.
* @en @brief Finding an existing value.
* @param s - the key you are looking for.
* @details If the key exists, returns a pointer to the stored value.
* Otherwise returns nullptr.
*/
T* existed(simple_str<K> key) {
if (auto it = find(key); it != hash_t::end()) {
return std::addressof(it->second);
}
return nullptr;
}
/*!
* @ru @brief Поиск существующего значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает указатель на хранимое значение.
* Иначе возвращает nullptr.
* @en @brief Finding an existing value.
* @param s - the key you are looking for.
* @details If the key exists, returns a pointer to the stored value.
* Otherwise returns nullptr.
*/
T* existed(const InStore& key) {
if (auto it = find(key); it != hash_t::end()) {
return std::addressof(it->second);
}
return nullptr;
}
/*!
* @ru @brief Поиск существующего значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает константный указатель на хранимое значение.
* Иначе возвращает nullptr.
* @en @brief Finding an existing value.
* @param s - the key you are looking for.
* @details If the key exists, returns a constant pointer to the stored value.
* Otherwise returns nullptr.
*/
const T* existed(simple_str<K> key) const {
if (auto it = find(key); it != hash_t::end()) {
return std::addressof(it->second);
}
return nullptr;
}
/*!
* @ru @brief Поиск существующего значения.
* @param key - искомый ключ.
* @details Если ключ существует, возвращает константный указатель на хранимое значение.
* Иначе возвращает nullptr.
* @en @brief Finding an existing value.
* @param s - the key you are looking for.
* @details If the key exists, returns a constant pointer to the stored value.
* Otherwise returns nullptr.
*/
const T* existed(const InStore& key) const {
if (auto it = find(key); it != hash_t::end()) {
return std::addressof(it->second);
}
return nullptr;
}
void clear() {
for (auto& k: *this)
((sstring<K>*)k.first.node)->~sstring();
@ -4987,94 +5223,6 @@ inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
}
} // namespace literals
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 Оператор вывода в поток simple_str.
* @param stream - поток вывода.
@ -5169,7 +5317,7 @@ inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type
* @param text - text.
* @return std::ostream&.
*/
template<size_t N, bool S, simstr::Allocatorable A>
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()};
}
@ -5184,7 +5332,7 @@ inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A
* @param text - text.
* @return std::ostream&.
*/
template<size_t N, bool S, simstr::Allocatorable A>
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()};
}
@ -5199,7 +5347,7 @@ inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S,
* @param text - text.
* @return std::ostream&.
*/
template<size_t N, bool S, simstr::Allocatorable A>
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()};
}

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -3274,6 +3274,20 @@ public:
}
};
struct from_begin {
size_t idx_;
};
struct from_end {
size_t idx_;
};
inline constexpr from_end to_end{0};
template<typename T>
concept first_index_in_str = std::is_same_v<T, from_end> || std::is_convertible_v<T, size_t>;
template<typename T>
concept last_index_in_str = first_index_in_str<T> || std::is_same_v<T, from_begin>;
/*!
* @ru @brief Класс с базовыми константными строковыми алгоритмами.
* @details Является базой для классов, могущих выполнять константные операции со строками.
@ -3445,6 +3459,7 @@ public:
* "0123456789"_ss(-4, -1) == "678";
* ```
*/
[[deprecated("Use 'get' or [start, end] in C++23")]]
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
size_t myLen = _len(), idxStart = from >= 0 ? from : (ptrdiff_t)myLen > -from ? myLen + from : 0,
idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
@ -3454,6 +3469,83 @@ public:
idxStart = idxEnd;
return str_piece{_str() + idxStart, idxEnd - idxStart};
}
/*!
* @ru @brief Получить часть строки как "str_src".
* @param start - начальная позиция в строке.
* @param end - конечная позиция в строке.
* @return Подстроку, str_src.
* @details В качестве начальной позиции может задаваться или число - отсчитывается от начала строки, но не далее её конца,
* или `from_end(число)` - отсчитывается от конца строки, но не ранее её начала.
* В качестве конечной позиции может задаваться или число - отсчитывается от начальной позиции, но не далее конца строки,
* или `from_end(число)` - отсчитывается от конца строки, но не ранее начальной позиции,
* или `from_begin(число)` - отсчитывается от начала строки, но не ранее начальной позиции и не далее конца строки.
* Также можно использовать `to_end` - эквивалентно from_end(0).
* @en @brief Get part of a string as "str_src".
* @param start - starting position in the string.
* @param end - the end position in the string.
* @return Substring, str_src.
* @details Either a number can be specified as the starting position - it is counted from the beginning of the string, but not further than its end,
* or `from_end(number)` - counted from the end of the string, but not earlier than its beginning.
* The ending position can be either a number - counted from the starting position, but not further than the end of the string,
* or `from_end(number)` - counted from the end of the string, but not earlier than the starting position,
* or `from_begin(number)` - counted from the beginning of the string, but not earlier than the starting position and not further than the end of the string.
* You can also use `to_end` - equivalent to from_end(0).
* @~
* ```cpp
* "0123456789"_ss.get(5, 2) == "56";
* "0123456789"_ss.get(5, to_end) == "56789";
* "0123456789"_ss.get(5, from_end(1)) == "5678";
* "0123456789"_ss.get(from_end(3), to_end) == "789";
* "0123456789"_ss.get(from_end(3), 2) == "78";
* "0123456789"_ss.get(from_end(4), from_end(1)) == "678";
* ```
*/
template<first_index_in_str S, last_index_in_str E>
constexpr str_piece get(S start, E end) const noexcept {
size_t idxStart, idxEnd, len = _len();
if constexpr (std::is_same_v<S, from_end>) {
idxStart = start.idx_ > len ? 0 : len - start.idx_;
} else {
idxStart = std::min(size_t(start), len);
}
if constexpr (std::is_same_v<E, from_begin>) {
idxEnd = std::max(idxStart, std::min(end.idx_, len));
} else if constexpr (std::is_same_v<E, from_end>) {
idxEnd = std::max(idxStart, end.idx_ > len ? 0 : len - end.idx_);
} else {
size_t e = end;
idxEnd = len - idxStart < e ? len : idxStart + e;
}
return str_piece{_str() + idxStart, idxEnd - idxStart};
}
#if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202211L
/*!
* @ru @brief Обертка вокруг `get` в виде многомерного оператора [].
* @en @brief Wraps `get` as a multidimensional operator [].
*/
template<first_index_in_str S, last_index_in_str E>
constexpr str_piece operator[](S start, E end) const noexcept {
return get(start, end);
}
#endif
/*!
* @ru @brief Обертка вокруг `get(0, count)`.
* @en @brief Wraps `get(0, count)`.
*/
constexpr str_piece prefix(size_t count) const noexcept {
return get(0, count);
}
/*!
* @ru @brief Обертка вокруг `get(from_end(count), to_end)`.
* @en @brief Wraps `get(from_end(count), to_end)`.
*/
constexpr str_piece suffix(size_t count) const noexcept {
return get(from_end(count), to_end);
}
/*!
* @ru @brief Получить часть строки как "кусок строки".
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
@ -3500,7 +3592,7 @@ public:
* @return Substring, str_src.
*/
constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
return (*this)(0, find_or_all(pattern, offset));
return get(0, find_or_all(pattern, offset));
}
/*!
* @ru @brief Проверка на пустоту.
@ -3726,7 +3818,7 @@ public:
* @return size_t - the position of the beginning of the substring occurrence, or throws an Exc exception if not found.
*/
template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const {
if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
return fnd;
}
@ -4071,9 +4163,14 @@ public:
* @param len - the number of characters in the resulting "chunk". If less than or equal to zero, then count len characters from the end of the string.
* @return my_type - a substring, an object of the same type to which the method is applied.
*/
[[deprecated("Use sub(start, end)")]]
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
return my_type{d()(from, len)};
}
template<first_index_in_str S, last_index_in_str E>
constexpr my_type sub(S start, E end) const noexcept {
return my_type{get(start, end)};
}
/*!
* @ru @brief Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
@ -4355,6 +4452,36 @@ public:
constexpr bool starts_with(str_piece prefix) const noexcept {
return starts_with(prefix.symbols(), prefix.length());
}
/*!
* @ru @brief Проверить, начинается ли строка с указанной подстроки, за которой следует ней пробельный ASCII символ.
* @param prefix - проверяемая подстрока.
* @en @brief Check if a string begins with the specified substring followed by a whitespace ASCII character.
* @param prefix - substring to be checked.
*/
constexpr bool starts_with_and_ws(str_piece prefix) const noexcept {
return _len() > prefix.length() &&
starts_with(prefix) &&
trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
}
/*!
* @ru @brief Проверить, начинается ли строка с указанной подстроки, за которой следует один из указанных символов.
* @param prefix - проверяемая подстрока.
* @param next_symbol - проверяемые после подстроки символы.
* @en @brief Check if a string begins with the specified substring followed by one of the specified characters.
* @param prefix - substring to be checked.
* @param next_symbol - symbols to be checked after the substring.
*/
constexpr bool starts_with_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
return _len() > prefix.length() &&
starts_with(prefix) &&
trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
}
template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
constexpr bool starts_with_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
return _len() >= N &&
starts_with(prefix) &&
trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
}
constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
size_t myLen = _len();
@ -4380,6 +4507,36 @@ public:
constexpr bool starts_with_ia(str_piece prefix) const noexcept {
return starts_with_ia(prefix.symbols(), prefix.length());
}
/*!
* @ru @brief Проверить, начинается ли строка с указанной подстроки без учета регистра ASCII, за которой следует ней пробельный ASCII символ.
* @param prefix - проверяемая подстрока.
* @en @brief Check if a string begins with the specified case-insensitive ASCII substring followed by a whitespace ASCII character.
* @param prefix - substring to be checked.
*/
constexpr bool starts_with_ia_and_ws(str_piece prefix) const noexcept {
return _len() > prefix.length() &&
starts_with_ia(prefix) &&
trim_operator<TrimSides::TrimLeft, K, size_t(-1), true>{}.isTrim(_str()[prefix.length()]);
}
/*!
* @ru @brief Проверить, начинается ли строка с указанной подстроки без учета регистра ASCII, за которой следует один из указанных символов.
* @param prefix - проверяемая подстрока.
* @param next_symbol - проверяемые после подстроки символы.
* @en @brief Check if a string begins with the specified case-insensitive ASCII substring followed by one of the specified characters.
* @param prefix - substring to be checked.
* @param next_symbol - symbols to be checked after the substring.
*/
constexpr bool starts_with_ia_and_oneof(str_piece prefix, str_piece next_symbol) const noexcept {
return _len() > prefix.length() &&
starts_with_ia(prefix) &&
trim_operator<TrimSides::TrimLeft, K, 0, false>{next_symbol}.isTrim(_str()[prefix.length()]);
}
template<typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From> requires is_const_pattern<N>
constexpr bool starts_with_ia_and_oneof(str_piece prefix, T&& next_symbol) const noexcept {
return _len() >= N &&
starts_with_ia(prefix) &&
trim_operator<TrimSides::TrimLeft, K, N - 1, false>{next_symbol}.isTrim(_str()[prefix.length()]);
}
// Является ли эта строка началом указанной строки
// Is this string the beginning of the specified string
@ -4522,6 +4679,82 @@ public:
R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
return R::replaced_from(d(), pattern, repl, offset, maxCount);
}
/*!
* @ru @brief Получить строку без префикса, если она начинается с него.
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
* @param prefix - искомый префикс.
* @return constexpr std::optional<R> - если строка начинается с указанного префикса, возвращает часть строки без этого префикса,
* иначе пустое значение.
* @en @brief Get a string without a prefix if it starts with one.
* @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, returns the part of the string without this prefix,
* otherwise empty value.
*/
template<typename R = str_piece>
constexpr std::optional<R> strip_prefix(str_piece prefix) const {
if (starts_with(prefix)) {
return R{get(prefix.length(), to_end)};
}
return {};
}
/*!
* @ru @brief Получить строку без префикса, если она начинается с него без учёта регистра ASCII символов.
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
* @param prefix - искомый префикс.
* @return constexpr std::optional<R> - если строка начинается с указанного префикса без учёта регистра ASCII символов,
* возвращает часть строки без этого префикса, иначе пустое значение.
* @en @brief Get a string without a prefix if it starts with it, insensitive to ASCII characters.
* @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 ASCII characters,
* returns the part of the string without this prefix, otherwise empty.
*/
template<typename R = str_piece>
constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
if (starts_with_ia(prefix)) {
return R{get(prefix.length(), to_end)};
}
return {};
}
/*!
* @ru @brief Получить строку без суффикса, если она заканчивается им.
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
* @param suffix - искомый суффикс.
* @return constexpr std::optional<R> - если строка заканчивается указанным суффиксом, возвращает часть строки без него,
* иначе пустое значение.
* @en @brief Get a string without a suffix if it ends with one.
* @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, returns the part of the string without it,
* otherwise empty value.
*/
template<typename R = str_piece>
constexpr std::optional<R> strip_suffix(str_piece suffix) const {
if (ends_with(suffix)) {
return R{get(0, from_end{suffix.length()})};
}
return {};
}
/*!
* @ru @brief Получить строку без суффикса, если она заканчивается им без учёта регистра ASCII символов.
* @tparam R - желаемый тип строкового объекта, по умолчанию str_piece.
* @param suffix - искомый суффикс.
* @return constexpr std::optional<R> - если строка заканчивается указанным суффиксом без учёта регистра ASCII символов,
* возвращает часть строки без него, иначе пустое значение.
* @en @brief Get a string without a suffix if it ends with one in case-insensitive ASCII characters.
* @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 ASCII characters,
* returns part of the string without it, otherwise empty.
*/
template<typename R = str_piece>
constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
if (ends_with_ia(suffix)) {
return R{get(0, from_end{suffix.length()})};
}
return {};
}
template<StrType<K> From>
constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
@ -4568,7 +4801,7 @@ public:
* @return R - a string with leading whitespace characters removed.
*/
template<typename R = str_piece>
R trimmed_left() const {
constexpr R trimmed_left() const {
return R::template trim_static<TrimSides::TrimLeft>(d());
}
/*!
@ -4580,7 +4813,7 @@ public:
* @return R - a string with whitespace characters removed at the end.
*/
template<typename R = str_piece>
R trimmed_right() const {
constexpr R trimmed_right() const {
return R::template trim_static<TrimSides::TrimRight>(d());
}
/*!
@ -4595,7 +4828,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed(T&& pattern) const {
constexpr R trimmed(T&& pattern) const {
return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
}
/*!
@ -4610,7 +4843,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_left(T&& pattern) const {
constexpr R trimmed_left(T&& pattern) const {
return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
}
/*!
@ -4625,7 +4858,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_right(T&& pattern) const {
constexpr R trimmed_right(T&& pattern) const {
return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
}
// Триминг по символам в литерале и пробелам
@ -4647,7 +4880,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_with_spaces(T&& pattern) const {
constexpr R trimmed_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
}
/*!
@ -4666,7 +4899,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_left_with_spaces(T&& pattern) const {
constexpr R trimmed_left_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
}
/*!
@ -4685,7 +4918,7 @@ public:
*/
template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N>
R trimmed_right_with_spaces(T&& pattern) const {
constexpr R trimmed_right_with_spaces(T&& pattern) const {
return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
}
// Триминг по динамическому источнику
@ -4702,7 +4935,7 @@ public:
* @return R - a string with the characters contained in the pattern removed at the beginning and at the end.
*/
template<typename R = str_piece>
R trimmed(str_piece pattern) const {
constexpr R trimmed(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
}
/*!
@ -4716,7 +4949,7 @@ public:
* @return R - a string with the characters contained in the pattern removed at the beginning.
*/
template<typename R = str_piece>
R trimmed_left(str_piece pattern) const {
constexpr R trimmed_left(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
}
/*!
@ -4730,7 +4963,7 @@ public:
* @return R - a string with characters contained in the pattern removed at the end.
*/
template<typename R = str_piece>
R trimmed_right(str_piece pattern) const {
constexpr R trimmed_right(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
}
/*!
@ -4748,7 +4981,7 @@ public:
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_with_spaces(str_piece pattern) const {
constexpr R trimmed_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
}
/*!
@ -4766,7 +4999,7 @@ public:
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_left_with_spaces(str_piece pattern) const {
constexpr R trimmed_left_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
}
/*!
@ -4784,10 +5017,95 @@ public:
* and whitespace characters.
*/
template<typename R = str_piece>
R trimmed_right_with_spaces(str_piece pattern) const {
constexpr R trimmed_right_with_spaces(str_piece pattern) const {
return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
}
/*!
* @ru @brief Возвращает строку, в которой удалено начало строки, если она начинается с указанного префикса.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param prefix - искомый префикс.
* @param max_count - до сколько раз можно удалять префикс, 0 - без ограничений.
* @return R - Копию строки, в которой удалено начало, если она начинается с этого префикса.
* @en @brief Returns a string with the beginning of the string removed if it begins with the specified prefix.
* @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.
*/
template<typename R = str_piece>
constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
str_piece res = *this;
while(res.starts_with(prefix)) {
res.remove_prefix(prefix.length());
if (--max_count == 0) {
break;
}
}
return res;
}
/*!
* @ru @brief Возвращает строку, в которой удалено начало строки, если она начинается с указанного префикса без учёта регистра ASCII символов.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param prefix - искомый префикс.
* @param max_count - до сколько раз можно удалять префикс, 0 - без ограничений.
* @return R - Копию строки, в которой удалено начало, если она начинается с этого префикса без учёта регистра ASCII символов.
* @en @brief Returns a string with the beginning of the string removed if it begins with the specified prefix, insensitive to ASCII characters.
* @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 ASCII characters.
*/
template<typename R = str_piece>
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
str_piece res = *this;
while(res.starts_with_ia(prefix)) {
res.remove_prefix(prefix.length());
if (--max_count == 0) {
break;
}
}
return res;
}
/*!
* @ru @brief Возвращает строку, в которой удалён конец строки, если она заканчивается указанным суффиксом.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param suffix - искомый суффикс.
* @param max_count - до сколько раз можно удалять суффикс, 0 - без ограничений.
* @return R - Копию строки, в которой удалён конец, если она заканчивается этим суффиксом.
* @en @brief Returns a string with the beginning of the string removed if it begins with the specified prefix.
* @tparam R - desired string type, default str_src.
* @param suffix - the prefix to search 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 beginning removed, if it starts with this prefix.
*/
template<typename R = str_piece>
constexpr R trimmed_suffix(str_piece suffix) const {
str_piece res = *this;
while(res.ends_with(suffix)) {
res.remove_suffix(suffix.length());
}
return res;
}
/*!
* @ru @brief Возвращает строку, в которой удалён конец строки, если она заканчивается указанным суффиксом без учёта регистра ASCII символов.
* @tparam R - желаемый тип строки, по умолчанию str_src.
* @param suffix - искомый суффикс.
* @param max_count - до сколько раз можно удалять суффикс, 0 - без ограничений.
* @return R - Копию строки, в которой удалён конец, если она заканчивается этим суффиксом без учёта регистра ASCII символов.
* @en @brief Returns a string with the end of the string removed if it ends with the specified suffix, insensitive to ASCII characters.
* @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 ASCII characters.
*/
template<typename R = str_piece>
constexpr R trimmed_suffix_ia(str_piece suffix) const {
str_piece res = *this;
while(res.ends_with_ia(suffix)) {
res.remove_suffix(suffix.length());
}
return res;
}
/*!
* @ru @brief Получить объект `Splitter` по заданному разделителю, который позволяет последовательно
* получать подстроки методом `next()`, пока `is_done()` false.

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Version 1.7.2.
Version 1.9.1
<h2>Speed up your work with strings by 2-10 times!</h2>
@ -76,13 +76,14 @@ When using only `#include "simstr/strexpr.h"`:
- str::replace - replaces occurrences of the search substring with a replacement string or string expression.
If the substring is not found, the string expression is not even evaluated.
- str::make_ascii_upper, str::make_ascii_lower - change the case of ASCII characters.
- Parsing integers with the possibility of "fine" tuning at compile time - you can set options for checking overflow,
skipping whitespace characters, a specific radix or auto-selection by prefixes `0x`, `0`, `0b`, `0o`,
the admissibility of the `+` sign. Parsing is implemented for all types of strings and characters.
- Parsing double for `char` and `wchar_t`, as well as character types compatible with them in size.
- Parsing of integers from a "piece of string" (does not require null termination) with the possibility of "fine" tuning during compilation -
you can set options for checking for overflow, skipping whitespace characters, a specific radix, or auto-select by
prefixes `0x`, `0`, `0b`, `0o`, the `+` sign is allowed. Parsing is implemented for all types of strings and characters.
- Parsing double from a "piece of string" for `char` and `char8_t`.
When using the full version of the library:
- Everything that is listed above, plus
- Parsing double from a "piece of string" for all types of characters.
- Additional efficient string objects - `sstring` (shared string), `lstring` (local string).
- `lstring` - supports many mutable operations with strings - various replacements, insertions, deletions, etc.
Allows you to set the size for the internal character buffer, which can turn *Small String Optimization* into *Big String Optimization* :).
@ -324,8 +325,8 @@ function(add_simstr)
simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE
GIT_TAG tags/rel1.7.2 # Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.7.2
GIT_TAG tags/rel1.9.1# Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.9.1
)
FetchContent_MakeAvailable(simstr)
endfunction()

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Версия 1.7.2.
Версия 1.9.1
<h2>Ускорь работу со строками в 2-10 раз!</h2>
@ -77,13 +77,14 @@
- str::replace - заменяет вхождения искомой подстроки на строку замены или строковое выражение.
Если подстрока не найдена, строковое выражение даже не вычисляется.
- str::make_ascii_upper, str::make_ascii_lower - смена регистра ASCII символов.
- Парсинг целых чисел с возможностью "тонкой" настройки при компиляции - можно задавать опции проверки переполнения,
пропуск пробельных символов, конкретное основание счисления либо автовыбор по префиксам `0x`, `0`, `0b`, `0o`,
допустимость знака `+`. Парсинг реализован для всех видов строк и символов.
- Парсинг double для `char` и `wchar_t`, а также совместимых с ними по размеру типов символов.
- Парсинг целых чисел из "куска строки" (не требует нуль-терминированности) с возможностью "тонкой" настройки при компиляции -
можно задавать опции проверки переполнения, пропуск пробельных символов, конкретное основание счисления либо автовыбор по
префиксам `0x`, `0`, `0b`, `0o`, допустимость знака `+`. Парсинг реализован для всех видов строк и символов.
- Парсинг double из "куска строки" для `char` и `char8_t`.
При использовании полной версии библиотеки:
- Всё то же, что и перечислено выше, плюс
- Парсинг double из "куска строки" для всех типов символов.
- Дополнительные эффективные строковые объекты - `sstring` (shared string), `lstring` (local string).
- `lstring` - поддерживает множество мутабельных операций со строками - различные замены, вставки, удаления и т.п.
Позволяет задавать размер для внутреннего буфера символов, что может превращать *Small String Optimization* в *Big String Optimization* :).
@ -325,8 +326,8 @@ function(add_simstr)
simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE
GIT_TAG tags/rel1.7.2 # Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.7.2
GIT_TAG tags/rel1.9.1# Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.9.1
)
FetchContent_MakeAvailable(simstr)
endfunction()

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Реализация строковых функций
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -250,7 +250,7 @@ size_t utf8_case_change(const u8s*& src, size_t len, u8s*& dest, size_t lenBuffe
// то есть если символ считан, и длина записываемого символа не больше считанного, то он поместится в буфер записи
// и не перетрет символы, которые еще не прочитали
// По другому работать откажемся
if (lenBuffer < len || (dest > src && dest < src + len))
if (dest > src && dest < src + len)
return len;
const uu8s *beginReadPos = reinterpret_cast<const uu8s*>(src), *readPos = beginReadPos, *endReadPos = beginReadPos + len,
*readFromPos = readPos;

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -427,4 +427,47 @@ TEST(StrExpr, ChangeCase) {
EXPECT_EQ(ures, u"/teST/");
}
TEST(StrExpr, StripPrefixSuffix) {
const ssa from = "begin--end";
EXPECT_EQ(*from.strip_prefix("begin"), "--end");
EXPECT_FALSE(from.strip_prefix("sd").has_value());
EXPECT_EQ(*from.strip_suffix_ia("End"), "begin--");
EXPECT_FALSE(from.strip_suffix("sd").has_value());
}
TEST(StrExpr, TrimPrefixSuffix) {
const ssa from = "beginbegin--end";
EXPECT_EQ(from.trimmed_prefix("begin"), "--end");
EXPECT_EQ(from.trimmed_prefix("begin", 1), "begin--end");
EXPECT_EQ(from.trimmed_prefix("sd"), from);
EXPECT_EQ(from.trimmed_suffix_ia("End"), "beginbegin--");
EXPECT_EQ(from.trimmed_suffix("sd"), from);
}
TEST(StrExpr, StartWithAnd) {
EXPECT_TRUE("begin --end"_ss.starts_with_and_ws("begin"));
EXPECT_FALSE("beginn --end"_ss.starts_with_and_ws("begin"));
EXPECT_TRUE("BegiN\t--end"_ss.starts_with_ia_and_ws("begin"_ss));
EXPECT_TRUE("begin[ --end"_ss.starts_with_and_oneof("begin", "/*[]"));
EXPECT_TRUE("Begin[ --end"_ss.starts_with_ia_and_oneof("begin", "/*[]"_ss));
}
TEST(SimStr, Get) {
ssa testa{"test"};
EXPECT_EQ(testa.get(1, to_end), "est");
EXPECT_EQ(testa.get(1, 2), "es");
EXPECT_EQ(testa.get(1, from_end(1)), "es");
EXPECT_EQ(testa.get(1, from_end(10)), "");
EXPECT_EQ(testa.get(from_end(3), from_end(1)), "es");
ssu testu{u"test"};
EXPECT_EQ(testu.get(1, to_end), u"est");
EXPECT_EQ(testu.get(1, 2), u"es");
EXPECT_EQ(testu.get(from_end(3), from_end(1)), u"es");
ssw testw{L"test"};
EXPECT_EQ(testw.get(1, to_end), L"est");
EXPECT_EQ(testw.get(1, 2), L"es");
EXPECT_EQ(testw.get(from_end(3), from_end(1)), L"es");
}
} // namespace simstr::tests

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -24,6 +24,9 @@ public:
size_t sharedCount() const {
return type_ == Shared ? SharedStringData<u8s>::from_str(sstr_)->ref_.load() : 0u;
}
size_t sharedCountForce() const {
return SharedStringData<u8s>::from_str(sstr_)->ref_.load();
}
};
TEST(SimStr, CreateSimpleEmpty) {
@ -271,6 +274,7 @@ TEST(SimStr, SimpleFind) {
EXPECT_EQ("abccccc"_ss.find_last("ab"), 0);
}
#if 0
TEST(SimStr, SubPiece) {
ssa testa{"test"};
EXPECT_EQ(testa(1), "est");
@ -286,39 +290,83 @@ TEST(SimStr, SubPiece) {
EXPECT_EQ(testw(1), L"est");
EXPECT_EQ(testw(1, 2), L"es");
}
#endif
TEST(SimStr, Get) {
ssa testa{"test"};
EXPECT_EQ(testa.get(1, to_end), "est");
EXPECT_EQ(testa.get(1, 2), "es");
EXPECT_EQ(testa.get(1, from_end(1)), "es");
EXPECT_EQ(testa.get(1, from_end(10)), "");
ssu testu{u"test"};
EXPECT_EQ(testu.get(1, to_end), u"est");
EXPECT_EQ(testu.get(1, 2), u"es");
ssw testw{L"test"};
EXPECT_EQ(testw.get(1, to_end), L"est");
EXPECT_EQ(testw.get(1, 2), L"es");
}
TEST(SimStr, SimpleSubstr) {
ssa testa = "test";
#if 0
EXPECT_EQ(testa.substr(1, 0), "est");
EXPECT_EQ(testa.substr(1, 2), "es");
EXPECT_EQ(testa.substr(0, -1), "tes");
EXPECT_EQ(testa.substr(-2), "st");
EXPECT_EQ(testa.substr(-3, 2), "es");
EXPECT_EQ(testa.substr(-3, -1), "es");
#endif
EXPECT_EQ(testa.sub(1, to_end), "est");
EXPECT_EQ(testa.sub(1, 2), "es");
EXPECT_EQ(testa.sub(0, from_end(1)), "tes");
EXPECT_EQ(testa.sub(from_end(2), to_end), "st");
EXPECT_EQ(testa.sub(from_end(3), 2), "es");
EXPECT_EQ(testa.sub(from_end(3), from_end(1)), "es");
EXPECT_EQ(testa.str_mid(1), "est");
EXPECT_EQ(testa.str_mid(1, 0), "");
EXPECT_EQ(testa.str_mid(1, 2), "es");
EXPECT_EQ(testa.str_mid(2, 2), "st");
ssu testu = u"test";
#if 0
EXPECT_EQ(testu.substr(1, 0), u"est");
EXPECT_EQ(testu.substr(1, 2), u"es");
EXPECT_EQ(testu.substr(0, -1), u"tes");
EXPECT_EQ(testu.substr(-2), u"st");
EXPECT_EQ(testu.substr(-3, 2), u"es");
EXPECT_EQ(testu.substr(-3, -1), u"es");
#endif
EXPECT_EQ(testu.sub(1, to_end), u"est");
EXPECT_EQ(testu.sub(1, 2), u"es");
EXPECT_EQ(testu.sub(0, from_end(1)), u"tes");
EXPECT_EQ(testu.sub(from_end(2), to_end), u"st");
EXPECT_EQ(testu.sub(from_end(3), 2), u"es");
EXPECT_EQ(testu.sub(from_end(3), from_end(1)), u"es");
EXPECT_EQ(testu.str_mid(1), u"est");
EXPECT_EQ(testu.str_mid(1, 0), u"");
EXPECT_EQ(testu.str_mid(1, 2), u"es");
EXPECT_EQ(testu.str_mid(2, 2), u"st");
ssw testw = L"test";
#if 0
EXPECT_EQ(testw.substr(1, 0), L"est");
EXPECT_EQ(testw.substr(1, 2), L"es");
EXPECT_EQ(testw.substr(0, -1), L"tes");
EXPECT_EQ(testw.substr(-2), L"st");
EXPECT_EQ(testw.substr(-3, 2), L"es");
EXPECT_EQ(testw.substr(-3, -1), L"es");
#endif
EXPECT_EQ(testw.sub(1, to_end), L"est");
EXPECT_EQ(testw.sub(1, 2), L"es");
EXPECT_EQ(testw.sub(0, from_end(1)), L"tes");
EXPECT_EQ(testw.sub(from_end(2), to_end), L"st");
EXPECT_EQ(testw.sub(from_end(3), 2), L"es");
EXPECT_EQ(testw.sub(from_end(3), from_end(1)), L"es");
EXPECT_EQ(testw.str_mid(1), L"est");
EXPECT_EQ(testw.str_mid(1, 0), L"");
EXPECT_EQ(testw.str_mid(1, 2), L"es");
@ -327,7 +375,7 @@ TEST(SimStr, SimpleSubstr) {
TEST(SimStr, ToInt) {
EXPECT_EQ(ssa{" 123"}.as_int<int>(), 123);
EXPECT_EQ(ssa{" 123"}(0, -1).as_int<int>(), 12);
EXPECT_EQ(ssa{" 123"}.get(0, from_end(1)).as_int<int>(), 12);
EXPECT_EQ(ssa{"+123"}.as_int<int>(), 123);
EXPECT_EQ(ssa{" -123aa"}.as_int<int>(), -123);
EXPECT_EQ(ssa{"123"}.as_int<size_t>(), 123u);
@ -810,7 +858,7 @@ TEST(SimStr, AssignSstring) {
EXPECT_EQ(test = ssa{"other"}, "other");
EXPECT_EQ(test = stringa{"trtr"_ss + 10}, "trtr10");
EXPECT_EQ(test = "trtr"_ss + 20, "trtr20");
EXPECT_EQ(test = test(2), "tr20");
EXPECT_EQ(test = test.get(2, to_end), "tr20");
EXPECT_EQ(test = lstringa<10>{"func"}, "func");
EXPECT_EQ(test = lstringsa<10>{"func"}, "func");
lstringsa<10> sample{15, "1234"};
@ -939,10 +987,10 @@ TEST(SimStr, LStringAssign) {
test = lstringa<1>{"next step"};
EXPECT_EQ(test, "next step");
test = test(0);
test = test.get(0, to_end);
EXPECT_EQ(test, "next step");
test = test(1, 2);
test = test.get(1, 2);
EXPECT_EQ(test, "ex");
test = e_c(100, 'a');
@ -1402,7 +1450,7 @@ TEST(SimStr, LStrJoinAndExpressions) {
buffer.prepend(e_choice(test.length() > 2, eea + 99, eea + 12.1 + "asd"));
EXPECT_EQ(buffer, "99asd<>fgh<>jkl");
buffer.change(2, 4, test(0, 3) + "__" + 1 + ',');
buffer.change(2, 4, test.get(0, 3) + "__" + 1 + ',');
EXPECT_EQ(buffer, "99>as__1,>fgh<>jkl");
}
@ -2109,6 +2157,62 @@ TEST(SimStr, ConstEval) {
}
#endif
struct sized_alloc {
inline static size_t allocated = 0, dealloced = 0;
inline static void* alloced = nullptr;
void* allocate(size_t bytes) {
allocated += bytes;
return alloced = ::operator new(bytes);
}
template<typename T>
void deallocate(T* address, size_t size) noexcept {
dealloced += size;
EXPECT_EQ(address, alloced);
if constexpr (requires{::operator delete(address, size);}) {
::operator delete(address, size);
} else {
::operator delete(address);
}
}
};
TEST(SimStr, SizedAlloc) {
sized_alloc::allocated = sized_alloc::dealloced = 0;
{
lstring<u8s, 0, true, sized_alloc> src{10, "test"};
sstring<u8s, sized_alloc> str{std::move(src)};
EXPECT_TRUE(src.is_empty());
EXPECT_EQ(((Tstringa*)&str)->sharedCount(), SharedStringData<u8s>::check | 1);
}
EXPECT_EQ(sized_alloc::allocated, sized_alloc::dealloced);
EXPECT_EQ(sized_alloc::allocated, 64);
{
sstring<u8s, sized_alloc> str{10, "test"};
EXPECT_EQ(((Tstringa*)&str)->sharedCount(), 1);
}
EXPECT_EQ(sized_alloc::allocated, sized_alloc::dealloced);
EXPECT_EQ(sized_alloc::allocated, 113);
}
TEST(SimStr, SizedAllocU) {
sized_alloc::allocated = sized_alloc::dealloced = 0;
{
lstring<u16s, 0, true, sized_alloc> src{10, u"test"};
sstring<u16s, sized_alloc> str{std::move(src)};
EXPECT_TRUE(src.is_empty());
EXPECT_EQ(((Tstringa*)&str)->sharedCountForce(), SharedStringData<u8s>::check | 1);
}
EXPECT_EQ(sized_alloc::allocated, sized_alloc::dealloced);
EXPECT_EQ(sized_alloc::allocated, 112);
{
sstring<u16s, sized_alloc> str{10, u"test"};
EXPECT_EQ(((Tstringa*)&str)->sharedCountForce(), 1);
}
EXPECT_EQ(sized_alloc::allocated, sized_alloc::dealloced);
EXPECT_EQ(sized_alloc::allocated, 202);
}
} // namespace simstr::tests
TEST(SimStr, StrNoNamespace) {

View File

@ -1,5 +1,5 @@
/*
* ver. 1.7.2
* ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com