From d34eb1b97b3f9013aefda9aeefee2adbf0cf24f4 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:56:37 +0000 Subject: [PATCH 01/10] Attempt to reduce the binary size due to fmtlib --- src/vendor/fmt/base.h | 51 ++++++++++++------------------------- src/vendor/fmt/format-inl.h | 12 ++++----- src/vendor/fmt/format.h | 40 ++++++++++++++--------------- 3 files changed, 41 insertions(+), 62 deletions(-) diff --git a/src/vendor/fmt/base.h b/src/vendor/fmt/base.h index 3c1200058..1a70dcf07 100644 --- a/src/vendor/fmt/base.h +++ b/src/vendor/fmt/base.h @@ -10,17 +10,11 @@ #define FMT_HEADER_ONLY -#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) -# define FMT_MODULE -#endif - -#ifndef FMT_MODULE -# include // CHAR_BIT -# include // FILE -# include // memcmp +#include // CHAR_BIT +#include // FILE +#include // memcmp -# include // std::enable_if -#endif +#include // std::enable_if // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 120100 @@ -233,7 +227,7 @@ // Enable minimal optimizations for more compact code in debug mode. FMT_PRAGMA_GCC(push_options) -#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) FMT_PRAGMA_GCC(optimize("Og")) #endif FMT_PRAGMA_CLANG(diagnostic push) @@ -276,27 +270,14 @@ FMT_PRAGMA_GCC(diagnostic push) # define FMT_WIN32 0 #endif -#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 -# if defined(FMT_LIB_EXPORT) -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -# define FMT_API FMT_VISIBILITY("default") -#endif -#ifndef FMT_API -# define FMT_API -#endif - #ifndef FMT_OPTIMIZE_SIZE -# define FMT_OPTIMIZE_SIZE 0 +# define FMT_OPTIMIZE_SIZE 2 #endif // FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher // per-call binary size by passing built-in types through the extension API. #ifndef FMT_BUILTIN_TYPES -# define FMT_BUILTIN_TYPES 1 +# define FMT_BUILTIN_TYPES 0 #endif #define FMT_APPLY_VARIADIC(expr) \ @@ -354,7 +335,7 @@ template constexpr auto max_of(T a, T b) -> T { return a > b ? a : b; } -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, +FMT_NORETURN void assert_fail(const char* file, int line, const char* message); namespace detail { @@ -384,7 +365,7 @@ template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { return val; } -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, +FMT_NORETURN void assert_fail(const char* file, int line, const char* message); #if defined(FMT_ASSERT) @@ -663,7 +644,7 @@ struct formatter { /// Reports a format error at compile time or, via a `format_error` exception, /// at runtime. // This function is intentionally not constexpr to give a compile-time error. -FMT_NORETURN FMT_API void report_error(const char* message); +FMT_NORETURN void report_error(const char* message); enum class presentation_type : unsigned char { // Common specifiers: @@ -2451,11 +2432,11 @@ FMT_CONSTEXPR inline auto is_locking() -> bool { return locking::value || is_locking(); } -FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, +void vformat_to(buffer& buf, string_view fmt, format_args args, locale_ref loc = {}); #if FMT_WIN32 -FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +void vprint_mojibake(FILE*, string_view, format_args, bool); #else // format_args is passed by reference since it is defined later. inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} #endif @@ -2947,10 +2928,10 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, return buf.count(); } -FMT_API void vprint(string_view fmt, format_args args); -FMT_API void vprint(FILE* f, string_view fmt, format_args args); -FMT_API void vprintln(FILE* f, string_view fmt, format_args args); -FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); +void vprint(string_view fmt, format_args args); +void vprint(FILE* f, string_view fmt, format_args args); +void vprintln(FILE* f, string_view fmt, format_args args); +void vprint_buffered(FILE* f, string_view fmt, format_args args); /** * Formats `args` according to specifications in `fmt` and writes the output diff --git a/src/vendor/fmt/format-inl.h b/src/vendor/fmt/format-inl.h index 945cb912a..8e5ef53c4 100644 --- a/src/vendor/fmt/format-inl.h +++ b/src/vendor/fmt/format-inl.h @@ -8,13 +8,11 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#ifndef FMT_MODULE -# include -# include // errno -# include -# include -# include -#endif +#include +#include // errno +#include +#include +#include #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty diff --git a/src/vendor/fmt/format.h b/src/vendor/fmt/format.h index e5ea41728..a44daf1fe 100644 --- a/src/vendor/fmt/format.h +++ b/src/vendor/fmt/format.h @@ -978,8 +978,8 @@ class loc_value; FMT_END_EXPORT namespace detail { -FMT_API auto write_console(int fd, string_view text) -> bool; -FMT_API void print(FILE*, string_view); +auto write_console(int fd, string_view text) -> bool; +void print(FILE*, string_view); } // namespace detail namespace detail { @@ -1162,7 +1162,7 @@ template struct thousands_sep_result { }; template -FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; template inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { auto result = thousands_sep_impl(loc); @@ -1174,7 +1174,7 @@ inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { } template -FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +auto decimal_point_impl(locale_ref loc) -> Char; template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } @@ -1184,12 +1184,12 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { #ifndef FMT_HEADER_ONLY FMT_BEGIN_EXPORT -extern template FMT_API auto thousands_sep_impl(locale_ref) +extern template auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -extern template FMT_API auto thousands_sep_impl(locale_ref) +extern template auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -extern template FMT_API auto decimal_point_impl(locale_ref) -> char; -extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +extern template auto decimal_point_impl(locale_ref) -> char; +extern template auto decimal_point_impl(locale_ref) -> wchar_t; FMT_END_EXPORT #endif // FMT_HEADER_ONLY @@ -1302,7 +1302,7 @@ class utf8_to_utf16 { basic_memory_buffer buffer_; public: - FMT_API explicit utf8_to_utf16(string_view s); + explicit utf8_to_utf16(string_view s); inline operator basic_string_view() const { return {&buffer_[0], size()}; } @@ -1446,7 +1446,7 @@ inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept return r; } -FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; +auto get_cached_power(int k) noexcept -> uint128_fallback; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1496,7 +1496,7 @@ template struct decimal_fp { int exponent; }; -template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; +template auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. @@ -1735,7 +1735,7 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) } // Returns true iff the code point cp is printable. -FMT_API auto is_printable(uint32_t cp) -> bool; +auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; @@ -1985,7 +1985,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, #if FMT_USE_LOCALE // Writes a localized value. -FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, +auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; auto write_loc(basic_appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; @@ -3806,10 +3806,10 @@ template struct format_handler { // It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer&, int, const char*); -FMT_API void do_report_error(format_func func, int error_code, +void do_report_error(format_func func, int error_code, const char* message) noexcept; -FMT_API void format_error_code(buffer& out, int error_code, +void format_error_code(buffer& out, int error_code, string_view message) noexcept; template @@ -3900,7 +3900,7 @@ template class format_facet : public Locale::facet { const format_specs& specs) const -> bool; public: - static FMT_API typename Locale::id id; + static typename Locale::id id; explicit format_facet(Locale& loc); explicit format_facet(string_view sep = "", std::string grouping = "\3", @@ -4250,7 +4250,7 @@ class format_int { */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) -FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) +auto vsystem_error(int error_code, string_view fmt, format_args args) -> std::system_error; /** @@ -4287,12 +4287,12 @@ auto system_error(int error_code, format_string fmt, T&&... args) * message corresponding to the error code. * `error_code` is a system error code as given by `errno`. */ -FMT_API void format_system_error(detail::buffer& out, int error_code, +void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. -FMT_API void report_system_error(int error_code, const char* message) noexcept; +void report_system_error(int error_code, const char* message) noexcept; inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { @@ -4332,7 +4332,7 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, return buf.count(); } -FMT_API auto vformat(string_view fmt, format_args args) -> std::string; +auto vformat(string_view fmt, format_args args) -> std::string; /** * Formats `args` according to specifications in `fmt` and returns the result From 8602523eef121fb1ec69ef96f661107cb9381d05 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 5 Nov 2025 19:57:28 +0000 Subject: [PATCH 02/10] Revert FMT_BUILTIN_TYPES --- src/vendor/fmt/base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vendor/fmt/base.h b/src/vendor/fmt/base.h index 1a70dcf07..b53f17370 100644 --- a/src/vendor/fmt/base.h +++ b/src/vendor/fmt/base.h @@ -277,7 +277,7 @@ FMT_PRAGMA_GCC(diagnostic push) // FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher // per-call binary size by passing built-in types through the extension API. #ifndef FMT_BUILTIN_TYPES -# define FMT_BUILTIN_TYPES 0 +# define FMT_BUILTIN_TYPES 1 #endif #define FMT_APPLY_VARIADIC(expr) \ From 71b23330f2e9d7ae4dd332591369eb318671ca8a Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:42:25 +0000 Subject: [PATCH 03/10] Un-inline some code and instantiate some templates --- cmake/objects.cmake | 2 + src/object.hpp | 8 +-- src/processor/fingerprint.cpp | 2 +- src/ruleset_info.hpp | 2 +- src/semver.cpp | 67 ++++++++++++++++++++ src/semver.hpp | 53 +--------------- src/utils.cpp | 73 ++++++++++++++++++++++ src/utils.hpp | 114 +++------------------------------- 8 files changed, 157 insertions(+), 164 deletions(-) create mode 100644 src/semver.cpp create mode 100644 src/utils.cpp diff --git a/cmake/objects.cmake b/cmake/objects.cmake index 421ed063d..d6bc53e19 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -21,6 +21,8 @@ set(LIBDDWAF_SOURCE ${libddwaf_SOURCE_DIR}/src/dynamic_string.cpp ${libddwaf_SOURCE_DIR}/src/attribute_collector.cpp ${libddwaf_SOURCE_DIR}/src/rule.cpp + ${libddwaf_SOURCE_DIR}/src/semver.cpp + ${libddwaf_SOURCE_DIR}/src/utils.cpp ${libddwaf_SOURCE_DIR}/src/utf8.cpp ${libddwaf_SOURCE_DIR}/src/builder/action_mapper_builder.cpp ${libddwaf_SOURCE_DIR}/src/builder/matcher_builder.cpp diff --git a/src/object.hpp b/src/object.hpp index dbbcd6038..9895829c0 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -1136,13 +1136,13 @@ template <> struct object_converter { case object_type::small_string: return view.as(); case object_type::boolean: - return ddwaf::to_string(view.as()); + return ddwaf::to_string(view.as()); case object_type::uint64: - return ddwaf::to_string(view.as()); + return ddwaf::to_string(view.as()); case object_type::int64: - return ddwaf::to_string(view.as()); + return ddwaf::to_string(view.as()); case object_type::float64: - return ddwaf::to_string(view.as()); + return ddwaf::to_string(view.as()); default: break; } diff --git a/src/processor/fingerprint.cpp b/src/processor/fingerprint.cpp index 2407b0b8c..f038fcfc1 100644 --- a/src/processor/fingerprint.cpp +++ b/src/processor/fingerprint.cpp @@ -178,7 +178,7 @@ template struct unsigned_field : field_generator> { explicit unsigned_field(T input) : value(input) {} - [[nodiscard]] dynamic_string generate() { return ddwaf::to_string(value); } + [[nodiscard]] dynamic_string generate() { return ddwaf::to_string(value); } T value; }; diff --git a/src/ruleset_info.hpp b/src/ruleset_info.hpp index 13e801b58..91df86757 100644 --- a/src/ruleset_info.hpp +++ b/src/ruleset_info.hpp @@ -19,7 +19,7 @@ namespace ddwaf { -inline std::string index_to_id(unsigned idx) { return "index:" + to_string(idx); } +inline std::string index_to_id(unsigned idx) { return "index:" + to_string(idx); } enum class ruleset_info_state : uint8_t { empty, invalid, valid }; diff --git a/src/semver.cpp b/src/semver.cpp new file mode 100644 index 000000000..f05d925a2 --- /dev/null +++ b/src/semver.cpp @@ -0,0 +1,67 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +#include +#include +#include +#include +#include + +#include "semver.hpp" +#include "utils.hpp" + +namespace ddwaf { + +semantic_version::semantic_version(std::string_view version) : str_(version) +{ + // The expected version string is: xxx.yyy.zzz[-label] + // We only try to extract xxx, yyy and zzz, while discarding the label. + // Each element can be 1 to 3 digits long, but no longer. + // Any deviation from this will be rejected. + + // Major + std::size_t start = 0; + auto end = version.find('.'); + if (end == std::string_view::npos) { + throw std::invalid_argument("invalid version syntax"); + } + auto major_str = version.substr(start, end - start); + if (major_str.empty() || major_str.size() > 3 || !parse_number(major_str, major_)) { + throw std::invalid_argument("invalid major version: " + std::string{major_str}); + } + + // Minor + start = end + 1; + end = version.find('.', start); + if (end == std::string_view::npos) { + throw std::invalid_argument("invalid version syntax"); + } + auto minor_str = version.substr(start, end - start); + if (minor_str.empty() || minor_str.size() > 3 || !parse_number(minor_str, minor_)) { + throw std::invalid_argument("invalid minor version: " + std::string{minor_str}); + } + + // Patch + start = end + 1; + end = version.find('-', start); + auto patch_str = version.substr(start, end - start); + if (patch_str.empty() || patch_str.size() > 3 || !parse_number(patch_str, patch_)) { + throw std::invalid_argument("invalid patch version: " + std::string{patch_str}); + } + + number_ = major_ * 1000000 + minor_ * 1000 + patch_; +} + +bool semantic_version::parse_number(std::string_view str, uint16_t &output) +{ + if (auto [res, value] = from_string(str); res) { + output = value; + return true; + } + return false; +} + +} // namespace ddwaf diff --git a/src/semver.hpp b/src/semver.hpp index 405197954..7224a1b53 100644 --- a/src/semver.hpp +++ b/src/semver.hpp @@ -6,58 +6,16 @@ #pragma once -#include #include #include -#include #include #include -#include "fmt/core.h" -#include "utils.hpp" - namespace ddwaf { class semantic_version { public: - explicit semantic_version(std::string_view version) : str_(version) - { - // The expected version string is: xxx.yyy.zzz[-label] - // We only try to extract xxx, yyy and zzz, while discarding the label. - // Each element can be 1 to 3 digits long, but no longer. - // Any deviation from this will be rejected. - - // Major - std::size_t start = 0; - auto end = version.find('.'); - if (end == std::string_view::npos) { - throw std::invalid_argument("invalid version syntax"); - } - auto major_str = version.substr(start, end - start); - if (major_str.empty() || major_str.size() > 3 || !parse_number(major_str, major_)) { - throw std::invalid_argument("invalid major version: " + std::string{major_str}); - } - - // Minor - start = end + 1; - end = version.find('.', start); - if (end == std::string_view::npos) { - throw std::invalid_argument("invalid version syntax"); - } - auto minor_str = version.substr(start, end - start); - if (minor_str.empty() || minor_str.size() > 3 || !parse_number(minor_str, minor_)) { - throw std::invalid_argument("invalid minor version: " + std::string{minor_str}); - } + explicit semantic_version(std::string_view version); - // Patch - start = end + 1; - end = version.find('-', start); - auto patch_str = version.substr(start, end - start); - if (patch_str.empty() || patch_str.size() > 3 || !parse_number(patch_str, patch_)) { - throw std::invalid_argument("invalid patch version: " + std::string{patch_str}); - } - - number_ = major_ * 1000000 + minor_ * 1000 + patch_; - } semantic_version(semantic_version &&other) = default; semantic_version &operator=(semantic_version &&other) noexcept = default; semantic_version(const semantic_version &other) = default; @@ -99,14 +57,7 @@ class semantic_version { : str_(str), major_(major), minor_(minor), patch_(patch), number_(number) {} - static bool parse_number(std::string_view str, uint16_t &output) - { - if (auto [res, value] = from_string(str); res) { - output = value; - return true; - } - return false; - } + static bool parse_number(std::string_view str, uint16_t &output); std::string str_; uint16_t major_{0}; diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 000000000..e524fed18 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,73 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +#include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ddwaf { + +std::vector split(std::string_view str, char sep) +{ + std::vector components; + + std::size_t start = 0; + while (start < str.size()) { + const std::size_t end = str.find(sep, start); + + if (end == start) { + // Ignore zero-sized strings + start = end + 1; + continue; + } + + if (end == std::string_view::npos) { + // Last element + components.emplace_back(str.substr(start)); + start = str.size(); + } else { + components.emplace_back(str.substr(start, end - start)); + start = end + 1; + } + } + + return components; +} + +std::vector> convert_key_path( + std::span> key_path) +{ + std::vector> result; + result.reserve(key_path.size()); + + for (const auto &key : key_path) { + std::visit([&result](auto &&k) { result.emplace_back(k); }, key); + } + return result; +} + +bool string_iequals(std::string_view left, std::string_view right) +{ + return left.size() == right.size() && + std::equal(left.begin(), left.end(), right.begin(), + [](char l, char r) { return tolower(l) == tolower(r); }); +} + +template std::string to_string(T value) { return ddwaf::fmt::format("{}", value); } + +template std::string to_string(bool value); +template std::string to_string(int64_t value); +template std::string to_string(uint64_t value); +template std::string to_string(unsigned value); +template std::string to_string(double value); + +} // namespace ddwaf diff --git a/src/utils.hpp b/src/utils.hpp index e838517a2..4d6861731 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -12,9 +12,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -22,11 +20,12 @@ #include #include #include -#include #include #include #include +#include + // NOLINTBEGIN(cppcoreguidelines-macro-usage) // Convert numbers to strings #define STR_HELPER(x) #x @@ -80,67 +79,7 @@ concept has_to_chars = requires(T v) { std::to_chars(nullptr, nullptr, std::decl template concept has_from_chars = requires(T v) { std::from_chars(nullptr, nullptr, std::declval()); }; -template -StringType to_string(T value) - requires std::is_integral_v && (!std::is_same_v) && - std::is_same_v> -{ - // Maximum number of characters required to represent a 64 bit integer as a string - // 20 bytes for UINT64_MAX or INT64_MIN - static constexpr size_t max_chars = 20; - - std::array str{}; - auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value); - [[unlikely]] if (ec != std::errc()) { - return {}; - } - return {str.data(), ptr}; -} - -template - requires std::is_same_v || std::is_same_v -// XXX: add long double, though it's tricker, we don't know if it's quad-precision -// or x87 80-bit "extended precision" or even the same as double -inline constexpr std::size_t max_exp_digits = sizeof(T) == 4 ? 2 : 4; - -template -StringType to_string(T value) - requires(std::is_same_v || std::is_same_v) && - std::is_same_v> -{ - if constexpr (has_to_chars) { - static constexpr std::size_t max_chars = std::numeric_limits::digits10 + 1 + - 1 /* sign */ + 1 /* dot */ + 1 /* e */ + - 1 /* exp sign */ - + (sizeof(T) == 4 ? 2 : 4); - - std::array str{}; - auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value); - [[unlikely]] if (ec != std::errc()) { - // This is likely unreachable if the max_chars calculation is accurate - return {}; - } - return {str.data(), ptr}; - } else { - using char_type = typename StringType::value_type; - using traits_type = typename StringType::traits_type; - using allocator_type = typename StringType::allocator_type; - std::basic_ostringstream ss; - ss << std::setprecision(std::numeric_limits::digits10) << value; - return std::move(ss).str(); - } -} - -template -StringType to_string(T value) - requires std::is_same_v && - std::is_same_v> -{ - return value ? "true" : "false"; -} +template std::string to_string(T value); template std::pair from_string(std::string_view str) { @@ -163,32 +102,7 @@ template std::pair from_string(std::string_view str) return {false, {}}; } -inline std::vector split(std::string_view str, char sep) -{ - std::vector components; - - std::size_t start = 0; - while (start < str.size()) { - const std::size_t end = str.find(sep, start); - - if (end == start) { - // Ignore zero-sized strings - start = end + 1; - continue; - } - - if (end == std::string_view::npos) { - // Last element - components.emplace_back(str.substr(start)); - start = str.size(); - } else { - components.emplace_back(str.substr(start, end - start)); - start = end + 1; - } - } - - return components; -} +std::vector split(std::string_view str, char sep); // NOLINTNEXTLINE(fuchsia-multiple-inheritance) class null_ostream : public std::ostream { @@ -222,23 +136,9 @@ constexpr bool string_iequals_literal(std::string_view left, const char (&right) [](char l, char r) { return tolower(l) == r; }); } -inline bool string_iequals(std::string_view left, std::string_view right) -{ - return left.size() == right.size() && - std::equal(left.begin(), left.end(), right.begin(), - [](char l, char r) { return tolower(l) == tolower(r); }); -} +bool string_iequals(std::string_view left, std::string_view right); -inline std::vector> convert_key_path( - std::span> key_path) -{ - std::vector> result; - result.reserve(key_path.size()); - - for (const auto &key : key_path) { - std::visit([&result](auto &&k) { result.emplace_back(k); }, key); - } - return result; -} +std::vector> convert_key_path( + std::span> key_path); } // namespace ddwaf From 56460325ece153d0b5c5a03f8fb98ea95776ddcd Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:04:05 +0000 Subject: [PATCH 04/10] More instantiations and fixes --- src/processor/fingerprint.cpp | 4 +-- src/utils.cpp | 42 ++++++++++++++++++++++++++++- src/utils.hpp | 51 +---------------------------------- 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/src/processor/fingerprint.cpp b/src/processor/fingerprint.cpp index f038fcfc1..6169149a9 100644 --- a/src/processor/fingerprint.cpp +++ b/src/processor/fingerprint.cpp @@ -564,7 +564,7 @@ owned_object http_header_fingerprint::eval_impl(const unary_argument & } std::sort(unknown_headers.begin(), unknown_headers.end()); - auto unknown_header_size = unknown_headers.size(); + const uint64_t unknown_header_size = unknown_headers.size(); owned_object res; try { res = generate_fragment("hdr", alloc, string_field{known_header_bitset}, @@ -611,7 +611,7 @@ owned_object http_network_fingerprint::eval_impl(const unary_argument } } - unsigned ip_count = 0; + uint64_t ip_count = 0; if (!chosen_header_value.empty()) { // For now, count commas ++ip_count; diff --git a/src/utils.cpp b/src/utils.cpp index e524fed18..5658de685 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -4,17 +4,30 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2025 Datadog, Inc. -#include "utils.hpp" #include +#include #include #include #include +#include #include #include +#include +#include #include #include +#include + +#include "utils.hpp" + namespace ddwaf { +namespace { + +template +concept has_from_chars = requires(T v) { std::from_chars(nullptr, nullptr, std::declval()); }; + +} // namespace std::vector split(std::string_view str, char sep) { @@ -64,10 +77,37 @@ bool string_iequals(std::string_view left, std::string_view right) template std::string to_string(T value) { return ddwaf::fmt::format("{}", value); } +template std::pair from_string(std::string_view str) +{ + T result; + if constexpr (has_from_chars) { + const auto *end = str.data() + str.size(); + auto [endConv, err] = std::from_chars(str.data(), end, result); + if (err == std::errc{} && endConv == end) { + return {true, result}; + } + } else { + // NOLINTNEXTLINE(misc-const-correctness) + std::istringstream iss(std::string{str}); + iss >> result; + if (!iss.fail() && iss.eof()) { + return {true, result}; + } + } + + return {false, {}}; +} + template std::string to_string(bool value); template std::string to_string(int64_t value); template std::string to_string(uint64_t value); template std::string to_string(unsigned value); template std::string to_string(double value); +template std::pair from_string(std::string_view str); +template std::pair from_string(std::string_view str); +template std::pair from_string(std::string_view str); +template std::pair from_string(std::string_view str); +template std::pair from_string(std::string_view str); + } // namespace ddwaf diff --git a/src/utils.hpp b/src/utils.hpp index 4d6861731..0b18e550c 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -8,24 +8,17 @@ #include #include -#include #include #include #include -#include #include -#include #include -#include #include #include -#include #include #include #include -#include - // NOLINTBEGIN(cppcoreguidelines-macro-usage) // Convert numbers to strings #define STR_HELPER(x) #x @@ -73,53 +66,11 @@ template class defer { Fn fn_; }; -template -concept has_to_chars = requires(T v) { std::to_chars(nullptr, nullptr, std::declval()); }; - -template -concept has_from_chars = requires(T v) { std::from_chars(nullptr, nullptr, std::declval()); }; - template std::string to_string(T value); - -template std::pair from_string(std::string_view str) -{ - T result; - if constexpr (has_from_chars) { - const auto *end = str.data() + str.size(); - auto [endConv, err] = std::from_chars(str.data(), end, result); - if (err == std::errc{} && endConv == end) { - return {true, result}; - } - } else { - // NOLINTNEXTLINE(misc-const-correctness) - std::istringstream iss(std::string{str}); - iss >> result; - if (!iss.fail() && iss.eof()) { - return {true, result}; - } - } - - return {false, {}}; -} +template std::pair from_string(std::string_view str); std::vector split(std::string_view str, char sep); -// NOLINTNEXTLINE(fuchsia-multiple-inheritance) -class null_ostream : public std::ostream { -public: - null_ostream() = default; - ~null_ostream() override = default; - null_ostream(const null_ostream & /*unused*/) = delete; - null_ostream(null_ostream && /*unused*/) = delete; - null_ostream &operator=(const null_ostream & /*unused*/) = delete; - null_ostream &operator=(null_ostream && /*unused*/) = delete; -}; - -template const null_ostream &operator<<(null_ostream &os, const T & /*unused*/) -{ - return os; -} - template // NOLINTNEXTLINE(modernize-avoid-c-arrays,readability-named-parameter) constexpr std::array make_array(const char (&str)[N], std::index_sequence) From 5a10376c9ba49bf4aa0836977dd1210fb12a8ec6 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:04:51 +0000 Subject: [PATCH 05/10] Small change --- src/vendor/fmt/format-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vendor/fmt/format-inl.h b/src/vendor/fmt/format-inl.h index 8e5ef53c4..dc40b5743 100644 --- a/src/vendor/fmt/format-inl.h +++ b/src/vendor/fmt/format-inl.h @@ -153,7 +153,7 @@ template format_facet::format_facet(Locale& loc) { #if FMT_USE_LOCALE template <> -FMT_API FMT_FUNC auto format_facet::do_put( +FMT_FUNC auto format_facet::do_put( appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); From 77f97eb49a85c397bdfe6c587d2e3e888cd1b644 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:31:44 +0000 Subject: [PATCH 06/10] Reduce inlining of fmtlib --- cmake/objects.cmake | 1 + src/vendor/fmt/base.h | 2 -- src/vendor/fmt/format.cc | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/vendor/fmt/format.cc diff --git a/cmake/objects.cmake b/cmake/objects.cmake index d6bc53e19..b1bc2116b 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -93,6 +93,7 @@ set(LIBDDWAF_SOURCE ${libddwaf_SOURCE_DIR}/src/transformer/css_decode.cpp ${libddwaf_SOURCE_DIR}/src/transformer/html_entity_decode.cpp ${libddwaf_SOURCE_DIR}/src/transformer/js_decode.cpp + ${libddwaf_SOURCE_DIR}/src/vendor/fmt/format.cc ${libddwaf_SOURCE_DIR}/src/vendor/radixlib/radixlib.c ${libddwaf_SOURCE_DIR}/src/vendor/lua-aho-corasick/ac_fast.cxx ${libddwaf_SOURCE_DIR}/src/vendor/lua-aho-corasick/ac_slow.cxx diff --git a/src/vendor/fmt/base.h b/src/vendor/fmt/base.h index b53f17370..88dd31e14 100644 --- a/src/vendor/fmt/base.h +++ b/src/vendor/fmt/base.h @@ -8,8 +8,6 @@ #ifndef FMT_BASE_H_ #define FMT_BASE_H_ -#define FMT_HEADER_ONLY - #include // CHAR_BIT #include // FILE #include // memcmp diff --git a/src/vendor/fmt/format.cc b/src/vendor/fmt/format.cc new file mode 100644 index 000000000..3202afc7c --- /dev/null +++ b/src/vendor/fmt/format.cc @@ -0,0 +1,43 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/format-inl.h" + +FMT_BEGIN_NAMESPACE + +#if FMT_USE_LOCALE +template locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! +template auto locale_ref::get() const -> std::locale; +#endif + +namespace detail { + +template auto dragonbox::to_decimal(float x) noexcept + -> dragonbox::decimal_fp; +template auto dragonbox::to_decimal(double x) noexcept + -> dragonbox::decimal_fp; + +// Explicit instantiations for char. + +template auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +template auto decimal_point_impl(locale_ref) -> char; + +// DEPRECATED! +template void buffer::append(const char*, const char*); + +// Explicit instantiations for wchar_t. + +template auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +template auto decimal_point_impl(locale_ref) -> wchar_t; + +// DEPRECATED! +template void buffer::append(const wchar_t*, const wchar_t*); + +} // namespace detail +FMT_END_NAMESPACE From 955b11daca65856e8302378f79d3520ab3c0c032 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:01:17 +0000 Subject: [PATCH 07/10] Enable LTO on macos --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00efbd515..9e7646da8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,7 +79,7 @@ jobs: - name: Create Build Directory run: cmake -E make_directory ${{ github.workspace }}/build ${{ github.workspace }}/packages - name: Generating Build Scripts - run: cmake -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} -DCPACK_PACKAGE_DIRECTORY=${{ github.workspace }}/packages ${{ github.workspace }} + run: cmake -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBDDWAF_ENABLE_LTO=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} -DCPACK_PACKAGE_DIRECTORY=${{ github.workspace }}/packages ${{ github.workspace }} working-directory: ${{ github.workspace }}/build - name: Build Binaries run: cmake --build . --config RelWithDebInfo --verbose --target all --target waf_test -j $(getconf _NPROCESSORS_ONLN) From e88d389946f0f2c09202ba8973552cf7dbb24506 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Thu, 6 Nov 2025 19:27:37 +0000 Subject: [PATCH 08/10] Achieve 100% test coverage for src/utils.cpp (libddwaf) (#481) Co-authored-by: datadog-datadog-prod-us1[bot] <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> --- tests/unit/utils_test.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/utils_test.cpp b/tests/unit/utils_test.cpp index 625ab516d..2864c6e93 100644 --- a/tests/unit/utils_test.cpp +++ b/tests/unit/utils_test.cpp @@ -175,6 +175,12 @@ TEST(TestUtils, Split) EXPECT_VEC(ddwaf::split("a,b,c,d,e,f,g", ','), "a", "b", "c", "d", "e", "f", "g"); } +TEST(TestUtils, SplitEmpty) +{ + auto result = ddwaf::split("", '|'); + EXPECT_TRUE(result.empty()); +} + TEST(TestUtils, ConvertKeyPath) { std::vector> input{"root", "key", 0, -1, "leaf"}; @@ -183,4 +189,11 @@ TEST(TestUtils, ConvertKeyPath) EXPECT_EQ(converted, expected); } +TEST(TestUtils, ConvertKeyPathEmpty) +{ + std::vector> input; + auto converted = convert_key_path(input); + EXPECT_TRUE(converted.empty()); +} + } // namespace From 5fb6717b8c4386288149a2aba833102a5fac87e4 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:27:30 +0000 Subject: [PATCH 09/10] Remove dead cmake code --- cmake/shared.cmake | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cmake/shared.cmake b/cmake/shared.cmake index cf86991e3..42964059a 100644 --- a/cmake/shared.cmake +++ b/cmake/shared.cmake @@ -70,14 +70,4 @@ elseif (MSVC) PUBLIC ${LIBDDWAF_INTERFACE_LIBRARIES}) install(FILES $ DESTINATION lib OPTIONAL) -elseif (MINGW) - target_link_libraries(libddwaf_shared PUBLIC ${LIBDDWAF_INTERFACE_LIBRARIES}) - target_link_libraries(libddwaf_shared PRIVATE - $<$:-flto> - -Wl,--no-undefined - -Wl,-version-script=${libddwaf_SOURCE_DIR}/libddwaf.version - -Wl,--build-id=0x${BUILD_ID} - ${LIBDDWAF_PRIVATE_LIBRARIES} - -static-libstdc++ - glibc_compat_time64 glibc_compat_math) endif() From ddb4949371c8adaa57d1dedff8e8fde9b7b11fd0 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:48:30 +0000 Subject: [PATCH 10/10] Simplify fmt changes - address review comment --- cmake/objects.cmake | 2 +- src/vendor/fmt/base.h | 31 ++++++++++++++++++++++------ src/vendor/fmt/format-inl.h | 14 +++++++------ src/vendor/fmt/format.cc | 20 +++++++++---------- src/vendor/fmt/format.h | 40 ++++++++++++++++++------------------- 5 files changed, 64 insertions(+), 43 deletions(-) diff --git a/cmake/objects.cmake b/cmake/objects.cmake index b1bc2116b..38ec40915 100644 --- a/cmake/objects.cmake +++ b/cmake/objects.cmake @@ -150,7 +150,7 @@ function(gen_objects target_name) target_include_directories(${target_name} PUBLIC ${LIBDDWAF_PUBLIC_INCLUDES}) target_include_directories(${target_name} PRIVATE ${LIBDDWAF_PRIVATE_INCLUDES}) - target_compile_definitions(${target_name} PRIVATE UTF8PROC_STATIC=1) + target_compile_definitions(${target_name} PRIVATE UTF8PROC_STATIC=1 FMT_OPTIMIZE_SIZE=2) if (MSVC) target_compile_definitions(${target_name} PRIVATE NOMINMAX) endif() diff --git a/src/vendor/fmt/base.h b/src/vendor/fmt/base.h index 88dd31e14..f9dfbd101 100644 --- a/src/vendor/fmt/base.h +++ b/src/vendor/fmt/base.h @@ -8,11 +8,17 @@ #ifndef FMT_BASE_H_ #define FMT_BASE_H_ -#include // CHAR_BIT -#include // FILE -#include // memcmp +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // memcmp -#include // std::enable_if +# include // std::enable_if +#endif // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 120100 @@ -225,7 +231,7 @@ // Enable minimal optimizations for more compact code in debug mode. FMT_PRAGMA_GCC(push_options) -#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) FMT_PRAGMA_GCC(optimize("Og")) #endif FMT_PRAGMA_CLANG(diagnostic push) @@ -268,8 +274,21 @@ FMT_PRAGMA_GCC(diagnostic push) # define FMT_WIN32 0 #endif +#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 +# if defined(FMT_LIB_EXPORT) +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") +#endif +#ifndef FMT_API +# define FMT_API +#endif + #ifndef FMT_OPTIMIZE_SIZE -# define FMT_OPTIMIZE_SIZE 2 +# define FMT_OPTIMIZE_SIZE 0 #endif // FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher diff --git a/src/vendor/fmt/format-inl.h b/src/vendor/fmt/format-inl.h index dc40b5743..945cb912a 100644 --- a/src/vendor/fmt/format-inl.h +++ b/src/vendor/fmt/format-inl.h @@ -8,11 +8,13 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include -#include // errno -#include -#include -#include +#ifndef FMT_MODULE +# include +# include // errno +# include +# include +# include +#endif #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty @@ -153,7 +155,7 @@ template format_facet::format_facet(Locale& loc) { #if FMT_USE_LOCALE template <> -FMT_FUNC auto format_facet::do_put( +FMT_API FMT_FUNC auto format_facet::do_put( appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); diff --git a/src/vendor/fmt/format.cc b/src/vendor/fmt/format.cc index 3202afc7c..526082e34 100644 --- a/src/vendor/fmt/format.cc +++ b/src/vendor/fmt/format.cc @@ -10,34 +10,34 @@ FMT_BEGIN_NAMESPACE #if FMT_USE_LOCALE -template locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! -template auto locale_ref::get() const -> std::locale; +template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! +template FMT_API auto locale_ref::get() const -> std::locale; #endif namespace detail { -template auto dragonbox::to_decimal(float x) noexcept +template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; -template auto dragonbox::to_decimal(double x) noexcept +template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; // Explicit instantiations for char. -template auto thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template auto decimal_point_impl(locale_ref) -> char; +template FMT_API auto decimal_point_impl(locale_ref) -> char; // DEPRECATED! -template void buffer::append(const char*, const char*); +template FMT_API void buffer::append(const char*, const char*); // Explicit instantiations for wchar_t. -template auto thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template auto decimal_point_impl(locale_ref) -> wchar_t; +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; // DEPRECATED! -template void buffer::append(const wchar_t*, const wchar_t*); +template FMT_API void buffer::append(const wchar_t*, const wchar_t*); } // namespace detail FMT_END_NAMESPACE diff --git a/src/vendor/fmt/format.h b/src/vendor/fmt/format.h index a44daf1fe..e5ea41728 100644 --- a/src/vendor/fmt/format.h +++ b/src/vendor/fmt/format.h @@ -978,8 +978,8 @@ class loc_value; FMT_END_EXPORT namespace detail { -auto write_console(int fd, string_view text) -> bool; -void print(FILE*, string_view); +FMT_API auto write_console(int fd, string_view text) -> bool; +FMT_API void print(FILE*, string_view); } // namespace detail namespace detail { @@ -1162,7 +1162,7 @@ template struct thousands_sep_result { }; template -auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; template inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { auto result = thousands_sep_impl(loc); @@ -1174,7 +1174,7 @@ inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { } template -auto decimal_point_impl(locale_ref loc) -> Char; +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } @@ -1184,12 +1184,12 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { #ifndef FMT_HEADER_ONLY FMT_BEGIN_EXPORT -extern template auto thousands_sep_impl(locale_ref) +extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -extern template auto thousands_sep_impl(locale_ref) +extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -extern template auto decimal_point_impl(locale_ref) -> char; -extern template auto decimal_point_impl(locale_ref) -> wchar_t; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; FMT_END_EXPORT #endif // FMT_HEADER_ONLY @@ -1302,7 +1302,7 @@ class utf8_to_utf16 { basic_memory_buffer buffer_; public: - explicit utf8_to_utf16(string_view s); + FMT_API explicit utf8_to_utf16(string_view s); inline operator basic_string_view() const { return {&buffer_[0], size()}; } @@ -1446,7 +1446,7 @@ inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept return r; } -auto get_cached_power(int k) noexcept -> uint128_fallback; +FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1496,7 +1496,7 @@ template struct decimal_fp { int exponent; }; -template auto to_decimal(T x) noexcept -> decimal_fp; +template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. @@ -1735,7 +1735,7 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) } // Returns true iff the code point cp is printable. -auto is_printable(uint32_t cp) -> bool; +FMT_API auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; @@ -1985,7 +1985,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, #if FMT_USE_LOCALE // Writes a localized value. -auto write_loc(appender out, loc_value value, const format_specs& specs, +FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; auto write_loc(basic_appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; @@ -3806,10 +3806,10 @@ template struct format_handler { // It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer&, int, const char*); -void do_report_error(format_func func, int error_code, +FMT_API void do_report_error(format_func func, int error_code, const char* message) noexcept; -void format_error_code(buffer& out, int error_code, +FMT_API void format_error_code(buffer& out, int error_code, string_view message) noexcept; template @@ -3900,7 +3900,7 @@ template class format_facet : public Locale::facet { const format_specs& specs) const -> bool; public: - static typename Locale::id id; + static FMT_API typename Locale::id id; explicit format_facet(Locale& loc); explicit format_facet(string_view sep = "", std::string grouping = "\3", @@ -4250,7 +4250,7 @@ class format_int { */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) -auto vsystem_error(int error_code, string_view fmt, format_args args) +FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) -> std::system_error; /** @@ -4287,12 +4287,12 @@ auto system_error(int error_code, format_string fmt, T&&... args) * message corresponding to the error code. * `error_code` is a system error code as given by `errno`. */ -void format_system_error(detail::buffer& out, int error_code, +FMT_API void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. -void report_system_error(int error_code, const char* message) noexcept; +FMT_API void report_system_error(int error_code, const char* message) noexcept; inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { @@ -4332,7 +4332,7 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, return buf.count(); } -auto vformat(string_view fmt, format_args args) -> std::string; +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; /** * Formats `args` according to specifications in `fmt` and returns the result