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( project(
simstr simstr
VERSION 1.7.2 VERSION 1.9.1
DESCRIPTION "Yet another modern C++ string library" DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr" HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX LANGUAGES CXX
@ -131,6 +131,9 @@ if(SIMSTR_BUILD_TESTS)
if(TARGET gtest) if(TARGET gtest)
target_compile_features(gtest PUBLIC cxx_std_23) target_compile_features(gtest PUBLIC cxx_std_23)
target_compile_features(gtest_main 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()
endif() endif()

View File

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

View File

@ -185,7 +185,7 @@
<body> <body>
<div class="head"> <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://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 <span><a href="https://github.com/orefkov/simstr/blob/main/bench/bench_str.cpp" target="blank">Sources for
benchmarks</a></span> benchmarks</a></span>

View File

@ -38,7 +38,7 @@ struct result_info {
} }
// Текущее положение поставим сразу за cpuinfo и откинем завершающие переводы строк // Текущее положение поставим сразу за cpuinfo и откинем завершающие переводы строк
// We will put the current position immediately after cpuinfo and discard the final line feeds // 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 // 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 (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); 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; return results;
@ -117,7 +117,7 @@ void write_platforms_cpu(out_t& out, const results_vector& results) {
for (const auto& r : results) { for (const auto& r : results) {
size_t rp = r.cpu_info_.find('(') + 1, lp = r.cpu_info_.find(')', rp); size_t rp = r.cpu_info_.find('(') + 1, lp = r.cpu_info_.find(')', rp);
ssa shortCpuInfo = r.cpu_info_.from_to(rp, lp); 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"--( 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>)--", <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++); r.platform_, shortCpuInfo, cpuInfo, counter++);
@ -167,7 +167,7 @@ ssa extract_name_result(ssa line, ssa& result) {
line.len = ns; line.len = ns;
if (inNs) { if (inNs) {
size_t end = line.find_last(' '); 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) { if (auto rp = line.find("/repeats"); rp != str::npos) {
line.len = rp; line.len = rp;
} else { } else {
@ -182,7 +182,7 @@ ssa extract_name_result(ssa line, ssa& result) {
ssa extract_comment(ssa commentsText, ssa benchmarkName) { ssa extract_comment(ssa commentsText, ssa benchmarkName) {
size_t idx = commentsText.find_end(lstringa<120>{"- " + benchmarkName + "\n"}); size_t idx = commentsText.find_end(lstringa<120>{"- " + benchmarkName + "\n"});
if (idx != str::npos) { 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)); 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('/'); 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; benchName.len = delim;
} }
auto [it, not_exist] = textes.try_emplace(benchName, stringa{}, 0); 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; std::cerr << "Can not found benchmark function name for " << benchName << std::endl;
return {stra::empty_str, 0}; 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) { if (start == str::npos) {
std::cerr << "Can not found benchmark function name for " << benchName << std::endl; std::cerr << "Can not found benchmark function name for " << benchName << std::endl;
return {stra::empty_str, 0}; 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); start = sourceText.find_last('\n', start);
size_t templ_start = sourceText.find_last('\n', start) + 1; 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; start = templ_start;
} else { } else {
start++; 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"}; throw std::runtime_error{"Not found end of func"};
} }
lstringa<2048> text{sourceText.from_to(beginLine, end + indent.length() + 1), indent, "\n"}; 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 { } else {
end = sourceText.find("\n}\n", start); end = sourceText.find("\n}\n", start);
func_it->second.first = repl_html_symbols(sourceText.from_to(start, end + 2)); 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) { 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) { if (!releaseVersion) {
std::cerr << "Not found release version in sources" << std::endl; std::cerr << "Not found release version in sources" << std::endl;
throw std::runtime_error{"Not found release version in sources"}; 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()) { while(!splitters[0].is_done()) {
ssa line = splitters[0].next(), benchName, result; ssa line = splitters[0].next(), benchName, result;
if (auto rm = line.find("_mean"); rm != str::npos) { 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 [source, line_num] = extract_source_for_benchmark(benchName, sourceText);
auto comment = extract_comment(commentsText, benchName); 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"}; throw std::runtime_error{"Not expected end of file"};
} }
line = splitters[idx].next(); 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) { if (rbench_name != benchName) {
while (line.find("_mean") == str::npos && !splitters[idx].is_done()) { while (line.find("_mean") == str::npos && !splitters[idx].is_done()) {
line = splitters[idx].next(); 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) { if (rbench_name != benchName) {
std::cerr << "In results for " << results[idx].platform_ << " benchmark '" << rbench_name std::cerr << "In results for " << results[idx].platform_ << " benchmark '" << rbench_name
<< "' does not match with other results" << std::endl; << "' 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 # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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 # 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 # 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. # this, this feature is disabled by default.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # 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 # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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 # 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 # 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. # this, this feature is disabled by default.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.

View File

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

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.7.2 * ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Классы для работы со строками * Классы для работы со строками
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -373,6 +373,87 @@ public:
constexpr void as_number(T& t) const { constexpr void as_number(T& t) const {
base::as_number(t); 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) init(size_t size)
set_size(size_t size)
*/ */
template<typename O> template<typename O>
requires(!std::is_same_v<O, K>) requires(!std::is_same_v<O, K>)
@ -760,6 +840,94 @@ expr_utf<From, To> e_utf(simple_str<From> from) {
return {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 Концепт типа, который может сохранить строку. * @ru @brief Концепт типа, который может сохранить строку.
* @en @brief A type concept that can store a string. * @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)` - выделить место для строки указанного размера, вернуть адрес * - `K* init(size_t size)` - выделить место для строки указанного размера, вернуть адрес
* - `void create_empty()` - создать пустой объект * - `void create_empty()` - создать пустой объект
* - `K* set_size(size_t size)` - перевыделить место для строки, если при создании не угадали
* нужный размер и место нужно больше или меньше.
* Содержимое строки нужно оставить.
* Хотя тип аллокатора и задаётся параметром шаблона, делается это только для проброса * Хотя тип аллокатора и задаётся параметром шаблона, делается это только для проброса
* его типа в конструкторы, методы аллокатора не вызываются. Если наследник не пользуется * его типа в конструкторы, методы аллокатора не вызываются. Если наследник не пользуется
* аллокатором, а сам в `init` и `set_size` как-то выделяет место, может указать типом аллокатора * аллокатором, а сам в `init` как-то выделяет место, может указать типом аллокатора
* какой-либо пустой класс. * какой-либо пустой класс.
* @en @brief The base for the objects that own the string. * @en @brief The base for the objects that own the string.
* @tparam K - character type. * @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 * 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 * - `K* init(size_t size)` - allocate space for a string of the specified size, return the address
* - `void create_empty()` - create an empty object * - `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 * 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 * 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. * any empty class.
*/ */
template<typename K, typename Impl, typename Allocator> template<typename K, typename Impl, typename Allocator>
@ -987,74 +1149,6 @@ protected:
*ptr = 0; *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: public:
inline static constexpr bool is_str_storable = true; inline static constexpr bool is_str_storable = true;
@ -1183,7 +1277,7 @@ public:
template<StrType<K> From, typename... Args> template<StrType<K> From, typename... Args>
requires std::is_constructible_v<allocator_t, Args...> requires std::is_constructible_v<allocator_t, Args...>
static my_type upperred_only_ascii_from(const From& f, Args&&... 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. * @ru @brief Создать копию переданной строки в нижнем регистре символов ASCII.
@ -1196,7 +1290,7 @@ public:
template<StrType<K> From, typename... Args> template<StrType<K> From, typename... Args>
requires std::is_constructible_v<allocator_t, Args...> requires std::is_constructible_v<allocator_t, Args...>
static my_type lowered_only_ascii_from(const From& f, Args&&... 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). * @ru @brief Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF).
@ -1213,7 +1307,7 @@ public:
template<StrType<K> From, typename... Args> template<StrType<K> From, typename... Args>
requires std::is_constructible_v<allocator_t, Args...> requires std::is_constructible_v<allocator_t, Args...>
static my_type upperred_from(const From& f, Args&&... 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). * @ru @brief Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF).
@ -1230,7 +1324,7 @@ public:
template<StrType<K> From, typename... Args> template<StrType<K> From, typename... Args>
requires std::is_constructible_v<allocator_t, Args...> requires std::is_constructible_v<allocator_t, Args...>
static my_type lowered_from(const From& f, Args&&... 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 Создать копию переданной строки с заменой подстрок. * @ru @brief Создать копию переданной строки с заменой подстрок.
@ -1260,11 +1354,28 @@ public:
* @en @brief Concept of a memory management type * @en @brief Concept of a memory management type
*/ */
template<typename A> 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.allocate(size) } -> std::same_as<void*>;
{ a.deallocate(void_ptr) } noexcept -> 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 { struct printf_selector {
template<typename K, typename... T> requires (is_one_of_std_char_v<K>) 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) { static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
@ -2496,28 +2607,47 @@ public:
template<typename K> template<typename K>
struct SharedStringData { struct SharedStringData {
std::atomic_size_t ref_; // Счетчик ссылок | Reference count std::atomic_size_t ref_; // Счетчик ссылок | Reference count
inline static constexpr size_t mask = (size_t(-1)) >> 1, check = ~mask;
SharedStringData() { SharedStringData() {
ref_ = 1; 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 { K* str() const {
return (K*)(this + 1); return (K*)(this + 1);
} }
void incr() { void incr() {
ref_.fetch_add(1, std::memory_order_relaxed); 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); size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
if (val == 1) { if (val == 1) {
allocator.deallocate(this); allocator.deallocate(this);
} }
} }
static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) { void decr(AllocatableSized auto& allocator, size_t len) {
size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K); 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(); return new (allocator.allocate(size)) SharedStringData();
} }
static SharedStringData<K>* from_str(const K* p) { static SharedStringData* from_str(const K* p) {
return (SharedStringData<K>*)p - 1; return (SharedStringData*)p - 1;
} }
K* place(K* p, size_t len) { K* place(K* p, size_t len) {
ch_traits<K>::copy(p, str(), len); ch_traits<K>::copy(p, str(), len);
@ -2530,10 +2660,10 @@ struct SharedStringData {
class string_common_allocator { class string_common_allocator {
public: public:
void* allocate(size_t bytes) { void* allocate(size_t bytes) {
return new char[bytes]; return ::operator new(bytes);
} }
void deallocate(void* address) noexcept { 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); // your_allocator_type default_string_allocator_selector(int);
using allocator_string = decltype(default_string_allocator_selector(int(0))); using allocator_string = decltype(default_string_allocator_selector(int(0)));
template<typename K, Allocatorable Allocator> template<typename K, Allocatable Allocator>
class sstring; 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 * 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. * +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 : class decl_empty_bases lstring :
public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>, public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
public str_mutable<K, lstring<K, N, forShared, Allocator>>, public str_mutable<K, lstring<K, N, forShared, Allocator>>,
@ -2597,7 +2727,7 @@ public:
protected: protected:
enum : size_t { 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>; using base_algs = str_algs<K, simple_str<K>, my_type, true>;
@ -2651,7 +2781,11 @@ protected:
constexpr void dealloc() { constexpr void dealloc() {
if (is_alloced()) { if (is_alloced()) {
if constexpr (AllocatableNoSized<Allocator>) {
base_storable::allocator().deallocate(to_real_address(data_)); base_storable::allocator().deallocate(to_real_address(data_));
} else {
base_storable::allocator().deallocate(to_real_address(data_), (capacity_ + 1) * sizeof(K) + extra);
}
data_ = local_; data_ = local_;
} }
} }
@ -3123,7 +3257,7 @@ public:
} }
/*! /*!
* @ru @brief Определить длину строки. * @ru @brief Определить длину строки.
* Ищет символ 0 в буфере строки до его ёмкости, после чего устаналивает длину строки по найденному 0. * Ищет символ 0 в буфере строки до его ёмкости, после чего устанавливает длину строки по найденному 0.
* @en @brief Determine the length of the string. * @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. * 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) { if (is_alloced() && capacity_ > need_capacity) {
K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity); K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
traits::copy(newData, data_, size_ + 1); traits::copy(newData, data_, size_ + 1);
base_storable::allocator().deallocate(to_real_address(data_)); dealloc();
data_ = newData; data_ = newData;
if (size_ > LocalCapacity) { 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 u16s - 32 bytes, stores strings of up to 15 characters + 0
* - for u32s - 32 bytes, stores strings of up to 7 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 : class decl_empty_bases sstring :
public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>, public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
public str_storable<K, sstring<K, Allocator>, Allocator>, public str_storable<K, sstring<K, Allocator>, Allocator>,
@ -3340,49 +3474,15 @@ protected:
} }
} }
K* set_size(size_t newSize) { void dealloc() {
// Вызывается при создании строки при необходимости изменить размер.
// Других ссылок на 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) {
if (type_ == Shared) { if (type_ == Shared) {
SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_); if constexpr (AllocatableNoSized<Allocator>) {
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);
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator()); SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
sstr_ = newStr; } else {
} SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator(), bigLen_);
} 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;
} }
} }
} }
K* str = type_ == Local ? buf_ : (K*)sstr_;
str[newSize] = 0;
return str;
}
public: public:
sstring() { sstring() {
@ -3490,9 +3590,7 @@ public:
static const sstring<K> empty_str; static const sstring<K> empty_str;
/// @ru Деструктор строки. @en String destructor. /// @ru Деструктор строки. @en String destructor.
constexpr ~sstring() { constexpr ~sstring() {
if (type_ == Shared) { dealloc();
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
}
} }
/*! /*!
* @ru @brief Конструктор копирования строки. * @ru @brief Конструктор копирования строки.
@ -3531,8 +3629,8 @@ public:
size_t size = src.length(); size_t size = src.length();
if (size) { if (size) {
if (src.is_alloced()) { if (src.is_alloced()) {
// Там динамический буфер, выделенный с запасом для SharedStringData. // Там динамический буфер, выделенный с запасом для SharedStringData и size_t.
// There is a dynamic buffer allocated with a reserve for SharedStringData. // There is a dynamic buffer allocated with a reserve for SharedStringData and size_t.
K* str = src.str(); K* str = src.str();
if (size > LocalCount) { if (size > LocalCount) {
// Просто присвоим его себе. // Просто присвоим его себе.
@ -3541,7 +3639,11 @@ public:
bigLen_ = size; bigLen_ = size;
type_ = Shared; type_ = Shared;
localRemain_ = 0; 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 { } else {
// Скопируем локально // Скопируем локально
// Copy locally // Copy locally
@ -3684,8 +3786,7 @@ public:
* @return my_type& - a reference to yourself. * @return my_type& - a reference to yourself.
*/ */
constexpr my_type& make_empty() noexcept { constexpr my_type& make_empty() noexcept {
if (type_ == Shared) dealloc();
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
create_empty(); create_empty();
return *this; 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{}; inline const sstring<K> sstring<K, Allocator>::empty_str{};
struct no_alloc{}; struct no_alloc{};
@ -4264,8 +4365,7 @@ public:
} }
auto erase(const InStore& key) { auto erase(const InStore& key) {
auto it = hash_t::find(key); if (auto it = hash_t::find(key); it != hash_t::end()) {
if (it != hash_t::end()) {
((sstring<K>*)it->first.node)->~sstring(); ((sstring<K>*)it->first.node)->~sstring();
hash_t::erase(it); hash_t::erase(it);
return 1; return 1;
@ -4277,15 +4377,151 @@ public:
return erase(toStoreType(key)); return erase(toStoreType(key));
} }
bool lookup(simple_str<K> txt, T& val) const { /*!
auto it = find(txt); * @ru @brief Поиск и извлечение значения.
if (it != hash_t::end()) { * @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; val = it->second;
return true; return true;
} }
return false; 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() { void clear() {
for (auto& k: *this) for (auto& k: *this)
((sstring<K>*)k.first.node)->~sstring(); ((sstring<K>*)k.first.node)->~sstring();
@ -4987,94 +5223,6 @@ inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
} }
} // namespace literals } // 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. * @ru @brief Оператор вывода в поток simple_str.
* @param stream - поток вывода. * @param stream - поток вывода.
@ -5169,7 +5317,7 @@ inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type
* @param text - text. * @param text - text.
* @return std::ostream&. * @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) { inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
return stream << std::string_view{text.symbols(), text.length()}; 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. * @param text - text.
* @return std::ostream&. * @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) { inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
return stream << std::wstring_view{text.symbols(), text.length()}; 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. * @param text - text.
* @return std::ostream&. * @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) { 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()}; 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) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции * База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov 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 Класс с базовыми константными строковыми алгоритмами. * @ru @brief Класс с базовыми константными строковыми алгоритмами.
* @details Является базой для классов, могущих выполнять константные операции со строками. * @details Является базой для классов, могущих выполнять константные операции со строками.
@ -3445,6 +3459,7 @@ public:
* "0123456789"_ss(-4, -1) == "678"; * "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 { 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, 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; idxEnd = len > 0 ? idxStart + len : (ptrdiff_t)myLen > -len ? myLen + len : 0;
@ -3454,6 +3469,83 @@ public:
idxStart = idxEnd; idxStart = idxEnd;
return str_piece{_str() + idxStart, idxEnd - idxStart}; 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 Получить часть строки как "кусок строки". * @ru @brief Получить часть строки как "кусок строки".
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку. * @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
@ -3500,7 +3592,7 @@ public:
* @return Substring, str_src. * @return Substring, str_src.
*/ */
constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept { 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 Проверка на пустоту. * @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. * @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...> 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) { if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
return fnd; 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. * @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. * @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 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
return my_type{d()(from, len)}; 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. * @ru @brief Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
* @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку. * @param from - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
@ -4355,6 +4452,36 @@ public:
constexpr bool starts_with(str_piece prefix) const noexcept { constexpr bool starts_with(str_piece prefix) const noexcept {
return starts_with(prefix.symbols(), prefix.length()); 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 { constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
size_t myLen = _len(); size_t myLen = _len();
@ -4380,6 +4507,36 @@ public:
constexpr bool starts_with_ia(str_piece prefix) const noexcept { constexpr bool starts_with_ia(str_piece prefix) const noexcept {
return starts_with_ia(prefix.symbols(), prefix.length()); 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 // 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 { 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); 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> template<StrType<K> From>
constexpr static my_type make_trim_op(const From& from, const auto& opTrim) { 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. * @return R - a string with leading whitespace characters removed.
*/ */
template<typename R = str_piece> template<typename R = str_piece>
R trimmed_left() const { constexpr R trimmed_left() const {
return R::template trim_static<TrimSides::TrimLeft>(d()); return R::template trim_static<TrimSides::TrimLeft>(d());
} }
/*! /*!
@ -4580,7 +4813,7 @@ public:
* @return R - a string with whitespace characters removed at the end. * @return R - a string with whitespace characters removed at the end.
*/ */
template<typename R = str_piece> template<typename R = str_piece>
R trimmed_right() const { constexpr R trimmed_right() const {
return R::template trim_static<TrimSides::TrimRight>(d()); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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> template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
requires is_const_pattern<N> 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); 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. * @return R - a string with the characters contained in the pattern removed at the beginning and at the end.
*/ */
template<typename R = str_piece> 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); 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. * @return R - a string with the characters contained in the pattern removed at the beginning.
*/ */
template<typename R = str_piece> 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); 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. * @return R - a string with characters contained in the pattern removed at the end.
*/ */
template<typename R = str_piece> 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); return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
} }
/*! /*!
@ -4748,7 +4981,7 @@ public:
* and whitespace characters. * and whitespace characters.
*/ */
template<typename R = str_piece> 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); return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
} }
/*! /*!
@ -4766,7 +4999,7 @@ public:
* and whitespace characters. * and whitespace characters.
*/ */
template<typename R = str_piece> 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); return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
} }
/*! /*!
@ -4784,10 +5017,95 @@ public:
* and whitespace characters. * and whitespace characters.
*/ */
template<typename R = str_piece> 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); 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` по заданному разделителю, который позволяет последовательно * @ru @brief Получить объект `Splitter` по заданному разделителю, который позволяет последовательно
* получать подстроки методом `next()`, пока `is_done()` false. * получать подстроки методом `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) [![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> <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. - 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. 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. - 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, - Parsing of integers from a "piece of string" (does not require null termination) with the possibility of "fine" tuning during compilation -
skipping whitespace characters, a specific radix or auto-selection by prefixes `0x`, `0`, `0b`, `0o`, you can set options for checking for overflow, skipping whitespace characters, a specific radix, or auto-select by
the admissibility of the `+` sign. Parsing is implemented for all types of strings and characters. prefixes `0x`, `0`, `0b`, `0o`, the `+` sign is allowed. 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 double from a "piece of string" for `char` and `char8_t`.
When using the full version of the library: When using the full version of the library:
- Everything that is listed above, plus - 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). - Additional efficient string objects - `sstring` (shared string), `lstring` (local string).
- `lstring` - supports many mutable operations with strings - various replacements, insertions, deletions, etc. - `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* :). 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 simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_TAG tags/rel1.7.2 # Specify the desired release GIT_TAG tags/rel1.9.1# Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.7.2 FIND_PACKAGE_ARGS NAMES simstr 1.9.1
) )
FetchContent_MakeAvailable(simstr) FetchContent_MakeAvailable(simstr)
endfunction() 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) [![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> <h2>Ускорь работу со строками в 2-10 раз!</h2>
@ -77,13 +77,14 @@
- str::replace - заменяет вхождения искомой подстроки на строку замены или строковое выражение. - str::replace - заменяет вхождения искомой подстроки на строку замены или строковое выражение.
Если подстрока не найдена, строковое выражение даже не вычисляется. Если подстрока не найдена, строковое выражение даже не вычисляется.
- str::make_ascii_upper, str::make_ascii_lower - смена регистра ASCII символов. - str::make_ascii_upper, str::make_ascii_lower - смена регистра ASCII символов.
- Парсинг целых чисел с возможностью "тонкой" настройки при компиляции - можно задавать опции проверки переполнения, - Парсинг целых чисел из "куска строки" (не требует нуль-терминированности) с возможностью "тонкой" настройки при компиляции -
пропуск пробельных символов, конкретное основание счисления либо автовыбор по префиксам `0x`, `0`, `0b`, `0o`, можно задавать опции проверки переполнения, пропуск пробельных символов, конкретное основание счисления либо автовыбор по
допустимость знака `+`. Парсинг реализован для всех видов строк и символов. префиксам `0x`, `0`, `0b`, `0o`, допустимость знака `+`. Парсинг реализован для всех видов строк и символов.
- Парсинг double для `char` и `wchar_t`, а также совместимых с ними по размеру типов символов. - Парсинг double из "куска строки" для `char` и `char8_t`.
При использовании полной версии библиотеки: При использовании полной версии библиотеки:
- Всё то же, что и перечислено выше, плюс - Всё то же, что и перечислено выше, плюс
- Парсинг double из "куска строки" для всех типов символов.
- Дополнительные эффективные строковые объекты - `sstring` (shared string), `lstring` (local string). - Дополнительные эффективные строковые объекты - `sstring` (shared string), `lstring` (local string).
- `lstring` - поддерживает множество мутабельных операций со строками - различные замены, вставки, удаления и т.п. - `lstring` - поддерживает множество мутабельных операций со строками - различные замены, вставки, удаления и т.п.
Позволяет задавать размер для внутреннего буфера символов, что может превращать *Small String Optimization* в *Big String Optimization* :). Позволяет задавать размер для внутреннего буфера символов, что может превращать *Small String Optimization* в *Big String Optimization* :).
@ -325,8 +326,8 @@ function(add_simstr)
simstr simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_TAG tags/rel1.7.2 # Укажите нужный релиз GIT_TAG tags/rel1.9.1# Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.7.2 FIND_PACKAGE_ARGS NAMES simstr 1.9.1
) )
FetchContent_MakeAvailable(simstr) FetchContent_MakeAvailable(simstr)
endfunction() endfunction()

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.7.2 * ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Реализация строковых функций * Реализация строковых функций
* (c) Project "SimStr", Aleksandr Orefkov 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; return len;
const uu8s *beginReadPos = reinterpret_cast<const uu8s*>(src), *readPos = beginReadPos, *endReadPos = beginReadPos + len, const uu8s *beginReadPos = reinterpret_cast<const uu8s*>(src), *readPos = beginReadPos, *endReadPos = beginReadPos + len,
*readFromPos = readPos; *readFromPos = readPos;

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.7.2 * ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr * Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -427,4 +427,47 @@ TEST(StrExpr, ChangeCase) {
EXPECT_EQ(ures, u"/teST/"); 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 } // namespace simstr::tests

View File

@ -1,5 +1,5 @@
/* /*
* ver. 1.7.2 * ver. 1.9.1
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr * Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com * (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -24,6 +24,9 @@ public:
size_t sharedCount() const { size_t sharedCount() const {
return type_ == Shared ? SharedStringData<u8s>::from_str(sstr_)->ref_.load() : 0u; 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) { TEST(SimStr, CreateSimpleEmpty) {
@ -271,6 +274,7 @@ TEST(SimStr, SimpleFind) {
EXPECT_EQ("abccccc"_ss.find_last("ab"), 0); EXPECT_EQ("abccccc"_ss.find_last("ab"), 0);
} }
#if 0
TEST(SimStr, SubPiece) { TEST(SimStr, SubPiece) {
ssa testa{"test"}; ssa testa{"test"};
EXPECT_EQ(testa(1), "est"); EXPECT_EQ(testa(1), "est");
@ -286,39 +290,83 @@ TEST(SimStr, SubPiece) {
EXPECT_EQ(testw(1), L"est"); EXPECT_EQ(testw(1), L"est");
EXPECT_EQ(testw(1, 2), L"es"); 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) { TEST(SimStr, SimpleSubstr) {
ssa testa = "test"; ssa testa = "test";
#if 0
EXPECT_EQ(testa.substr(1, 0), "est"); EXPECT_EQ(testa.substr(1, 0), "est");
EXPECT_EQ(testa.substr(1, 2), "es"); EXPECT_EQ(testa.substr(1, 2), "es");
EXPECT_EQ(testa.substr(0, -1), "tes"); EXPECT_EQ(testa.substr(0, -1), "tes");
EXPECT_EQ(testa.substr(-2), "st"); EXPECT_EQ(testa.substr(-2), "st");
EXPECT_EQ(testa.substr(-3, 2), "es"); EXPECT_EQ(testa.substr(-3, 2), "es");
EXPECT_EQ(testa.substr(-3, -1), "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), "est");
EXPECT_EQ(testa.str_mid(1, 0), ""); EXPECT_EQ(testa.str_mid(1, 0), "");
EXPECT_EQ(testa.str_mid(1, 2), "es"); EXPECT_EQ(testa.str_mid(1, 2), "es");
EXPECT_EQ(testa.str_mid(2, 2), "st"); EXPECT_EQ(testa.str_mid(2, 2), "st");
ssu testu = u"test"; ssu testu = u"test";
#if 0
EXPECT_EQ(testu.substr(1, 0), u"est"); EXPECT_EQ(testu.substr(1, 0), u"est");
EXPECT_EQ(testu.substr(1, 2), u"es"); EXPECT_EQ(testu.substr(1, 2), u"es");
EXPECT_EQ(testu.substr(0, -1), u"tes"); EXPECT_EQ(testu.substr(0, -1), u"tes");
EXPECT_EQ(testu.substr(-2), u"st"); EXPECT_EQ(testu.substr(-2), u"st");
EXPECT_EQ(testu.substr(-3, 2), u"es"); EXPECT_EQ(testu.substr(-3, 2), u"es");
EXPECT_EQ(testu.substr(-3, -1), 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), u"est");
EXPECT_EQ(testu.str_mid(1, 0), u""); EXPECT_EQ(testu.str_mid(1, 0), u"");
EXPECT_EQ(testu.str_mid(1, 2), u"es"); EXPECT_EQ(testu.str_mid(1, 2), u"es");
EXPECT_EQ(testu.str_mid(2, 2), u"st"); EXPECT_EQ(testu.str_mid(2, 2), u"st");
ssw testw = L"test"; ssw testw = L"test";
#if 0
EXPECT_EQ(testw.substr(1, 0), L"est"); EXPECT_EQ(testw.substr(1, 0), L"est");
EXPECT_EQ(testw.substr(1, 2), L"es"); EXPECT_EQ(testw.substr(1, 2), L"es");
EXPECT_EQ(testw.substr(0, -1), L"tes"); EXPECT_EQ(testw.substr(0, -1), L"tes");
EXPECT_EQ(testw.substr(-2), L"st"); EXPECT_EQ(testw.substr(-2), L"st");
EXPECT_EQ(testw.substr(-3, 2), L"es"); EXPECT_EQ(testw.substr(-3, 2), L"es");
EXPECT_EQ(testw.substr(-3, -1), 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), L"est");
EXPECT_EQ(testw.str_mid(1, 0), L""); EXPECT_EQ(testw.str_mid(1, 0), L"");
EXPECT_EQ(testw.str_mid(1, 2), L"es"); EXPECT_EQ(testw.str_mid(1, 2), L"es");
@ -327,7 +375,7 @@ TEST(SimStr, SimpleSubstr) {
TEST(SimStr, ToInt) { TEST(SimStr, ToInt) {
EXPECT_EQ(ssa{" 123"}.as_int<int>(), 123); 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{"+123"}.as_int<int>(), 123);
EXPECT_EQ(ssa{" -123aa"}.as_int<int>(), -123); EXPECT_EQ(ssa{" -123aa"}.as_int<int>(), -123);
EXPECT_EQ(ssa{"123"}.as_int<size_t>(), 123u); 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 = ssa{"other"}, "other");
EXPECT_EQ(test = stringa{"trtr"_ss + 10}, "trtr10"); EXPECT_EQ(test = stringa{"trtr"_ss + 10}, "trtr10");
EXPECT_EQ(test = "trtr"_ss + 20, "trtr20"); 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 = lstringa<10>{"func"}, "func");
EXPECT_EQ(test = lstringsa<10>{"func"}, "func"); EXPECT_EQ(test = lstringsa<10>{"func"}, "func");
lstringsa<10> sample{15, "1234"}; lstringsa<10> sample{15, "1234"};
@ -939,10 +987,10 @@ TEST(SimStr, LStringAssign) {
test = lstringa<1>{"next step"}; test = lstringa<1>{"next step"};
EXPECT_EQ(test, "next step"); EXPECT_EQ(test, "next step");
test = test(0); test = test.get(0, to_end);
EXPECT_EQ(test, "next step"); EXPECT_EQ(test, "next step");
test = test(1, 2); test = test.get(1, 2);
EXPECT_EQ(test, "ex"); EXPECT_EQ(test, "ex");
test = e_c(100, 'a'); 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")); buffer.prepend(e_choice(test.length() > 2, eea + 99, eea + 12.1 + "asd"));
EXPECT_EQ(buffer, "99asd<>fgh<>jkl"); 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"); EXPECT_EQ(buffer, "99>as__1,>fgh<>jkl");
} }
@ -2109,6 +2157,62 @@ TEST(SimStr, ConstEval) {
} }
#endif #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 } // namespace simstr::tests
TEST(SimStr, StrNoNamespace) { TEST(SimStr, StrNoNamespace) {

View File

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