mirror of https://github.com/orefkov/simstr.git
Compare commits
5 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e9eaa6c4cb | |
|
|
00a4a008a9 | |
|
|
86deccbac3 | |
|
|
a779aa7320 | |
|
|
e9e717c8bd |
|
|
@ -5,7 +5,7 @@ include(FetchContent)
|
||||||
|
|
||||||
project(
|
project(
|
||||||
simstr
|
simstr
|
||||||
VERSION 1.7.3
|
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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> 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> 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;
|
||||||
|
|
|
||||||
1421
bench/results.html
1421
bench/results.html
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
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
|
|
@ -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.3
|
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.
|
||||||
|
|
|
||||||
|
|
@ -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.3
|
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.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||||
* ver. 1.7.3
|
* ver. 1.9.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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
|
||||||
|
|
@ -775,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>)
|
||||||
|
|
@ -841,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.
|
||||||
|
|
@ -879,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.
|
||||||
|
|
@ -899,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 didn’t 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>
|
||||||
|
|
@ -1068,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;
|
||||||
|
|
@ -1264,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.
|
||||||
|
|
@ -1277,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).
|
||||||
|
|
@ -1294,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).
|
||||||
|
|
@ -1311,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 Создать копию переданной строки с заменой подстрок.
|
||||||
|
|
@ -1341,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) {
|
||||||
|
|
@ -2577,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);
|
||||||
|
|
@ -2611,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2627,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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2659,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>>,
|
||||||
|
|
@ -2678,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>;
|
||||||
|
|
@ -2732,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_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3204,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.
|
||||||
*/
|
*/
|
||||||
|
|
@ -3230,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) {
|
||||||
|
|
@ -3349,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>,
|
||||||
|
|
@ -3421,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() {
|
||||||
|
|
@ -3571,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 Конструктор копирования строки.
|
||||||
|
|
@ -3612,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) {
|
||||||
// Просто присвоим его себе.
|
// Просто присвоим его себе.
|
||||||
|
|
@ -3622,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
|
||||||
|
|
@ -3765,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;
|
||||||
}
|
}
|
||||||
|
|
@ -3832,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{};
|
||||||
|
|
@ -4345,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;
|
||||||
|
|
@ -4358,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();
|
||||||
|
|
@ -5068,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 - поток вывода.
|
||||||
|
|
@ -5250,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()};
|
||||||
}
|
}
|
||||||
|
|
@ -5265,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()};
|
||||||
}
|
}
|
||||||
|
|
@ -5280,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()};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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 - количество символов от начала строки. При превышении размера строки вернёт пустую строку.
|
||||||
|
|
@ -4597,7 +4694,7 @@ public:
|
||||||
template<typename R = str_piece>
|
template<typename R = str_piece>
|
||||||
constexpr std::optional<R> strip_prefix(str_piece prefix) const {
|
constexpr std::optional<R> strip_prefix(str_piece prefix) const {
|
||||||
if (starts_with(prefix)) {
|
if (starts_with(prefix)) {
|
||||||
return R{operator()(prefix.length())};
|
return R{get(prefix.length(), to_end)};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -4616,7 +4713,7 @@ public:
|
||||||
template<typename R = str_piece>
|
template<typename R = str_piece>
|
||||||
constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
|
constexpr std::optional<R> strip_prefix_ia(str_piece prefix) const {
|
||||||
if (starts_with_ia(prefix)) {
|
if (starts_with_ia(prefix)) {
|
||||||
return R{operator()(prefix.length())};
|
return R{get(prefix.length(), to_end)};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -4635,7 +4732,7 @@ public:
|
||||||
template<typename R = str_piece>
|
template<typename R = str_piece>
|
||||||
constexpr std::optional<R> strip_suffix(str_piece suffix) const {
|
constexpr std::optional<R> strip_suffix(str_piece suffix) const {
|
||||||
if (ends_with(suffix)) {
|
if (ends_with(suffix)) {
|
||||||
return R{operator()(0, -suffix.length())};
|
return R{get(0, from_end{suffix.length()})};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -4654,7 +4751,7 @@ public:
|
||||||
template<typename R = str_piece>
|
template<typename R = str_piece>
|
||||||
constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
|
constexpr std::optional<R> strip_suffix_ia(str_piece suffix) const {
|
||||||
if (ends_with_ia(suffix)) {
|
if (ends_with_ia(suffix)) {
|
||||||
return R{operator()(0, -suffix.length())};
|
return R{get(0, from_end{suffix.length()})};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -4939,7 +5036,7 @@ public:
|
||||||
constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
|
constexpr R trimmed_prefix(str_piece prefix, size_t max_count = 0) const {
|
||||||
str_piece res = *this;
|
str_piece res = *this;
|
||||||
while(res.starts_with(prefix)) {
|
while(res.starts_with(prefix)) {
|
||||||
res = res(prefix.length());
|
res.remove_prefix(prefix.length());
|
||||||
if (--max_count == 0) {
|
if (--max_count == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -4962,7 +5059,7 @@ public:
|
||||||
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
|
constexpr R trimmed_prefix_ia(str_piece prefix, size_t max_count = 0) const {
|
||||||
str_piece res = *this;
|
str_piece res = *this;
|
||||||
while(res.starts_with_ia(prefix)) {
|
while(res.starts_with_ia(prefix)) {
|
||||||
res = res(prefix.length());
|
res.remove_prefix(prefix.length());
|
||||||
if (--max_count == 0) {
|
if (--max_count == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -4985,7 +5082,7 @@ public:
|
||||||
constexpr R trimmed_suffix(str_piece suffix) const {
|
constexpr R trimmed_suffix(str_piece suffix) const {
|
||||||
str_piece res = *this;
|
str_piece res = *this;
|
||||||
while(res.ends_with(suffix)) {
|
while(res.ends_with(suffix)) {
|
||||||
res = res(0, -suffix.length());
|
res.remove_suffix(suffix.length());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -5005,7 +5102,7 @@ public:
|
||||||
constexpr R trimmed_suffix_ia(str_piece suffix) const {
|
constexpr R trimmed_suffix_ia(str_piece suffix) const {
|
||||||
str_piece res = *this;
|
str_piece res = *this;
|
||||||
while(res.ends_with_ia(suffix)) {
|
while(res.ends_with_ia(suffix)) {
|
||||||
res = res(0, -suffix.length());
|
res.remove_suffix(suffix.length());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
||||||
|
|
||||||
Version 1.7.3.
|
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>
|
||||||
|
|
||||||
|
|
@ -325,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.3 # Specify the desired release
|
GIT_TAG tags/rel1.9.1# Specify the desired release
|
||||||
FIND_PACKAGE_ARGS NAMES simstr 1.7.3
|
FIND_PACKAGE_ARGS NAMES simstr 1.9.1
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(simstr)
|
FetchContent_MakeAvailable(simstr)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
||||||
|
|
||||||
Версия 1.7.3.
|
Версия 1.9.1
|
||||||
|
|
||||||
<h2>Ускорь работу со строками в 2-10 раз!</h2>
|
<h2>Ускорь работу со строками в 2-10 раз!</h2>
|
||||||
|
|
||||||
|
|
@ -326,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.3 # Укажите нужный релиз
|
GIT_TAG tags/rel1.9.1# Укажите нужный релиз
|
||||||
FIND_PACKAGE_ARGS NAMES simstr 1.7.3
|
FIND_PACKAGE_ARGS NAMES simstr 1.9.1
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(simstr)
|
FetchContent_MakeAvailable(simstr)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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
|
||||||
|
|
@ -452,4 +452,22 @@ TEST(StrExpr, StartWithAnd) {
|
||||||
EXPECT_TRUE("Begin[ --end"_ss.starts_with_ia_and_oneof("begin", "/*[]"_ss));
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* ver. 1.7.3
|
* 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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue