- Update to 1.6.5.

- Benchmark build for Emscripten output text with no delay.
- Add ""_f16 flag for use e_hex as `num / 1_f16`.
This commit is contained in:
Aleksandr Orefkov 2026-02-08 14:17:03 +03:00
parent 4e1d1b5ffa
commit 2148718b32
22 changed files with 5534 additions and 5186 deletions

View File

@ -5,7 +5,7 @@ include(FetchContent)
project(
simstr
VERSION 1.6.4
VERSION 1.6.5
DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX

View File

@ -7,12 +7,21 @@ add_executable(benchStr bench_str.cpp bench.h)
target_link_libraries(benchStr simstr::simstr benchmark::benchmark)
target_compile_features(benchStr PUBLIC cxx_std_23)
target_compile_definitions(benchStr PRIVATE SIMSTR_VERSION="${PROJECT_VERSION}")
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG tags/12.1.0
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(benchStr fmt::fmt)
add_executable(process_result process_result.cpp)
target_link_libraries(process_result simstr_simstr)
if (EMSCRIPTEN)
#target_link_options(benchStr PRIVATE -sSTACK_SIZE=1048576 -sINITIAL_MEMORY=128MB -sALLOW_MEMORY_GROWTH=0)
set_target_properties (benchStr PROPERTIES SUFFIX .html)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sASYNCIFY --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/emscripten/shell_minimal.html")
if(SIMSTR_EMSCRIPTEN_MT)
target_link_options(benchStr PUBLIC -pthread -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency-1 -sENVIRONMENT=web,worker)
endif()

View File

@ -1,6 +1,9 @@
/*
* ver. 1.6.5
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Бенчмарки
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
* Benchmarks
*/
#include "bench.h"
@ -8,6 +11,28 @@
#include <string>
#include <charconv>
#include <iomanip>
#include <fmt/format.h>
#include <fmt/compile.h>
#ifdef EMSCRIPTEN
#include <emscripten.h>
static void DoTeardown(const benchmark::State& state) {
emscripten_sleep(1);
}
static void DoSetup(const benchmark::State& state) {
EM_ASM({
on_begin_benchmark(UTF8ToString($0));
}, state.name().c_str());
emscripten_sleep(1);
}
#undef BENCHMARK
#define BENCHMARK(...) \
BENCHMARK_PRIVATE_DECLARE(_benchmark_) = \
(::benchmark::internal::RegisterBenchmarkInternal( \
::benchmark::internal::make_unique< \
::benchmark::internal::FunctionBenchmark>(#__VA_ARGS__, \
__VA_ARGS__)))->Setup(DoSetup)->Teardown(DoTeardown)
#endif
using namespace simstr;
using namespace std::literals;
@ -110,7 +135,6 @@ void ConcatStdToCharsHex(benchmark::State& state) {
}
}
void ConcatSimToStdHex(benchmark::State& state) {
// We use a short string so that the longest result is 15 characters and fits in the std::string SSO buffer.
std::string s1 = "art ";
@ -118,7 +142,7 @@ void ConcatSimToStdHex(benchmark::State& state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
benchmark::DoNotOptimize(s1);
// Can work for all types of symbols
std::string str = +s1 + e_hex<HexFlags::Short>(i) + " end";
std::string str = +s1 + i / 1_f16 + " end";
benchmark::DoNotOptimize(str);
}
}
@ -131,7 +155,7 @@ void ConcatSimToSimHex(benchmark::State& state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
benchmark::DoNotOptimize(s1);
// Can work for all types of symbols
stringa str = s1 + e_hex<HexFlags::Short>(i) + " end";
stringa str = s1 + i / 1_f16 + " end";
benchmark::DoNotOptimize(str);
}
}
@ -144,7 +168,7 @@ void ConcatSimToSimHexC(benchmark::State& state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
benchmark::DoNotOptimize(s1);
// Can work for all types of symbols
stringa str = e_concat("", s1, e_hex<HexFlags::Short>(i), " end");
stringa str = e_concat("", s1, i / 1_f16, " end");
benchmark::DoNotOptimize(str);
}
}
@ -156,7 +180,7 @@ void ConcatSimToSimHexS(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
benchmark::DoNotOptimize(s1);
stringa str = e_subst("{}{} end", s1, e_hex<HexFlags::Short>(i));
stringa str = e_subst("{}{} end", s1, i / 1_f16);
benchmark::DoNotOptimize(str);
}
}
@ -168,7 +192,7 @@ void ConcatSimToSimHexVS(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
benchmark::DoNotOptimize(s1);
stringa str = e_vsubst("{}{} end"_ss, s1, e_hex<HexFlags::Short>(i));
stringa str = e_vsubst("{}{} end"_ss, s1, i / 1_f16);
benchmark::DoNotOptimize(str);
}
}
@ -187,7 +211,7 @@ BENCHMARK(ConcatSimToSimHexVS) ->Name("Subst stringa and hex number by e_vsubst
void ConcatSimToSimOct(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
stringa str = "art "_ss + i / 0x8a010_fmt + " end";
stringa str = "abcdefghihklmopqr "_ss + i / 0x8a010_fmt + " end";
benchmark::DoNotOptimize(str);
}
}
@ -196,23 +220,14 @@ void ConcatSimToSimOct(benchmark::State& state) {
void SubstSimToSimOct(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
stringa str = e_subst("art {} end", i / 0x8a010_fmt);
benchmark::DoNotOptimize(str);
}
}
}
void FormatStdToStdOct(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::string str = std::format("art {:#010o} end", i);
stringa str = e_subst("abcdefghihklmopqr {} end", i / 0x8a010_fmt);
benchmark::DoNotOptimize(str);
}
}
}
void VSubstSimToSimOct(benchmark::State& state) {
ssa pattern = "art {} end";
ssa pattern = "abcdefghihklmopqr {} end";
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
stringa str = e_vsubst(pattern, i / 0x8a010_fmt);
@ -221,8 +236,17 @@ void VSubstSimToSimOct(benchmark::State& state) {
}
}
void FormatStdToStdOct(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::string str = std::format("abcdefghihklmopqr {:#010o} end", i);
benchmark::DoNotOptimize(str);
}
}
}
void VFormatStdToStdOct(benchmark::State& state) {
std::string_view pattern = "art {:#010o} end";
std::string_view pattern = "abcdefghihklmopqr {:#010o} end";
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::string str = std::vformat(pattern, std::make_format_args(i));
@ -235,20 +259,41 @@ void StreamStdToStdOct(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::stringstream strm;
strm << "art 0" << std::oct << std::setw(9) << std::setfill('0') << i << " end";
strm << "abcdefghihklmopqr 0" << std::oct << std::setw(9) << std::setfill('0') << i << " end";
std::string str = strm.str();
benchmark::DoNotOptimize(str);
}
}
}
BENCHMARK(__)->Name("----- format/vformat and subst/vsubst octal number ---------")->Repetitions(1);
BENCHMARK(ConcatSimToSimOct) ->Name("\"art \"_ss + i / 0x8a010_fmt + \" end\"");
BENCHMARK(SubstSimToSimOct) ->Name("e_subst(\"art {} end\", i / 0x8a010_fmt)");
void FmtFormatComp(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::string str = fmt::format(FMT_COMPILE("abcdefghihklmopqr {:#010o} end"), i);
benchmark::DoNotOptimize(str);
}
}
}
void FmtFormat(benchmark::State& state) {
for (auto _: state) {
for (unsigned i = 1; i <= 100'000; i *= 10) {
std::string str = fmt::format("abcdefghihklmopqr {:#010o} end", i);
benchmark::DoNotOptimize(str);
}
}
}
BENCHMARK(__)->Name("---- format/vformat and subst/vsubst octal number to 32 symbols result ----")->Repetitions(1);
BENCHMARK(ConcatSimToSimOct) ->Name("\"abcdefghihklmopqr \"_ss + i / 0x8a010_fmt + \" end\"");
BENCHMARK(SubstSimToSimOct) ->Name("e_subst(\"abcdefghihklmopqr {} end\", i / 0x8a010_fmt)");
BENCHMARK(VSubstSimToSimOct) ->Name("e_vsubst(pattern, i / 0x8a010_fmt)");
BENCHMARK(FormatStdToStdOct) ->Name("std::format(\"art {:#010o} end\", i)");
BENCHMARK(FmtFormatComp) ->Name("fmt::format(FMT_COMPILE(\"abcdefghihklmopqr {:#010o} end\"), i)");
BENCHMARK(FmtFormat) ->Name("fmt::format(\"abcdefghihklmopqr {:#010o} end\", i)");
BENCHMARK(FormatStdToStdOct) ->Name("std::format(\"abcdefghihklmopqr {:#010o} end\", i)");
BENCHMARK(VFormatStdToStdOct) ->Name("std::vformat(pattern, std::make_format_args(i))");
BENCHMARK(StreamStdToStdOct) ->Name("strm << \"art 0\" << std::oct << std::setw(9) << std::setfill('0') << i << \" end\"");
BENCHMARK(StreamStdToStdOct) ->Name("strm << \"abcdefghihklmopqr 0\" << std::oct << std::setw(9) << std::setfill('0') << i << \" end\"");
void ConcatStdToStdS(benchmark::State& state) {
std::string s1 = "start ";
@ -2413,6 +2458,11 @@ int main(int argc, char** argv) {
std::cout << "Benchmarks of simstr, version " SIMSTR_VERSION << "\n"
<< "Sources: https://github.com/orefkov/simstr\n"
<< "Results: https://orefkov.github.io/simstr/results.html\n" << std::endl;
#ifdef EMSCRIPTEN
EM_ASM({ console.log(navigator.userAgent); });
#endif
char arg1[] = "--benchmark_repetitions=4", arg2[] = "--benchmark_report_aggregates_only=true";
char* my_params[] = {argv[0], arg1, arg2};
if (argc < 2) {
@ -2424,5 +2474,8 @@ int main(int argc, char** argv) {
return 1;
::benchmark::RunSpecifiedBenchmarks();
::benchmark::Shutdown();
#ifdef EMSCRIPTEN
EM_ASM({ on_done(); });
#endif
return 0;
}

View File

@ -0,0 +1,96 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>SimStr Bencjmarks</title>
<style>
body {
color: white;
background-color: black;
}
#output {
position: absolute;
left: 10px;
top: 80px;
right: 10px;
bottom: 10px;
margin: 0 auto;
border: 0;
display: block;
background-color: black;
color: white;
font-family: 'Cascadia Code', 'Consolas', 'Lucida Console', Monaco, monospace;
outline: none;
}
.ctr {
text-align: center;
font-size: 16pt;
}
</style>
</head>
<body>
<div class="ctr" id="bs_name"></div><div class="ctr" id="b_name"></div>
<textarea class="emscripten" id="output" rows="20"></textarea>
<script type='text/javascript'>
var outputElement = document.getElementById('output');
var bsNameEl= document.getElementById('bs_name');
var bNameEl= document.getElementById('b_name');
if (outputElement) outputElement.value = ''; // clear browser cache
var Module = {
print(...args) {
console.log(...args);
if (outputElement) {
var text = args.join(' ').replace('_mean', ' ');
var rpt = text.indexOf("/repeats");
if (rpt != -1)
text = text.substr(0, rpt);
if (text.indexOf('_median ') == -1 && text.indexOf('_stddev ') == -1 && text.indexOf('_cv ') == -1) {
outputElement.value += text + "\n";
outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom
}
}
},
setStatus(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m) {
text = m[1];
} else {
}
},
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
},
};
Module.setStatus('Downloading...');
window.onerror = () => {
Module.setStatus('Exception thrown, see JavaScript console');
Module.setStatus = (text) => {
if (text) console.error('[post-exception status] ' + text);
};
};
function on_begin_benchmark(name) {
if (name.substr(0, 3) == "---") {
bsNameEl.innerText = name.match(/^[- ]+(.+?)[ -]+$/)[1];
bNameEl.innerText = "";
} else {
bNameEl.innerText = name;
}
}
function on_done() {
bsNameEl.innerText = "Done";
bNameEl.innerText = "";
}
</script>
{{{ SCRIPT }}}
</body>
</html>

View File

@ -189,9 +189,9 @@ ssa extract_comment(ssa commentsText, ssa benchmarkName) {
return stra::empty_str;
}
void write_one_result(out_t& out, ssa result, ssa line, auto& script_text, bool last) {
void write_one_result(out_t& out, ssa result, ssa line, auto& script_text, char delim) {
out += "<td class=\"benchmarkresult\">" + result + "</td>";
script_text += e_choice(result[0] == 'N', "NaN", result) + e_choice(last, "]", ",");
script_text += e_choice(result[0] == 'N', "NaN", result) + delim;
}
std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceText) {
@ -274,6 +274,12 @@ std::pair<ssa, size_t> extract_source_for_benchmark(ssa benchName, ssa sourceTex
}
void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText, ssa commentsText) {
ssa releaseVersion = sourceText(sourceText.find_end("\n * ver. ")).until("\n").trimmed();
if (!releaseVersion) {
std::cerr << "Not found release version in sources" << std::endl;
throw std::runtime_error{"Not found release version in sources"};
}
std::vector<Splitter<u8s>> splitters;
splitters.reserve(results.size());
@ -291,14 +297,14 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
auto comment = extract_comment(commentsText, benchName);
// Нужно вывести название бенча и коммент
// Need to display title and comment
out += "\n<tr><td class=\"benchmarkname\"><span class=\"tooltip\"><a target=\"blank\" href=\"https://github.com/orefkov/simstr/blob/main/bench/bench_str.cpp#L"_ss +
line_num + "\">" + repl_html_symbols(benchName) +
out += "\n<tr><td class=\"benchmarkname\"><span class=\"tooltip\"><a target=\"blank\" href=\"https://github.com/orefkov/simstr/blob/rel"
+ releaseVersion + "/bench/bench_str.cpp#L" + line_num + "\">" + repl_html_symbols(benchName) +
"</a><span class=\"tooltiptext code\">" +
source + "</span></span></td><td>" +
e_if(!comment.is_empty(), "<span class=\"tooltip info\">&nbsp;>>&nbsp;<span class=\"tooltiptext\">" + comment + "</span></span>") +
"</td>";
script_text += e_if(needCommaForTests, "},") + "\n{name:'" + e_repl(benchName.to_str(), "'", "\\'") + "',data:[";
write_one_result(out, result, line, script_text, result.size() == 1);
script_text += e_if(needCommaForTests, "},") + "\n{name:'" + e_repl(benchName, "'", "\\'") + "',data:[";
write_one_result(out, result, line, script_text, result.size() == 1 ? ']' : ',');
for (unsigned idx = 1; idx < results.size(); idx++) {
if (splitters[idx].is_done()) {
@ -318,7 +324,7 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
result = stra::empty_str;
}
}
write_one_result(out, result, line, script_text, idx == results.size() - 1);
write_one_result(out, result, line, script_text, idx == results.size() - 1 ? ']' : ',');
}
out += "</tr>";
needCommaForTests = true;
@ -336,7 +342,7 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
write_benchset_header(out, results, benchName, ++benchSetId);
needFooter = true;
needCommaForTests = false;
script_text = "bench_sets['" + e_repl(benchName.to_str(), "'", "\\'") + "'] = {id:'bs" + benchSetId +"', tests:[";
script_text = "bench_sets['" + e_repl(benchName, "'", "\\'") + "'] = {id:'bs" + benchSetId +"', tests:[";
}
// Эти строки надо пропустить во всех файлах
// These lines must be skipped in all files
@ -346,9 +352,6 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
throw std::runtime_error{"Not expected end of file"};
}
line = splitters[idx].next();
if (line.starts_with("std::unordered_map<std::string, size_t> emplace & find std::string_view;_cv")) {
int t = 1;
}
}
}
if (needFooter) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.6.4
PROJECT_NUMBER = 1.6.5
# 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

View File

@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.6.4
PROJECT_NUMBER = 1.6.5
# 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

View File

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

View File

@ -1,9 +1,8 @@
/*
* ver. 1.6.5
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* ver. 1.6.4
* Классы для работы со строками
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
* ver. 1.6.4
* Classes for working with strings
*/

View File

@ -1,5 +1,5 @@
/*
* ver. 1.6.4
* ver. 1.6.5
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* База для строковых конкатенаций через выражения времени компиляции
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -2247,7 +2247,7 @@ concept good_int_flags =
* - f::с при задании ширины поля выравнивать по центру.
* - f::p выводить префикс системы счисления, 0b для 2, 0 для 8, 0x для 16.
* - f::P выводить префикс системы счисления, 0B для 2, 0 для 8, 0X для 16.
* - f::z дополнять число до заданной ширины нулями справа. Не совместимо с опциями выравнивания.
* - f::z дополнять число до заданной ширины нулями слева. Не совместимо с опциями выравнивания.
* - f::u Для систем счисления более 10, выводить символы в верхнем регистре.
* - f::sp Для знаковых типов чисел для положительных значений выводить '+' перед числом.
* - f::ss Для знаковых типов чисел для положительных значений выводить пробел перед числом.
@ -2269,8 +2269,8 @@ concept good_int_flags =
* - f::с when setting the field width, align to the center.
* - f::p print number system prefix, 0b for 2, 0 for 8, 0x for 16.
* - f::P print number system prefix, 0B for 2, 0 for 8, 0X for 16.
* - f::z pad the number to the specified width with zeros on the right. Not compatible with alignment options.
* - f::u For number systems greater than 10, output characters in uppercase.
* - f::z pad the number to the specified width with zeros on the let. Not compatible with alignment options.
* - f::u For radix greater than 10, output characters in uppercase.
* - f::sp For signed number types, print '+' before the number for positive values.
* - f::ss For signed number types, print a space before the number for positive values.
* - f::f<'c'> Specifies a placeholder character when specifying the field width. Default is space.
@ -2293,7 +2293,7 @@ concept good_int_flags =
* - с при задании ширины поля выравнивать по центру, аналог f::c.
* - a выводить префикс системы счисления, 0b для 2, 0 для 8, 0x для 16, аналог f::p.
* - A выводить префикс системы счисления, 0B для 2, 0 для 8, 0X для 16, аналог f::P.
* - 0 дополнять число до заданной ширины нулями справа. Не совместимо с опциями выравнивания, аналог f::z.
* - 0 дополнять число до заданной ширины нулями слева. Не совместимо с опциями выравнивания, аналог f::z.
* - E Для систем счисления более 10, выводить символы в верхнем регистре, аналог f::u.
* - e Для знаковых типов чисел для положительных значений выводить '+' перед числом, аналог f::sp.
* - f Для знаковых типов чисел для положительных значений выводить пробел перед числом, аналог f::ss.
@ -2303,6 +2303,7 @@ concept good_int_flags =
* либо дополнение нулями (0), но не оба сразу. Если ничего из этого не указано, применяется выравнивание вправо.
* Число дополняется до заданной ширины, если оно короче. Если длиннее, то выводится всё число.
* - ' разделитель, пропускается.
*
* Так как после `F` все символы воспринимаются как hex код символа разделителя, при необходимости его использования
* лучше ставить его в конце литерала форматирования, или отделять '.
* <br/>
@ -2314,8 +2315,8 @@ concept good_int_flags =
* - c when setting the field width, align to the center, analogous to f::c.
* - a display the number system prefix, 0b for 2, 0 for 8, 0x for 16, analogous to f::p.
* - A display the number system prefix, 0B for 2, 0 for 8, 0X for 16, analogous to f::P.
* - 0 pad the number to the specified width with zeros on the right. Not compatible with alignment options, similar to f::z.
* - E For number systems greater than 10, display characters in uppercase, analogous to f::u.
* - 0 pad the number to the specified width with zeros on the left. Not compatible with alignment options, similar to f::z.
* - E For radix greater than 10, display characters in uppercase, analogous to f::u.
* - e For signed number types, for positive values, print '+' before the number, analogous to f::sp.
* - f For signed number types, for positive values, print a space before the number, analogous to f::ss.
* - FCodeInhex Sets the code of the filler character when specifying the field width, analogous to f::f<'c'>.
@ -2324,6 +2325,7 @@ concept good_int_flags =
* or zero padding (0), but not both. If none of these are specified, right alignment is applied.
* The number is padded to the given width if it is shorter. If it is longer, then the entire number is displayed.
* - ' delimiter, skipped.
*
* Since after `F` all characters are perceived as hex code of the delimiter character, if necessary, use it
* it is better to put it at the end of format literal, or separate with '.
* <br/>
@ -2491,19 +2493,52 @@ enum HexFlags : unsigned {
/*!
* @ingroup StrExprs
* @ru @brief Позволяет конкатенировать текст и беззнаковое число в 16-ричном виде.
* @ru @brief Создает объект, который может преобразовываться в строковое выражение, генерирующее 16ричное представление числа.
* @tparam Flags - флаги форматирования, побитовое ИЛИ из HexFlags.
* @tparam T - тип числа, выводится автоматически.
* @en @brief Allows you to concatenate text and a unsigned number in hexadecimal.
* @param v - число.
* @details Это более быстрое преобразование в строку в 16ричном виде, чем через `e_int`, однако с меньшими возможностями
* форматирования. По умолчанию создается представление в виде `0x000012AB`, то есть префикс `0x` и дополнения нулями до ширины
* по размеру типа числа (2 символа на байт). Можно указать флаги:
* - HexFlags::Short - не дополнять нулями до фиксированной ширины.
* - HexFlags::Lcase - выводить символы в нижнем регистре.
* - HexFlags::No0x - не выводить префикс.
*
* Кроме того, возможна краткая запись этого выражения в виде `num / N_f16`
* N обозначает флаги форматирования
* - 1 - HexFlags::Short
* - 2 - HexFlags::Lcase
* - 3 - HexFlags::No0x
*
* @en @brief Creates an object that can be converted to a string expression that generates a hexadecimal representation of a number.
* @tparam Flags - format flags, bitwise OR of HexFlags.
* @tparam T - number type, deducted automatically.
* @details @ru Пример @en Example @~
* @param v - number.
* @details This is a faster conversion to a hexadecimal string than `e_int`, but with less power formatting.
* By default, a representation of the form `0x000012AB` is created, that is, prefixed with `0x` and padded with zeros to the width
* by number type size (2 characters per byte). You can specify flags:
* - HexFlags::Short - do not pad with zeros to a fixed width.
* - HexFlags::Lcase - display characters in lowercase.
* - HexFlags::No0x - do not display the prefix.
*
* In addition, this expression can be written briefly as `num / N_f16`
* N stands for formatting flags
* - 1 - HexFlags::Short
* - 2 - HexFlags::Lcase
* - 3 - HexFlags::No0x
*
* @ru Пример @en Example @~
* ```cpp
* stringa text = +"val = "sv + e_hex(10u);
* stringa text = +"val = "sv + e_hex(10);
* EXPECT_EQ(text, "val = 0x0000000A");
*
* stringu textu = +u"val = 0X"sv + e_hex<HexFlags::No0x | HexFlags::Short | HexFlags::Lcase>(0x12Au);
* stringu textu = +u"val = 0X"sv + e_hex<HexFlags::No0x | HexFlags::Short | HexFlags::Lcase>(0x12A);
* EXPECT_EQ(textu, u"val = 0X12a");
*
* text = "Num in hex: " + num / 0_f16; // same as e_hex(num);
* text = "Num in hex: " + num / 13_f16; // same as e_hex<HexFlags::Short | HexFlags::No0x>(num);
* text = "Num in hex: " + num / 123_f16; // same as e_hex<HexFlags::Short | HexFlags::No0x | HexFlags::Lcase>(num);
* text = "Num in hex: " + num / 2_f16; // same as e_hex<HexFlags::No0x | HexFlags::Lcase>(num);
* ```
*/
template<unsigned Flags = 0, FromIntNumber T>
@ -2511,6 +2546,34 @@ constexpr auto e_hex(T v) {
return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
}
template<char c = 0, char...Chars>
constexpr unsigned parse_f16_flags() {
if constexpr (c == '1') {
return HexFlags::Short | parse_f16_flags<Chars...>();
} else if constexpr (c == '2') {
return HexFlags::Lcase | parse_f16_flags<Chars...>();
} else if constexpr (c == '3') {
return HexFlags::No0x | parse_f16_flags<Chars...>();
} else {
return 0;
}
}
template<unsigned N>
struct f16flags{};
template<FromIntNumber T, unsigned Flags>
constexpr auto operator/(T v, const f16flags<Flags>&) {
return expr_hex_src<T, (Flags & HexFlags::Short) == 0, (Flags & HexFlags::Lcase) == 0, (Flags & HexFlags::No0x) == 0>{v};
}
inline namespace literals {
template<char...Chars>
constexpr auto operator""_f16() {
return f16flags<parse_f16_flags<Chars...>()>{};
}
} // namespace literals
/*!
* @ru @brief Специализация шаблона для преобразования e_hex в строковое выражение, позволяет использовать их в
* операциях конкатенации со строковыми выражениями.
@ -3387,6 +3450,19 @@ public:
constexpr str_piece from_to(size_t from, size_t to) const noexcept {
return str_piece{_str() + from, to - from};
}
/*!
* @ru @brief Получить подстроку str_src от начала и до первого найденного вхождения указанной подстроки.
* @details Если не найдено, вернёт всю строку.
* @param offset - позиция для начала поиска.
* @return Подстроку, str_src.
* @en @brief Get the substring str_src from the beginning to the first found occurrence of the specified substring.
* @details If not found, returns the entire string.
* @param offset - position to start the search.
* @return Substring, str_src.
*/
constexpr str_piece until(str_piece pattern, size_t offset = 0) const noexcept {
return (*this)(0, find_or_all(pattern, offset));
}
/*!
* @ru @brief Проверка на пустоту.
* @en @brief Check for emptiness.
@ -4933,7 +5009,7 @@ inline namespace literals {
* - c при задании ширины поля выравнивать по центру, аналог f::c.
* - a выводить префикс системы счисления, 0b для 2, 0 для 8, 0x для 16, аналог f::p.
* - A выводить префикс системы счисления, 0B для 2, 0 для 8, 0X для 16, аналог f::P.
* - 0 дополнять число до заданной ширины нулями справа. Не совместимо с опциями выравнивания, аналог f::z.
* - 0 дополнять число до заданной ширины нулями слева. Не совместимо с опциями выравнивания, аналог f::z.
* - E Для систем счисления более 10, выводить символы в верхнем регистре, аналог f::u.
* - e Для знаковых типов чисел для положительных значений выводить '+' перед числом, аналог f::sp.
* - f Для знаковых типов чисел для положительных значений выводить пробел перед числом, аналог f::ss.
@ -4943,6 +5019,7 @@ inline namespace literals {
* либо дополнение нулями (0), но не оба сразу. Если ничего из этого не указано, применяется выравнивание вправо.
* Число дополняется до заданной ширины, если оно короче. Если длиннее, то выводится всё число.
* - ' разделитель, пропускается.
*
* Так как после `F` все символы воспринимаются как hex код символа разделителя, при необходимости его использования
* лучше ставить его в конце литерала форматирования, или отделять '.
* <br/>
@ -4955,8 +5032,8 @@ inline namespace literals {
* - c when setting the field width, align to the center, analogous to f::c.
* - a display the number system prefix, 0b for 2, 0 for 8, 0x for 16, analogous to f::p.
* - A display the number system prefix, 0B for 2, 0 for 8, 0X for 16, analogous to f::P.
* - 0 pad the number to the specified width with zeros on the right. Not compatible with alignment options, similar to f::z.
* - E For number systems greater than 10, display characters in uppercase, analogous to f::u.
* - 0 pad the number to the specified width with zeros on the left. Not compatible with alignment options, similar to f::z.
* - E For radix greater than 10, display characters in uppercase, analogous to f::u.
* - e For signed number types, for positive values, print '+' before the number, analogous to f::sp.
* - f For signed number types, for positive values, print a space before the number, analogous to f::ss.
* - FCodeInhex Sets the code of the filler character when specifying the field width, analogous to f::f<'c'>.
@ -4965,6 +5042,7 @@ inline namespace literals {
* or zero padding (0), but not both. If none of these are specified, right alignment is applied.
* The number is padded to the given width if it is shorter. If it is longer, then the entire number is displayed.
* - ' delimiter, skipped.
*
* Since after `F` all characters are perceived as hex code of the delimiter character, if necessary, use it
* it is better to put it at the end of format literal, or separate with '.
* <br/>
@ -6717,6 +6795,57 @@ std::basic_string<K, std::char_traits<K>, A>& insert(std::basic_string<K, std::c
return change(str, from, 0, expr);
}
/*!
* @ru @brief Переписать всю строку заданным строковым выражением, при необходимости изменив размер строки.
* @tparam K - тип символов.
* @tparam A - тип аллокатора.
* @tparam E - тип строкового выражения.
* @param str - строка.
* @param expr - строковое выражение для вставки.
* @return std::basic_string<K, std::char_traits<K>, A>& - ссылку на переданную строку.
* @details Метод заменяет содержимое строки результатом строкового выражения.
*
* ВАЖНО!!! части строкового выражения не должны ссылаться на саму строку, иначе результат неопределён!!!
*
* @en @brief Rewrite the entire string with the given string expression, resizing the string if necessary.
* @tparam K - character type.
* @tparam A - allocator type.
* @tparam E - string expression type.
* @param str - string.
* @param expr - string expression to insert.
* @return std::basic_string<K, std::char_traits<K>, A>& - a reference to the passed string.
* @details The method replaces the contents of a string with the result of a string expression.
*
* IMPORTANT!!! Parts of a string expression must not reference the string itself, otherwise the result is undefined!!!
*
*/
template<typename K, typename A, StrExprForType<K> E>
std::basic_string<K, std::char_traits<K>, A>& overwrite(std::basic_string<K, std::char_traits<K>, A>& str, const E& expr) {
if (size_t expr_length = expr.length()) {
if (expr_length <= str.length()) {
K* data = str.data();
expr.place((typename E::symb_type*)data);
str.resize(expr_length);
} else {
auto fill = [&](K* data, size_t) -> size_t {
expr.place((typename E::symb_type*)data);
return expr_length;
};
if constexpr (requires{str.resize_and_overwrite(expr_length, fill);}) {
str.resize_and_overwrite(expr_length, fill);
} else if constexpr (requires{str._Resize_and_overwrite(expr_length, fill);}) {
str._Resize_and_overwrite(expr_length, fill);
} else {
str.resize(expr_length);
expr.place((typename E::symb_type*)str.data());
}
}
} else {
str.clear();
}
return str;
}
namespace details {
template<typename K, typename A, typename E>

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Version 1.6.4.
Version 1.6.5.
<h2>Speed up your work with strings by 2-10 times!</h2>
@ -108,8 +108,6 @@ the sizes of SSO buffers in objects are smaller.
On the [release page](https://github.com/orefkov/simstr/releases) you can download binary builds of benchmarks and run them on your equipment.
You can also run the [Emscripten build of benchmarks](https://orefkov.github.io/simstr/bench/benchStr.html) directly in the browser.
(Before following the link, it is better to open "Developer Tools" (usually **F12**) in advance to see the console
Javascript, since the page will not be updated until the benchmarks are completed, and all output will be visible in the console).
- [Benchmark source code](bench/bench_str.cpp)
- [Benchmark results](https://orefkov.github.io/simstr/results.html)
@ -324,8 +322,8 @@ function(add_simstr)
simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE
GIT_TAG tags/rel1.6.4 # Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.6.4
GIT_TAG tags/rel1.6.5 # Specify the desired release
FIND_PACKAGE_ARGS NAMES simstr 1.6.5
)
FetchContent_MakeAvailable(simstr)
endfunction()

View File

@ -3,7 +3,7 @@
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Версия 1.6.4.
Версия 1.6.5.
<h2>Ускорь работу со строками в 2-10 раз!</h2>
@ -109,8 +109,6 @@ Windows и Linux (в WSL), с использованием компилятор
На [странице релизов](https://github.com/orefkov/simstr/releases) вы можете скачать бинарные сборки бенчмарков и запустить их на своём оборудовании.
Также вы можете запустить [Emscripten сборку бенчмарков](https://orefkov.github.io/simstr/bench/benchStr.html) прямо в браузере.
(Перед переходом по ссылке лучше предварительно откройте "Инструменты разработчика" (обычно **F12**), чтобы видеть консоль
Javascript, так как до окончания бенчмарков страница не будет обновляться, а весь вывод будет виден в консоли).
- [Исходный код бенчмарков](bench/bench_str.cpp)
- [Результаты бенчмарков](https://orefkov.github.io/simstr/results.html)
@ -325,8 +323,8 @@ function(add_simstr)
simstr
GIT_REPOSITORY https://github.com/orefkov/simstr.git
GIT_SHALLOW TRUE
GIT_TAG tags/rel1.6.4 # Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.6.4
GIT_TAG tags/rel1.6.5 # Укажите нужный релиз
FIND_PACKAGE_ARGS NAMES simstr 1.6.5
)
FetchContent_MakeAvailable(simstr)
endfunction()

View File

@ -1,5 +1,5 @@
/*
* ver. 1.6.4
* ver. 1.6.5
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Реализация строковых функций
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com

View File

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

View File

@ -1,5 +1,5 @@
/*
* ver. 1.6.4
* ver. 1.6.5
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* Тесты simstr
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
@ -2004,6 +2004,11 @@ TEST(SimStr, EIntFmt) {
EXPECT_EQ(test, "'0x0000000A' _______A");
test = 100 / 0x2a010_fmt;
EXPECT_EQ(test, "0b01100100");
std::u16string textu = +u"val = 0X"sv + 0x12A / 123_f16;
EXPECT_EQ(textu, u"val = 0X12a");
textu = u"val = "_ss + 0x12A / 0_f16;
EXPECT_EQ(textu, u"val = 0x0000012A");
}
#ifndef _MSC_VER

View File

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