aboutsummaryrefslogtreecommitdiffstats
path: root/test/test_stringfmt01.cpp
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-09-02 04:15:31 +0200
committerSven Göthel <[email protected]>2024-09-02 13:37:49 +0200
commit4b886061fcf166bced932f9a8df8655ec4af2347 (patch)
treed2483292714d316c47f8396ff6b3037c40476704 /test/test_stringfmt01.cpp
parente0fd709be73fb7eada5dbeb1a58ebed6528202c8 (diff)
`snprintf` argument type checker `jau::cfmt` and simple string formatting perf tests
Due to 'Security Interpretation' implications, see below, the `snprintf` argument type checker `jau::cfmt` has been added and utilized in `template <typename... Args> format_string_v(...)`. jau::cfmt::check() provides strict type matching of arguments against the format string. Have jau::cfmt::check() to be passed _before_ using std::snprintf(), removing safety concerns of the latter and benefit from its formatting and performance. Validates arguments against format string at compile time or runtime, depending whether passed arguments are of constexpr nature (compile time). +++ Benchmark ========= gcc-12 benchmark name samples iterations mean ------------------------------------------------------------------------------- fmt__check bench 1 1 42.292 us fmt__check cnstexpr bench 1 2 9.9015 us format_000a_vsnprintf bench 1 1 214.458 us format_010a_vsnprintf bench 1 1 215.109 us fmt__020a macro bench 1 1 193.859 us fmt__020a cnstexpr-in bench 1 1 191.299 us fmt__020a_tsnprintf bench 1 1 264.927 us format_030a_strstream bench 1 1 490.923 us format_000b_vsnprintf bench 1 1 205.839 us format_010b_vsnprintf bench 1 1 204.899 us format_020b__snprintf bench 1 1 254.758 us format_030b_strstream bench 1 1 476.994 us +++ clang-18 benchmark name samples iterations mean ------------------------------------------------------------------------------- fmt__check bench 1 1 47.373 us fmt__check cnstexpr bench 1 2 9.477 us format_000a_vsnprintf bench 1 1 190.99 us format_010a_vsnprintf bench 1 1 200.4 us fmt__020a macro bench 1 1 181.141 us fmt__020a cnstexpr-in bench 1 1 178.34 us fmt__020a_tsnprintf bench 1 1 243.909 us format_030a_strstream bench 1 1 435.835 us format_000b_vsnprintf bench 1 1 184.91 us format_010b_vsnprintf bench 1 1 187.01 us format_020b__snprintf bench 1 1 234.629 us format_030b_strstream bench 1 1 415.386 us +++ Pass `--perf_analysis` for enhanced Catch2 benchmarking w/ 100 samples, e.g. `test_stringfmt --perf_analysis` Note: C++20 std::format wasn't available on either compiler toolchain. Note: Used formatting with C++ stringstream is limited and I haven't tried hard enought to properly reproduce the `printf` formatting goal. Intention of this test is for performance only. +++ C++ stringstream uses roughly 2.3 (or 5/2 max) of the time as c-lib's `snprintf`. Test utilizes non-const non-constexpr mutable values to be formatted at runtime - as well as compile time scenarious. Therefore stringstream is not able to format at compile time either, especially when evaluating values with formatting options. Hence only the va_args reinterpretation of stack data according to the format string conversion is added in `snprintf`. As visible - this extra work does not impact performance. Security Interpretation ======================= <https://en.cppreference.com/w/cpp/io/c/fprintf> "If any argument after default conversions is not the type expected by the corresponding conversion specifier, or if there are fewer arguments than required by format, the behavior is undefined." https://en.cppreference.com/w/cpp/language/variadic_arguments#Default_conversions https://en.cppreference.com/w/cpp/utility/variadic/va_arg "If the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined, unless: ... type promotion details .." "If va_arg is called when there are no more arguments in ap, the behavior is undefined."
Diffstat (limited to 'test/test_stringfmt01.cpp')
-rw-r--r--test/test_stringfmt01.cpp511
1 files changed, 511 insertions, 0 deletions
diff --git a/test/test_stringfmt01.cpp b/test/test_stringfmt01.cpp
new file mode 100644
index 0000000..e19ca6c
--- /dev/null
+++ b/test/test_stringfmt01.cpp
@@ -0,0 +1,511 @@
+/**
+ * Copyright the Collabora Online contributors.
+ * Copyright Gothel Software e.K.
+ *
+ * ***
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ***
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This Source Code Form is subject to the terms of the MIT License
+ * If a copy of the MIT was not distributed with this
+ * file, You can obtain one at https://opensource.org/license/mit/.
+ *
+ */
+#include <cassert>
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <string_view>
+#include <type_traits>
+
+#include <jau/cpp_lang_util.hpp>
+#include <jau/cpp_pragma.hpp>
+#include <jau/float_types.hpp>
+#include <jau/int_types.hpp>
+#include <jau/string_cfmt.hpp>
+#include <jau/string_util.hpp>
+#include <jau/test/catch2_ext.hpp>
+
+#ifdef HAS_STD_FORMAT
+ #include <format>
+#endif
+
+using namespace jau::float_literals;
+
+using namespace jau::int_literals;
+
+#define format_string_static_assert(...) \
+ jau::format_string(__VA_ARGS__); \
+ static_assert( 0 <= jau::cfmt::checkR(__VA_ARGS__).argCount() ); // compile time validation!
+
+#if 0
+// sadly doesn't work .. 'format' not constexpr ..
+template <typename... Args>
+constexpr std::string format_string_static_assert2(const std::string_view format, const Args &...args) {
+ static_assert(jau::cfmt::check2<Args...>(format));
+ return jau::format_string(format, args...);
+}
+#endif
+
+template <typename... Args>
+constexpr std::string format_string000(const std::size_t maxStrLen, const std::string_view format, const Args &...args) {
+ std::string str;
+ str.reserve(maxStrLen + 1); // incl. EOS
+ str.resize(maxStrLen); // excl. EOS
+
+ // -Wformat=2 -> -Wformat -Wformat-nonliteral -Wformat-security -Wformat-y2k
+ // -Wformat=2 -Wformat-overflow=2 -Wformat-signedness
+ PRAGMA_DISABLE_WARNING_PUSH
+ PRAGMA_DISABLE_WARNING_FORMAT_NONLITERAL
+ const size_t nchars = std::snprintf(&str[0], maxStrLen + 1, format.data(), args...);
+ PRAGMA_DISABLE_WARNING_POP
+ if( nchars < maxStrLen + 1 ) {
+ str.resize(nchars);
+ str.shrink_to_fit();
+ } // else truncated w/ nchars > MaxStrLen
+ return str;
+}
+
+std::string format_000a_vsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ size_t nchars;
+ std::string str;
+ {
+ const size_t bsz = 1024; // including EOS
+ str.reserve(bsz); // incl. EOS
+ str.resize(bsz - 1); // excl. EOS
+
+ nchars = std::snprintf(&str[0], bsz,
+ "format_000a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+
+ if( nchars < bsz ) {
+ str.resize(nchars);
+ str.shrink_to_fit();
+ return str;
+ }
+ }
+ str.clear(); // error
+ return str;
+}
+
+std::string format_010a_vsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return jau::format_string("format_010a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+
+constexpr std::string format_020a_tsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return jau::format_string_v(1023, "format_020a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+
+std::string format_030a_strstream(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ std::ostringstream ss1;
+ ss1 << "format_030a: "
+ << fa + 1.0_f32 << ", "
+ << fb + 1.0_f32 << ", "
+ << sz1 + 1 << ", "
+ << a_u64 + 1_u64 << ", "
+ << i + 1 << "\n";
+ return ss1.str();
+}
+
+#ifdef HAS_STD_FORMAT
+std::string format_040a_stdformat(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return std::format("format_040a: {0}, {1}, {3}, {4}, {5}\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+#endif
+
+std::string format_000b_vsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ size_t nchars;
+ std::string str;
+ {
+ const size_t bsz = 1024; // including EOS
+ str.reserve(bsz); // incl. EOS
+ str.resize(bsz - 1); // excl. EOS
+
+ nchars = std::snprintf(&str[0], bsz,
+ "format_000b: %.2f, %2.2f, %zu, %" PRIu64 ", %03d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+
+ if( nchars < bsz ) {
+ str.resize(nchars);
+ str.shrink_to_fit();
+ return str;
+ }
+ }
+ str.clear(); // error
+ return str;
+}
+std::string format_010b_vsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return jau::format_string("format_010b: %.2f, %2.2f, %zu, %" PRIu64 ", %03d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+
+std::string format_020b_tsnprintf(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return jau::format_string_v(1023, "format_020b: %.2f, %2.2f, %zu, %" PRIu64 ", %03d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+
+std::string format_030b_strstream(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ std::ostringstream ss1;
+ ss1.precision(3);
+ ss1 << "format_030b: "
+ << fa + 1.0_f32 << ", ";
+ ss1.width(3);
+ ss1 << fb + 1.0_f32 << ", "
+ << sz1 + 1 << ", "
+ << a_u64 + 1_u64 << ", ";
+ ss1.width(3);
+ ss1 << i + 1 << "\n";
+ return ss1.str();
+}
+
+#ifdef HAS_STD_FORMAT
+std::string format_040b_stdformat(float fa, float fb, size_t sz1, uint64_t a_u64, int i) {
+ return std::format("format_040b: {0:.2f}, {1:2.2f}, {3}, {4}, {5:03d}\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, i + 1);
+}
+#endif
+
+template <typename Func>
+size_t test_format(const Func func, bool output) {
+ constexpr float fa = 1.1f, fb = 2.2f;
+ constexpr size_t sz1 = 1;
+ constexpr uint64_t sz2 = 2;
+ constexpr int i = 3;
+
+ const std::string s = func(fa, fb, sz1, sz2, i);
+ volatile size_t l = s.length();
+ if( output ) {
+ std::cout << s;
+ }
+ REQUIRE(0 < l);
+ REQUIRE(l < 1024);
+ return l;
+}
+
+void format_0a() {
+ test_format(format_000a_vsnprintf, true);
+ test_format(format_010a_vsnprintf, true);
+ test_format(format_020a_tsnprintf, true);
+ test_format(format_030a_strstream, true);
+
+#ifdef HAS_STD_FORMAT
+ test_format(format_040a_stdformat, true);
+#endif
+}
+void format_0b() {
+ test_format(format_000b_vsnprintf, true);
+ test_format(format_010b_vsnprintf, true);
+ test_format(format_020b_tsnprintf, true);
+ test_format(format_030b_strstream, true);
+
+#ifdef HAS_STD_FORMAT
+ test_format(format_040b_stdformat, true);
+#endif
+}
+
+TEST_CASE("jau::cfmt_00", "[jau][std::string][jau::cfmt]") {
+ char buf[1024];
+ constexpr float fa = 1.123456f, fb = 2.2f;
+ constexpr size_t sz1 = 1;
+ constexpr int64_t sz2 = 2;
+ constexpr int i = 3;
+ const float *pf = &fa;
+
+ {
+ static_assert(false == std::is_signed_v<const float*>);
+ static_assert(false == std::is_unsigned_v<const float*>);
+
+ static_assert(true == std::is_signed_v<float>);
+ static_assert(false == std::is_unsigned_v<float>);
+ }
+ {
+ using E = int;
+ using T = unsigned int;
+ // using U = std::conditional_t<std::is_unsigned_v<T>, std::make_signed_t<T>, T>; // triggers the instantiating the 'other' case and hence fails
+ using U = typename std::conditional_t<std::is_unsigned_v<T>, std::make_signed<T>, std::type_identity<T>>::type; // NOLINT
+ static_assert( std::is_same_v<T, T> );
+ static_assert( std::is_same_v<E, U> );
+ }
+ {
+ using E = float;
+ using T = float;
+ // using U = std::conditional_t<std::is_unsigned_v<T>, std::make_signed_t<T>, T>; // triggers the instantiating the 'other' case and hence fails
+ using U = typename std::conditional_t<std::is_unsigned_v<T>, std::make_signed<T>, std::type_identity<T>>::type; // NOLINT
+ static_assert( std::is_same_v<T, T> );
+ static_assert( std::is_same_v<E, U> );
+ }
+ {
+ // we shall ignore signedness like snprintf
+ static_assert(true == jau::cfmt::check(" int -> int %d", (int)1));
+ static_assert(true == jau::cfmt::check("unsigned int -> int %d", (unsigned int)1));
+ static_assert(true == jau::cfmt::check("unsigned int -> unsigned int %u", (unsigned int)1));
+ static_assert(true == jau::cfmt::check(" int -> unsigned int %u", (int)1));
+ static_assert(true == jau::cfmt::check(" char -> int %d", (char)1)); // integral target type > given type
+ static_assert(false == jau::cfmt::check(" error long -> int %d", (long)1)); // error: integral target type < given type
+
+ static_assert(true == jau::cfmt::check(" %d", i));
+ static_assert(true == jau::cfmt::check(" %f", fa));
+ static_assert(true == jau::cfmt::check(" %zd", (ssize_t)1));
+ static_assert(true == jau::cfmt::check(" %zu", (size_t)1));
+ static_assert(true == jau::cfmt::check(" %" PRIi64 ".", (int64_t)1));
+ static_assert(true == jau::cfmt::check(" %" PRIi64 ".", sz2));
+ static_assert(true == jau::cfmt::check(" %p", pf));
+
+ static_assert(0 == jau::cfmt::checkR("Hello World").argCount());
+ static_assert(1 == jau::cfmt::checkR("Hello World %d", 1).argCount());
+ static_assert(1 == jau::cfmt::checkR("Hello 1 %d", i).argCount());
+ static_assert(true == jau::cfmt::check("Hello World"));
+ static_assert(true == jau::cfmt::check("Hello World %d", 1));
+ static_assert(true == jau::cfmt::check("Hello 1 %d", i));
+
+ static_assert(1 == jau::cfmt::checkR("Hello 1 %.2f", fa).argCount());
+ static_assert(1 == jau::cfmt::checkR("Hello 1 %.2f - end", fa).argCount());
+ static_assert(2 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f - end", fa, fb).argCount());
+ static_assert(3 == jau::cfmt::checkR("Hello 1 %.2f , 2 %2.2f, 3 %zu - end", fa, fb, sz1).argCount());
+ static_assert(4 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 " - end", fa, fb, sz1, sz2).argCount());
+ static_assert(5 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d - end", fa, fb, sz1, sz2, i).argCount());
+ // static_assert(6 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d, 6 %p - end", fa, fb, sz1, sz2, i, pf).argCount());
+
+ static_assert(false == jau::cfmt::check("Hello World %"));
+ static_assert(0 > jau::cfmt::checkR("Hello World %").argCount());
+ static_assert(0 > jau::cfmt::checkR("Hello 1 %d").argCount());
+ static_assert(-1 == jau::cfmt::checkR("Hello 1 %d", fa).argCount());
+ static_assert(-1 == jau::cfmt::checkR("Hello 1 %d", sz1).argCount());
+ static_assert(-6 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d, 6 %p - end",
+ fa, fb, sz1, sz2, i, i).argCount());
+ static_assert(false == jau::cfmt::check("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d, 6 %p - end",
+ fa, fb, sz1, sz2, i, i));
+
+ const std::string s = format_string_static_assert("format_020a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, sz2 + 1_u64, i + 1);
+ (void)s;
+ }
+ {
+ constexpr bool b1 = std::is_nothrow_assignable_v<int&, uint64_t>;
+ static_assert( b1, "Not Assignable" );
+ constexpr bool b2 = std::is_nothrow_assignable_v<unsigned int&, long double>;
+ static_assert( b2, "Not Assignable" );
+ format_string_static_assert("Hello");
+ format_string_static_assert("Hello %d", (unsigned int)2);
+ format_string_static_assert("Hello %u", (int)2);
+
+ constexpr jau::cfmt::PResult c1 = jau::cfmt::checkR("Hello %u", (unsigned int)1);
+ std::cerr << "XXX: " << __LINE__ << ": " << c1 << std::endl;
+ REQUIRE(false == c1.error());
+ }
+ {
+ constexpr jau::cfmt::PResult c1 = jau::cfmt::checkR("Hello World");
+ REQUIRE(false == c1.error());
+ REQUIRE(0 == c1.argCount());
+ REQUIRE(0 == jau::cfmt::checkR("Hello World").argCount());
+ constexpr jau::cfmt::PResult c3 = jau::cfmt::checkR("Hello 1 %d", i);
+ REQUIRE(false == c3.error());
+ REQUIRE(1 == c3.argCount());
+ REQUIRE(9 == std::snprintf(buf, sizeof(buf), "Hello 1 %d", i));
+
+ // `Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %li, 5 %03d, 6 %p - end`
+ REQUIRE(1 == jau::cfmt::checkR("Hello 1 %.2f", fa).argCount());
+ REQUIRE(1 == jau::cfmt::checkR("Hello 1 %.2f - end", fa).argCount());
+
+ // 'Hello 1 %.2f, 2 %2.2f - end'
+ jau::cfmt::PResult pc = jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f - end", fa, fb);
+ std::cerr << "XXX: " << __LINE__ << ": " << pc << std::endl;
+ REQUIRE(2 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f - end", fa, fb).argCount());
+
+ // `Hello 1 %.2f, 2 %2.2f, 3 %zu - end`
+ pc = jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu - end", fa, fb, sz1);
+ std::cerr << "XXX: " << __LINE__ << ": " << pc << std::endl;
+ REQUIRE(3 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu - end", fa, fb, sz1).argCount());
+
+ REQUIRE(4 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 " - end", fa, fb, sz1, sz2).argCount());
+ REQUIRE(5 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d - end", fa, fb, sz1, sz2, i).argCount());
+ REQUIRE(6 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d, 6 %p - end", fa, fb, sz1, sz2, i, pf).argCount());
+
+ // REQUIRE(13 == std::snprintf(buf, sizeof(buf), "Hello World %").argCount());
+ REQUIRE(0 > jau::cfmt::checkR("Hello World %").argCount());
+ REQUIRE(0 > jau::cfmt::checkR("Hello 1 %d").argCount());
+ REQUIRE(-1 == jau::cfmt::checkR("Hello 1 %d", fa).argCount());
+ REQUIRE(-1 == jau::cfmt::checkR("Hello 1 %d", sz1).argCount());
+ REQUIRE(-6 == jau::cfmt::checkR("Hello 1 %.2f, 2 %2.2f, 3 %zu, 4 %" PRIi64 ", 5 %03d, 6 %p - end",
+ fa, fb, sz1, sz2, i, i).argCount());
+ }
+}
+
+TEST_CASE("jau::cfmt_01", "[jau][std::string][format_string]") {
+ format_0a();
+ format_0b();
+}
+
+/// Execute with `test_stringfmt --perf_analysis`
+TEST_CASE("jau::cfmt_10", "[benchmark][jau][std::string][format_string]") {
+ const size_t loops = catch_auto_run ? 1000 : 1000;
+ WARN("Benchmark with " + std::to_string(loops) + " loops");
+ CHECK(true);
+
+ BENCHMARK("fmt__check bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ float fa = 1.1f, fb = 2.2f;
+ size_t sz1 = 1;
+ uint64_t sz2 = 2;
+ int i1 = 3;
+
+ size_t r = jau::cfmt::checkR("format_check: %.2f, %2.2f, %zu, %" PRIu64 ", %03d\n",
+ fa, fb, sz1, sz2, i1).argCount();
+ REQUIRE(5 == r);
+ res = res + r;
+ }
+ return res;
+ };
+ BENCHMARK("fmt__check cnstexpr bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ constexpr float fa = 1.1f, fb = 2.2f;
+ constexpr size_t sz1 = 1;
+ constexpr uint64_t sz2 = 2;
+ constexpr int i1 = 3;
+
+ constexpr jau::cfmt::PResult pc = jau::cfmt::checkR("format_check: %.2f, %2.2f, %zu, %" PRIu64 ", %03d\n",
+ fa, fb, sz1, sz2, i1);
+ constexpr size_t r = pc.argCount();
+ REQUIRE(5 == r);
+ res = res + r;
+ }
+ return res;
+ };
+ BENCHMARK("format_000a_vsnprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_000a_vsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("format_010a_vsnprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_010a_vsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("fmt__020a macro bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ {
+ constexpr float fa = 1.1f, fb = 2.2f;
+ constexpr size_t sz1 = 1;
+ constexpr uint64_t a_u64 = 2;
+ constexpr int j = 3;
+ const std::string s = format_string_static_assert("format_020a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, j + 1);
+ res = res + s.length();
+ }
+ }
+ return res;
+ };
+ BENCHMARK("fmt__020a cnstexpr-in bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ {
+ constexpr float fa = 1.1f, fb = 2.2f;
+ constexpr size_t sz1 = 1;
+ constexpr uint64_t a_u64 = 2;
+ constexpr int j = 3;
+ if constexpr( jau::cfmt::check("format_020a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, j + 1) ) {
+ std::string s;
+ s.reserve(1023 + 1); // incl. EOS
+ s.resize(1023); // excl. EOS
+
+ // -Wformat=2 -> -Wformat -Wformat-nonliteral -Wformat-security -Wformat-y2k
+ // -Wformat=2 -Wformat-overflow=2 -Wformat-signedness
+ PRAGMA_DISABLE_WARNING_PUSH
+ PRAGMA_DISABLE_WARNING_FORMAT_NONLITERAL
+ const size_t nchars = std::snprintf(&s[0], 1023 + 1, "format_020a: %f, %f, %zu, %" PRIu64 ", %d\n",
+ fa + 1.0_f32, fb + 1.0_f32, sz1 + 1, a_u64 + 1_u64, j + 1);
+ PRAGMA_DISABLE_WARNING_POP
+ if( nchars < 1023 + 1 ) {
+ s.resize(nchars);
+ s.shrink_to_fit();
+ } // else truncated w/ nchars > MaxStrLen
+ res = res + s.length();
+ }
+ }
+ }
+ return res;
+ };
+ BENCHMARK("fmt__020a_tsnprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_020a_tsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("format_030a_strstream bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_030a_strstream, false);
+ }
+ return res;
+ };
+#ifdef HAS_STD_FORMAT
+ BENCHMARK("format_040a_stdformat bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < iterations; ++i ) {
+ res = res + test_format(format_040a_stdformat, false);
+ }
+ return res;
+ };
+#endif
+
+ BENCHMARK("format_000b_vsnprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_000b_vsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("format_010b_vsnprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_010b_vsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("format_020b__snprintf bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_020b_tsnprintf, false);
+ }
+ return res;
+ };
+ BENCHMARK("format_030b_strstream bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < loops; ++i ) {
+ res = res + test_format(format_030b_strstream, false);
+ }
+ return res;
+ };
+#ifdef HAS_STD_FORMAT
+ BENCHMARK("format_040b_stdformat bench") {
+ volatile size_t res = 0;
+ for( size_t i = 0; i < iterations; ++i ) {
+ res = res + test_format(format_040b_stdformat, false);
+ }
+ return res;
+ };
+#endif
+}