From 30588a70857bffa57d46bdf0662450ca8d9b1272 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 4 Dec 2022 18:07:11 +0800 Subject: [PATCH] upd: [imp] fmt_context --- benchmark/benchmark_fmt.cpp | 49 +++++++- include/libimp/byte.h | 10 +- include/libimp/fmt.h | 120 ++++++++++---------- include/libimp/fmt_cpo.h | 52 ++++++++- include/libimp/log.h | 7 +- include/libimp/result.h | 10 +- include/libimp/span.h | 2 +- include/libimp/system.h | 12 +- src/libimp/fmt.cpp | 211 ++++++++++++++++++++++++----------- src/libimp/log.cpp | 21 +++- src/libimp/system.cpp | 5 - test/imp/test_imp_fmt.cpp | 111 ++++++++++-------- test/imp/test_imp_system.cpp | 3 - 13 files changed, 398 insertions(+), 215 deletions(-) diff --git a/benchmark/benchmark_fmt.cpp b/benchmark/benchmark_fmt.cpp index 5203c3f..be39e1a 100644 --- a/benchmark/benchmark_fmt.cpp +++ b/benchmark/benchmark_fmt.cpp @@ -11,13 +11,26 @@ namespace { void imp_fmt_string(benchmark::State &state) { for (auto _ : state) { - std::ignore = imp::fmt("hello world. hello world. hello world. hello world. hello world."); + std::ignore = imp::fmt("hello world.hello world.hello world.hello world.hello world."); + } +} + +void imp_fmt_multi_string(benchmark::State &state) { + for (auto _ : state) { + std::ignore = imp::fmt("hello world.", "hello world.", "hello world.", "hello world.", "hello world."); } } void fmt_format_string(benchmark::State &state) { for (auto _ : state) { - std::ignore = fmt::format("hello world. hello world. hello world. hello world. hello world."); + std::ignore = fmt::format("hello world.hello world.hello world.hello world.hello world."); + } +} + +void fmt_format_multi_string(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}{}{}{}{}", + "hello world.", " hello world.", " hello world.", " hello world.", " hello world."); } } @@ -27,24 +40,48 @@ void imp_fmt_int(benchmark::State &state) { } } +void imp_fmt_multi_int(benchmark::State &state) { + for (auto _ : state) { + std::ignore = imp::fmt(654321, 654321, 654321, 654321, 654321); + } +} + void fmt_format_int(benchmark::State &state) { for (auto _ : state) { std::ignore = fmt::format("{}", 654321); } } +void fmt_format_multi_int(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}{}{}{}{}", 654321, 654321, 654321, 654321, 654321); + } +} + void imp_fmt_float(benchmark::State &state) { for (auto _ : state) { std::ignore = imp::fmt(654.321); } } +void imp_fmt_multi_float(benchmark::State &state) { + for (auto _ : state) { + std::ignore = imp::fmt(654.321, 654.321, 654.321, 654.321, 654.321); + } +} + void fmt_format_float(benchmark::State &state) { for (auto _ : state) { std::ignore = fmt::format("{}", 654.321); } } +void fmt_format_multi_float(benchmark::State &state) { + for (auto _ : state) { + std::ignore = fmt::format("{}{}{}{}{}", 654.321, 654.321, 654.321, 654.321, 654.321); + } +} + void imp_fmt_chrono(benchmark::State &state) { for (auto _ : state) { std::ignore = imp::fmt(std::chrono::system_clock::now()); @@ -65,5 +102,13 @@ BENCHMARK(imp_fmt_int); BENCHMARK(fmt_format_int); BENCHMARK(imp_fmt_float); BENCHMARK(fmt_format_float); + +BENCHMARK(imp_fmt_multi_string); +BENCHMARK(fmt_format_multi_string); +BENCHMARK(imp_fmt_multi_int); +BENCHMARK(fmt_format_multi_int); +BENCHMARK(imp_fmt_multi_float); +BENCHMARK(fmt_format_multi_float); + BENCHMARK(imp_fmt_chrono); BENCHMARK(fmt_format_chrono); diff --git a/include/libimp/byte.h b/include/libimp/byte.h index 5bf2b6c..88b43a0 100644 --- a/include/libimp/byte.h +++ b/include/libimp/byte.h @@ -164,17 +164,17 @@ auto as_bytes(span s) noexcept -> span { return {byte_cast(s.data()), s.size_bytes()}; } -/// @brief Custom defined fmt_to_string method for imp::fmt +/// @brief Custom defined fmt_to method for imp::fmt namespace detail { -inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), ::LIBIMP::byte b) { - return ::LIBIMP::to_string(static_cast(b), "02x"); +inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, ::LIBIMP::byte b) { + return ::LIBIMP::to_string(ctx, static_cast(b), "02x"); } template , ::LIBIMP::byte>::value>> -std::string tag_invoke(fmt_to_string_t, fmt_ref arg) noexcept { - return ::LIBIMP::to_string(static_cast(arg.param), arg.fstr); +bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, fmt_ref arg) noexcept { + return ::LIBIMP::to_string(ctx, static_cast(arg.param), arg.fstr); } } // namespace detail diff --git a/include/libimp/fmt.h b/include/libimp/fmt.h index dbbb088..fe901dc 100644 --- a/include/libimp/fmt.h +++ b/include/libimp/fmt.h @@ -49,66 +49,62 @@ auto spec(char const (&fstr)[N]) noexcept { }; } -/// @brief String formatting function. - -template -LIBIMP_NODISCARD std::string fmt(A &&a) { - return fmt_to_string(std::forward(a)); +/** + * @brief String formatting function. + * + * @param args arguments that support the fmt output + * @return an empty string if the fmt output fails + */ +template +LIBIMP_NODISCARD std::string fmt(A &&...args) { + std::string joined; + fmt_context ctx(joined); + if (fmt_to(ctx, std::forward(args)...)) { + return ctx.finish() ? joined : ""; + } + return {}; } -template -LIBIMP_NODISCARD std::string fmt(A1 &&a1, A &&...args) { - std::string joined(fmt(std::forward(a1))); - LIBIMP_UNUSED auto unfold = { - joined.append(fmt_to_string(std::forward(args)))... - }; - return joined; -} - -/// @brief Return the string directly. - inline char const *to_string(char const * a) noexcept { return (a == nullptr) ? "" : a; } - inline std::string to_string(std::string const &a) noexcept { return a; } - inline std::string to_string(std::string && a) noexcept { return std::move(a); } -LIBIMP_EXPORT std::string to_string(char const * a, span fstr) noexcept; - inline std::string to_string(std::string const &a, span fstr) noexcept { return to_string(a.c_str(), fstr); } +/// @brief String types. +LIBIMP_EXPORT bool to_string(fmt_context &ctx, char const * a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::string const &a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, char const * a, span fstr) noexcept; + inline bool to_string(fmt_context &ctx, std::string const &a, span fstr) noexcept { return to_string(ctx, a.c_str(), fstr); } /// @brief Character to string conversion. -/// @return an empty string if the conversion fails - inline std::string to_string(char a) noexcept { return {a}; } +LIBIMP_EXPORT bool to_string(fmt_context &ctx, char a) noexcept; #if defined(LIBIMP_CPP_20) - inline std::string to_string(char8_t a) noexcept { return to_string((char)a); } + inline bool to_string(fmt_context &ctx, char8_t a) noexcept { return to_string(ctx, (char)a); } #endif // defined(LIBIMP_CPP_20) -LIBIMP_EXPORT std::string to_string(wchar_t a) noexcept; -LIBIMP_EXPORT std::string to_string(char16_t a) noexcept; -LIBIMP_EXPORT std::string to_string(char32_t a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, wchar_t a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, char16_t a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, char32_t a) noexcept; /// @brief Conversion of numeric types to strings. -/// @return an empty string if the conversion fails -LIBIMP_EXPORT std::string to_string( signed short a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(unsigned short a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string( signed int a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(unsigned int a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string( signed long a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(unsigned long a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string( signed long long a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(unsigned long long a, span fstr = {}) noexcept; - inline std::string to_string( signed char a, span fstr = {}) noexcept { return to_string( (int)a, fstr); } - inline std::string to_string(unsigned char a, span fstr = {}) noexcept { return to_string((unsigned)a, fstr); } +LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed short a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned short a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed int a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned int a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed long a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned long a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, signed long long a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, unsigned long long a, span fstr = {}) noexcept; + inline bool to_string(fmt_context &ctx, signed char a, span fstr = {}) noexcept { return to_string(ctx, (int)a, fstr); } + inline bool to_string(fmt_context &ctx, unsigned char a, span fstr = {}) noexcept { return to_string(ctx, (unsigned)a, fstr); } /// @brief Conversion of floating point type to strings. -/// @return an empty string if the conversion fails -LIBIMP_EXPORT std::string to_string( double a, span fstr = {}) noexcept; -LIBIMP_EXPORT std::string to_string(long double a, span fstr = {}) noexcept; - inline std::string to_string(float a, span fstr = {}) noexcept { return to_string((double)a, fstr); } +LIBIMP_EXPORT bool to_string(fmt_context &ctx, double a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, long double a, span fstr = {}) noexcept; + inline bool to_string(fmt_context &ctx, float a, span fstr = {}) noexcept { return to_string(ctx, (double)a, fstr); } /// @brief Pointer. -inline std::string to_string(std::nullptr_t) noexcept { return "null"; } +LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::nullptr_t) noexcept; template ::value>> -LIBIMP_EXPORT std::string to_string(T const volatile *a) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, T const volatile *a) noexcept; /// @brief Date and time. -LIBIMP_EXPORT std::string to_string(std::tm const &a, span fstr = {}) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, std::tm const &a, span fstr = {}) noexcept; namespace detail { @@ -116,53 +112,55 @@ namespace detail { * @brief Convert std::time_t to std::string. * @return an empty string if the conversion fails */ -inline std::string time_to_string(std::time_t tt, span fstr) noexcept { +inline bool time_to_string(fmt_context &ctx, std::time_t tt, span fstr) noexcept { #if defined(LIBIMP_CC_MSVC) /// @see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s std::tm tm {}; if (::localtime_s(&tm, &tt) != 0) { return {}; } - return to_string(tm, fstr); + return to_string(ctx, tm, fstr); #else - return to_string(*std::localtime(&tt), fstr); + return to_string(ctx, *std::localtime(&tt), fstr); #endif } } // namespace detail template -std::string to_string(std::chrono::time_point const &a, span fstr = {}) noexcept { - return detail::time_to_string(std::chrono::system_clock::to_time_t(a), fstr); +bool to_string(fmt_context &ctx, std::chrono::time_point const &a, span fstr = {}) noexcept { + return detail::time_to_string(ctx, std::chrono::system_clock::to_time_t(a), fstr); } /** - * @brief Predefined fmt_to_string method + * @brief Predefined fmt_to method */ namespace detail { template -auto tag_invoke(fmt_to_string_t, T &&arg) noexcept - -> decltype(::LIBIMP::to_string(std::forward(arg))) { - return ::LIBIMP::to_string(std::forward(arg)); +auto tag_invoke(fmt_to_t, fmt_context &ctx, T &&arg) noexcept + -> decltype(::LIBIMP::to_string(ctx, std::forward(arg))) { + return ::LIBIMP::to_string(ctx, std::forward(arg)); } template -auto tag_invoke(fmt_to_string_t, fmt_ref arg) noexcept - -> decltype(::LIBIMP::to_string(static_cast(arg.param), arg.fstr)) { - return ::LIBIMP::to_string(static_cast(arg.param), arg.fstr); +auto tag_invoke(fmt_to_t, fmt_context &ctx, fmt_ref arg) noexcept + -> decltype(::LIBIMP::to_string(ctx, static_cast(arg.param), arg.fstr)) { + return ::LIBIMP::to_string(ctx, static_cast(arg.param), arg.fstr); } template -std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), span s) { +bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, span s) { if (s.empty()) { - return {}; + return false; + } + if (!fmt_to(ctx, s[0])) { + return false; } - auto appender = fmt(s[0]); for (std::size_t i = 1; i < s.size(); ++i) { - appender += fmt(' ', s[i]); + if (!fmt_to(ctx, ' ', s[i])) return false; } - return appender; + return true; } } // namespace detail diff --git a/include/libimp/fmt_cpo.h b/include/libimp/fmt_cpo.h index dd5bb28..48437f5 100644 --- a/include/libimp/fmt_cpo.h +++ b/include/libimp/fmt_cpo.h @@ -7,26 +7,66 @@ #pragma once #include +#include +#include #include "libimp/def.h" #include "libimp/generic.h" +#include "libimp/detect_plat.h" +#include "libimp/span.h" +#include "libimp/export.h" LIBIMP_NAMESPACE_BEG_ +constexpr std::size_t fmt_context_aligned_size = 512U; + /** - * @brief Supports custom fmt_to_string methods for imp::fmt. + * @brief The context of fmt. + */ +class LIBIMP_EXPORT fmt_context { + std::array sbuf_; + + std::string &joined_; + std::size_t offset_; + +public: + fmt_context(std::string &j) noexcept; + + std::size_t capacity() noexcept; + void reset() noexcept; + bool finish() noexcept; + bool resize(std::size_t sz) noexcept; + span buffer() noexcept; + bool expend(std::size_t sz) noexcept; + bool append(std::string const &str) noexcept; +}; + +/** + * @brief Supports custom fmt_to methods for imp::fmt. */ namespace detail { -struct fmt_to_string_t { - template - std::string operator()(T &&arg) const { - return ::LIBIMP::tag_invoke(fmt_to_string_t{}, std::forward(arg)); +class fmt_to_t { + template + bool get_result(fmt_context &ctx, A1 && a1) const { + return ::LIBIMP::tag_invoke(fmt_to_t{}, ctx, std::forward(a1)); + } + + template + bool get_result(fmt_context &ctx, A1 && a1, A &&...args) const { + return get_result(ctx, std::forward(a1)) + && get_result(ctx, std::forward(args)...); + } + +public: + template + bool operator()(fmt_context &ctx, A &&...args) const { + return get_result(ctx, std::forward(args)...); } }; } // namespace detail -constexpr detail::fmt_to_string_t fmt_to_string {}; +constexpr detail::fmt_to_t fmt_to {}; LIBIMP_NAMESPACE_END_ diff --git a/include/libimp/log.h b/include/libimp/log.h index 43924ba..8e9c491 100644 --- a/include/libimp/log.h +++ b/include/libimp/log.h @@ -37,11 +37,12 @@ struct context { }; LIBIMP_EXPORT std::string to_string(context &&) noexcept; +LIBIMP_EXPORT bool to_string(fmt_context &ctx, context &&) noexcept; -/// @brief Custom defined fmt_to_string method for imp::fmt +/// @brief Custom defined fmt_to method for imp::fmt template -std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), context &&arg) noexcept { - return ::LIBIMP::log::to_string(std::move(arg)); +bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, context &&arg) noexcept { + return ::LIBIMP::log::to_string(ctx, std::move(arg)); } } // namespace log diff --git a/include/libimp/result.h b/include/libimp/result.h index 158a519..4aa8735 100644 --- a/include/libimp/result.h +++ b/include/libimp/result.h @@ -155,16 +155,16 @@ public: friend bool operator!=(result const &lhs, result const &rhs) noexcept { return lhs.code_ != rhs.code_; } }; -/// @brief Custom defined fmt_to_string method for imp::fmt +/// @brief Custom defined fmt_to method for imp::fmt namespace detail { -inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), result_code r) { - return fmt("[", (r ? "succ" : "fail"), ", value = ", *r, "]"); +inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result_code r) { + return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", *r, "]"); } template -std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), result r) { - return fmt("[", (r ? "succ" : "fail"), ", value = ", result::default_traits_t::format(r), "]"); +bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result r) { + return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", result::default_traits_t::format(r), "]"); } } // namespace detail diff --git a/include/libimp/span.h b/include/libimp/span.h index fd25f49..7a78182 100644 --- a/include/libimp/span.h +++ b/include/libimp/span.h @@ -234,7 +234,7 @@ auto make_span(T (&arr)[E]) noexcept -> span { } template -auto make_span(std::array const &arr) noexcept -> span { +auto make_span(std::array &arr) noexcept -> span { return {arr}; } diff --git a/include/libimp/system.h b/include/libimp/system.h index 69ab783..fd3edcf 100644 --- a/include/libimp/system.h +++ b/include/libimp/system.h @@ -50,10 +50,8 @@ public: std::string str() const noexcept; - friend bool operator==(error const &lhs, error const &rhs) noexcept; - friend bool operator!=(error const &lhs, error const &rhs) noexcept; - - friend std::ostream &operator<<(std::ostream &o, error const &e); + friend LIBIMP_EXPORT bool operator==(error const &lhs, error const &rhs) noexcept; + friend LIBIMP_EXPORT bool operator!=(error const &lhs, error const &rhs) noexcept; }; /** @@ -65,10 +63,10 @@ enum class info : std::int32_t { LIBIMP_EXPORT result conf(info) noexcept; /** - * @brief @brief Custom defined fmt_to_string method for imp::fmt + * @brief @brief Custom defined fmt_to method for imp::fmt */ -inline std::string tag_invoke(decltype(::LIBIMP::fmt_to_string), error r) noexcept { - return error_msg(r.code()); +inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, error r) noexcept { + return fmt_to(ctx, error_msg(r.code())); } } // namespace sys diff --git a/src/libimp/fmt.cpp b/src/libimp/fmt.cpp index cb680d5..c2ba502 100644 --- a/src/libimp/fmt.cpp +++ b/src/libimp/fmt.cpp @@ -19,12 +19,16 @@ LIBIMP_NAMESPACE_BEG_ */ namespace { -span normalize(span a) { +constexpr std::size_t roundup(std::size_t sz) noexcept { + return (sz & ~(fmt_context_aligned_size - 1)) + fmt_context_aligned_size; +} + +span normalize(span const &a) { if (a.empty()) return {}; return a.first(a.size() - (a.back() == '\0' ? 1 : 0)); } -span smem_cpy(span sbuf, span a) noexcept { +span smem_cpy(span const &sbuf, span a) noexcept { if (sbuf.empty()) return {}; a = normalize(a); auto sz = (std::min)(sbuf.size() - 1, a.size()); @@ -32,13 +36,13 @@ span smem_cpy(span sbuf, span a) noexcept { return sbuf.first(sz); } -span sbuf_cpy(span sbuf, span a) noexcept { +span sbuf_cpy(span sbuf, span const &a) noexcept { sbuf = smem_cpy(sbuf, a); *sbuf.end() = '\0'; return sbuf; } -span sbuf_cat(span sbuf, std::initializer_list> args) noexcept { +span sbuf_cat(span const &sbuf, std::initializer_list> args) noexcept { std::size_t remain = sbuf.size(); for (auto s : args) { remain -= smem_cpy(sbuf.last(remain), s).size(); @@ -49,21 +53,21 @@ span sbuf_cat(span sbuf, std::initializer_list> arg } span local_fmt_str() noexcept { - thread_local std::array sbuf; + thread_local std::array sbuf; return sbuf; } -char const *as_cstr(span a) { +char const *as_cstr(span const &a) { if (a.empty()) return ""; if (a.back() == '\0') return a.data(); return sbuf_cpy(local_fmt_str(), a).data(); } -span fmt_of(span fstr, span s) { +span fmt_of(span const &fstr, span const &s) { return sbuf_cat(local_fmt_str(), {"%", fstr, s}); } -span fmt_of_unsigned(span fstr, span l) { +span fmt_of_unsigned(span fstr, span const &l) { if (fstr.empty()) { return fmt_of(l, "u"); } @@ -77,7 +81,7 @@ span fmt_of_unsigned(span fstr, span l) { } } -span fmt_of_signed(span fstr, span l) { +span fmt_of_signed(span fstr, span const &l) { if (fstr.empty()) { return fmt_of(l, "d"); } @@ -91,7 +95,7 @@ span fmt_of_signed(span fstr, span l) { } } -span fmt_of_float(span fstr, span l) { +span fmt_of_float(span fstr, span const &l) { if (fstr.empty()) { return fmt_of(l, "f"); } @@ -106,125 +110,206 @@ span fmt_of_float(span fstr, span l) { } template -std::string sprintf(span sfmt, A a) { - std::array sbuf; - auto sz = std::snprintf(sbuf.data(), sbuf.size(), sfmt.data(), a); - if (sz <= 0) return {}; - if (sz < sbuf.size()) { - return std::string(sbuf.data(), sz); +int sprintf(fmt_context &ctx, span const &sfmt, A a) { + for (;;) { + auto sbuf = ctx.buffer(); + auto sz = std::snprintf(sbuf.data(), sbuf.size(), sfmt.data(), a); + if (sz <= 0) { + return sz; + } + if (sz < sbuf.size()) { + return ctx.expend(sz) ? sz : -1; + } + if (!ctx.resize(sz + 1)) { + return -1; + } } - std::string des; - des.resize(sz + 1); - if (std::snprintf(&des[0], des.size(), sfmt.data(), a) < 0) { - return {}; - } - des.pop_back(); // remove the terminated null character - return des; } template -std::string sprintf(F fop, span fstr, span s, A a) noexcept { +bool sprintf(fmt_context &ctx, F fop, span const &fstr, span const &s, A a) noexcept { LIBIMP_TRY { - return ::LIBIMP::sprintf(fop(fstr, s), a); + return ::LIBIMP::sprintf(ctx, fop(fstr, s), a) >= 0; } LIBIMP_CATCH(...) { - return {}; + return false; } } } // namespace -std::string to_string(char const *a, span fstr) noexcept { - if (a == nullptr) return {}; - return ::LIBIMP::sprintf(fmt_of, fstr, "s", a); +/// @brief The context of fmt. + +fmt_context::fmt_context(std::string &j) noexcept + : joined_(j) + , offset_(0) {} + +std::size_t fmt_context::capacity() noexcept { + return (offset_ < sbuf_.size()) ? sbuf_.size() : joined_.size(); } -std::string to_string(wchar_t a) noexcept { +void fmt_context::reset() noexcept { + offset_ = 0; +} + +bool fmt_context::finish() noexcept { + LIBIMP_TRY { + if (offset_ < sbuf_.size()) { + joined_.assign(sbuf_.data(), offset_); + } else { + joined_.resize(offset_); + } + return true; + } LIBIMP_CATCH(...) { + return false; + } +} + +bool fmt_context::resize(std::size_t sz) noexcept { + LIBIMP_TRY { + if (sz < sbuf_.size()) { + return true; + } + joined_.resize(roundup(sz)); + return true; + } LIBIMP_CATCH(...) { + return false; + } +} + +span fmt_context::buffer() noexcept { + if (offset_ < sbuf_.size()) { + return make_span(sbuf_).subspan(offset_); + } else { + return {&joined_[offset_], joined_.size() - offset_}; + } +} + +bool fmt_context::expend(std::size_t sz) noexcept { + if ((offset_ += sz) < sbuf_.size()) { + return true; + } + return (offset_ < joined_.size()) || resize(offset_); +} + +bool fmt_context::append(std::string const &str) noexcept { + if ((buffer().size() < str.size()) && !resize(offset_ + str.size())) { + return false; + } + std::memcpy(buffer().data(), str.data(), str.size()); + offset_ += str.size(); + return true; +} + +/// @brief To string conversion. + +bool to_string(fmt_context &ctx, char const *a) noexcept { + return to_string(ctx, a, {}); +} + +bool to_string(fmt_context &ctx, std::string const &a) noexcept { + return ctx.append(a); +} + +bool to_string(fmt_context &ctx, char const *a, span fstr) noexcept { + if (a == nullptr) return false; + return ::LIBIMP::sprintf(ctx, fmt_of, fstr, "s", a); +} + +bool to_string(fmt_context &ctx, char a) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of, {}, "c", a); +} + +bool to_string(fmt_context &ctx, wchar_t a) noexcept { LIBIMP_TRY { std::string des; cvt_sstr(std::wstring{a}, des); - return des; + return ctx.append(des); } LIBIMP_CATCH(...) { - return {}; + return false; } } -std::string to_string(char16_t a) noexcept { +bool to_string(fmt_context &ctx, char16_t a) noexcept { LIBIMP_TRY { std::string des; cvt_sstr(std::u16string{a}, des); - return des; + return ctx.append(des); } LIBIMP_CATCH(...) { - return {}; + return false; } } -std::string to_string(char32_t a) noexcept { +bool to_string(fmt_context &ctx, char32_t a) noexcept { LIBIMP_TRY { std::string des; cvt_sstr(std::u32string{a}, des); - return des; + return ctx.append(des); } LIBIMP_CATCH(...) { - return {}; + return false; } } -std::string to_string(signed short a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_signed, fstr, "h", a); +bool to_string(fmt_context &ctx, signed short a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "h", a); } -std::string to_string(unsigned short a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "h", a); +bool to_string(fmt_context &ctx, unsigned short a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "h", a); } -std::string to_string(signed int a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_signed, fstr, "", a); +bool to_string(fmt_context &ctx, signed int a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "", a); } -std::string to_string(unsigned int a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "", a); +bool to_string(fmt_context &ctx, unsigned int a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "", a); } -std::string to_string(signed long a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_signed, fstr, "l", a); +bool to_string(fmt_context &ctx, signed long a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "l", a); } -std::string to_string(unsigned long a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "l", a); +bool to_string(fmt_context &ctx, unsigned long a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "l", a); } -std::string to_string(signed long long a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_signed, fstr, "ll", a); +bool to_string(fmt_context &ctx, signed long long a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_signed, fstr, "ll", a); } -std::string to_string(unsigned long long a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "ll", a); +bool to_string(fmt_context &ctx, unsigned long long a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_unsigned, fstr, "ll", a); } -std::string to_string(double a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_float, fstr, "", a); +bool to_string(fmt_context &ctx, double a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_float, fstr, "", a); } -std::string to_string(long double a, span fstr) noexcept { - return ::LIBIMP::sprintf(fmt_of_float, fstr, "L", a); +bool to_string(fmt_context &ctx, long double a, span fstr) noexcept { + return ::LIBIMP::sprintf(ctx, fmt_of_float, fstr, "L", a); +} + +bool to_string(fmt_context &ctx, std::nullptr_t) noexcept { + return ctx.append("null"); } template <> -std::string to_string(void const volatile *a) noexcept { +bool to_string(fmt_context &ctx, void const volatile *a) noexcept { if (a == nullptr) { - return to_string(nullptr); + return to_string(ctx, nullptr); } - return ::LIBIMP::sprintf(fmt_of, "", "p", a); + return ::LIBIMP::sprintf(ctx, fmt_of, "", "p", a); } -std::string to_string(std::tm const &a, span fstr) noexcept { +bool to_string(fmt_context &ctx, std::tm const &a, span fstr) noexcept { if (fstr.empty()) { fstr = "%Y-%m-%d %H:%M:%S"; } LIBIMP_TRY { std::ostringstream ss; ss << std::put_time(&a, as_cstr(fstr)); - return ss.str(); + return ctx.append(ss.str()); } LIBIMP_CATCH(...) { return {}; } diff --git a/src/libimp/log.cpp b/src/libimp/log.cpp index 925fc46..3b68705 100644 --- a/src/libimp/log.cpp +++ b/src/libimp/log.cpp @@ -6,19 +6,28 @@ LIBIMP_NAMESPACE_BEG_ namespace log { -std::string to_string(context &&ctx) noexcept { +bool to_string(fmt_context &f_ctx, context &&l_ctx) noexcept { constexpr static char types[] = { 'T', 'D', 'I', 'W', 'E', 'F', }; LIBIMP_TRY { - auto ms = std::chrono::time_point_cast(ctx.tp).time_since_epoch().count() % 1000; - return fmt("[", types[enum_cast(ctx.level)], "][", ctx.tp, ".", spec("03")(ms), "][", ctx.func, "] ", ctx.text); - } LIBIMP_CATCH(std::exception const &e) { - /// @remark [TBD] std::string constructor may throw an exception - return e.what(); + auto ms = std::chrono::time_point_cast(l_ctx.tp).time_since_epoch().count() % 1000; + return fmt_to(f_ctx, "[", types[enum_cast(l_ctx.level)], "][", l_ctx.tp, ".", spec("03")(ms), "][", l_ctx.func, "] ", l_ctx.text); + } LIBIMP_CATCH(std::exception const &) { + return false; } } +std::string to_string(context &&ctx) noexcept { + std::string log_txt; + fmt_context f_ctx(log_txt); + if (!to_string(f_ctx, std::move(ctx))) { + return {}; + } + f_ctx.finish(); + return log_txt; +} + printer::operator bool() const noexcept { return (objp_ != nullptr) && (vtable_ != nullptr); } diff --git a/src/libimp/system.cpp b/src/libimp/system.cpp index 43d3972..8dfb15d 100644 --- a/src/libimp/system.cpp +++ b/src/libimp/system.cpp @@ -49,10 +49,5 @@ bool operator!=(error const &lhs, error const &rhs) noexcept { return lhs.code() != rhs.code(); } -std::ostream &operator<<(std::ostream &o, error const &e) { - o << error_msg(e.code()); - return o; -} - } // namespace sys LIBIMP_NAMESPACE_END_ diff --git a/test/imp/test_imp_fmt.cpp b/test/imp/test_imp_fmt.cpp index 83df1c0..faf2f19 100644 --- a/test/imp/test_imp_fmt.cpp +++ b/test/imp/test_imp_fmt.cpp @@ -1,5 +1,6 @@ #define _CRT_SECURE_NO_WARNINGS #include +#include #include "gtest/gtest.h" @@ -13,68 +14,82 @@ TEST(fmt, spec) { } TEST(fmt, to_string) { + std::string joined; + imp::fmt_context ctx(joined); + /// @brief string - EXPECT_STREQ(imp::to_string(""), ""); - EXPECT_STREQ(imp::to_string("%what%"), "%what%"); - EXPECT_EQ(imp::to_string("%what%", "10") , " %what%"); - EXPECT_EQ(imp::to_string("%what%", "-10"), "%what% "); + EXPECT_TRUE(imp::to_string(ctx, "")); ctx.finish(); + EXPECT_EQ(joined, ""); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%")); ctx.finish(); + EXPECT_EQ(joined, "%what%"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%", "10")); ctx.finish(); + EXPECT_EQ(joined, " %what%"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, "%what%", "-10")); ctx.finish(); + EXPECT_EQ(joined, "%what% "); /// @brief character - EXPECT_EQ(imp::to_string('A'), "A"); - EXPECT_EQ(imp::to_string(L'A'), "A"); - EXPECT_EQ(imp::to_string(u'A'), "A"); - EXPECT_EQ(imp::to_string(U'A'), "A"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 'A')); ctx.finish(); + EXPECT_EQ(joined, "A"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, L'A')); ctx.finish(); + EXPECT_EQ(joined, "A"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, u'A')); ctx.finish(); + EXPECT_EQ(joined, "A"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, U'A')); ctx.finish(); + EXPECT_EQ(joined, "A"); /// @brief numeric - EXPECT_EQ(imp::to_string((signed char)123), "123"); - EXPECT_EQ(imp::to_string((signed char)-321), "-65"); - EXPECT_EQ(imp::to_string((unsigned char)123), "123"); - EXPECT_EQ(imp::to_string((unsigned char)321), "65"); - EXPECT_EQ(imp::to_string((short)123), "123"); - EXPECT_EQ(imp::to_string((short)-321), "-321"); - EXPECT_EQ(imp::to_string((unsigned short)123), "123"); - EXPECT_EQ(imp::to_string((unsigned short)321), "321"); - EXPECT_EQ(imp::to_string((short)123123), "-7949"); - EXPECT_EQ(imp::to_string((short)-321321), "6359"); - EXPECT_EQ(imp::to_string((unsigned short)123123), "57587"); - EXPECT_EQ(imp::to_string((unsigned short)321321), "59177"); - EXPECT_EQ(imp::to_string(123123), "123123"); - EXPECT_EQ(imp::to_string(-321321), "-321321"); - EXPECT_EQ(imp::to_string(123123u), "123123"); - EXPECT_EQ(imp::to_string(321321u), "321321"); - EXPECT_EQ(imp::to_string(123123ll), "123123"); - EXPECT_EQ(imp::to_string(-321321ll), "-321321"); - EXPECT_EQ(imp::to_string(123123ull), "123123"); - EXPECT_EQ(imp::to_string(321321ull), "321321"); - EXPECT_EQ(imp::to_string(123123, "x"), "1e0f3"); - EXPECT_EQ(imp::to_string(123123u, "x"), "1e0f3"); - EXPECT_EQ(imp::to_string(123123123123ll, "X"), "1CAAB5C3B3"); - EXPECT_EQ(imp::to_string(123123123123ull, "X"), "1CAAB5C3B3"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (signed char)123)); ctx.finish(); EXPECT_EQ(joined, "123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (signed char)-321)); ctx.finish(); EXPECT_EQ(joined, "-65"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned char)123)); ctx.finish(); EXPECT_EQ(joined, "123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned char)321)); ctx.finish(); EXPECT_EQ(joined, "65"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)123)); ctx.finish(); EXPECT_EQ(joined, "123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)-321)); ctx.finish(); EXPECT_EQ(joined, "-321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)123)); ctx.finish(); EXPECT_EQ(joined, "123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)321)); ctx.finish(); EXPECT_EQ(joined, "321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)123123)); ctx.finish(); EXPECT_EQ(joined, "-7949"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (short)-321321)); ctx.finish(); EXPECT_EQ(joined, "6359"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)123123)); ctx.finish(); EXPECT_EQ(joined, "57587"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (unsigned short)321321)); ctx.finish(); EXPECT_EQ(joined, "59177"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123)); ctx.finish(); EXPECT_EQ(joined, "123123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, -321321)); ctx.finish(); EXPECT_EQ(joined, "-321321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123u)); ctx.finish(); EXPECT_EQ(joined, "123123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 321321u)); ctx.finish(); EXPECT_EQ(joined, "321321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123ll)); ctx.finish(); EXPECT_EQ(joined, "123123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, -321321ll)); ctx.finish(); EXPECT_EQ(joined, "-321321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123ull)); ctx.finish(); EXPECT_EQ(joined, "123123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 321321ull)); ctx.finish(); EXPECT_EQ(joined, "321321"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123, "x")); ctx.finish(); EXPECT_EQ(joined, "1e0f3"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123u, "x")); ctx.finish(); EXPECT_EQ(joined, "1e0f3"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123123123ll, "X")); ctx.finish(); EXPECT_EQ(joined, "1CAAB5C3B3"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123123123123ull, "X")); ctx.finish(); EXPECT_EQ(joined, "1CAAB5C3B3"); /// @brief floating point - EXPECT_EQ(imp::to_string(123.123f, ".3"), "123.123"); - EXPECT_EQ(imp::to_string(123.123, "010.5"), "0123.12300"); - EXPECT_EQ(imp::to_string(123.123l, "010.6"), "123.123000"); - EXPECT_EQ(imp::to_string(1.5, "e"), "1.500000e+00"); - EXPECT_EQ(imp::to_string(1.5, "E"), "1.500000E+00"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123f, ".3")); ctx.finish(); EXPECT_EQ(joined, "123.123"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123, "010.5")); ctx.finish(); EXPECT_EQ(joined, "0123.12300"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 123.123l, "010.6")); ctx.finish(); EXPECT_EQ(joined, "123.123000"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.5, "e")); ctx.finish(); EXPECT_EQ(joined, "1.500000e+00"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.5, "E")); ctx.finish(); EXPECT_EQ(joined, "1.500000E+00"); double r = 0.0; - std::cout << imp::to_string(0.0/r) << "\n"; - std::cout << imp::to_string(1.0/r) << "\n"; - SUCCEED(); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 0.0/r)); ctx.finish(); + std::cout << joined << "\n"; + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, 1.0/r)); ctx.finish(); + std::cout << joined << "\n"; /// @brief pointer - EXPECT_EQ(imp::to_string(nullptr), "null"); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, nullptr)); ctx.finish(); EXPECT_EQ(joined, "null"); int *p = (int *)0x0f013a04; - std::cout << imp::to_string((void *)p) << "\n"; - SUCCEED(); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, (void *)p)); ctx.finish(); + std::cout << joined << "\n"; /// @brief date and time auto tp = std::chrono::system_clock::now(); auto tt = std::chrono::system_clock::to_time_t(tp); auto tm = *std::localtime(&tt); - std::cout << imp::to_string(tm) << "\n"; - EXPECT_EQ(imp::to_string(tm), imp::to_string(tp)); - SUCCEED(); + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, tm)); ctx.finish(); + std::cout << joined << "\n"; + std::string tm_str = joined; + ctx.reset(); EXPECT_TRUE(imp::to_string(ctx, tp)); ctx.finish(); + EXPECT_EQ(tm_str, joined); } TEST(fmt, fmt) { @@ -87,7 +102,7 @@ namespace { class foo {}; -std::string tag_invoke(decltype(imp::fmt_to_string), foo arg) noexcept(false) { +bool tag_invoke(decltype(imp::fmt_to), imp::fmt_context &, foo arg) noexcept(false) { throw arg; return {}; } @@ -95,5 +110,5 @@ std::string tag_invoke(decltype(imp::fmt_to_string), foo arg) noexcept(false) { } // namespace TEST(fmt, throw) { - EXPECT_THROW(imp::fmt(foo{}), foo); + EXPECT_THROW(std::ignore = imp::fmt(foo{}), foo); } \ No newline at end of file diff --git a/test/imp/test_imp_system.cpp b/test/imp/test_imp_system.cpp index b3d46c2..81a6f6d 100644 --- a/test/imp/test_imp_system.cpp +++ b/test/imp/test_imp_system.cpp @@ -26,9 +26,6 @@ TEST(system, error_code) { imp::sys::error e_obj {err}; EXPECT_EQ(err.value(), e_obj.value()); auto e_msg = imp::fmt(imp::sys::error_msg(imp::sys::error_code())); - std::stringstream ss; - ss << imp::sys::error{}; - EXPECT_EQ(e_msg, ss.str()); EXPECT_EQ(e_msg, imp::fmt(imp::sys::error())); std::cout << e_msg << "\n"; }