#pragma once #include #include #include #include #include #include #include "arena_allocator.hpp" /** * @brief Runtime printf-style formatting with arena allocation optimization. * * This function provides familiar printf-style formatting with intelligent * optimization for arena allocation. It attempts single-pass formatting by * speculatively using available arena space, falling back to two-pass * formatting only when necessary. * * The function uses an optimized allocation strategy: * 1. **Single-pass attempt**: Try to format directly into available arena space * 2. **Fallback to two-pass**: If formatting doesn't fit, measure required size * and allocate exactly what's needed * * ## Supported Format Specifiers: * All standard printf format specifiers are supported: * - **Integers**: %d, %i, %u, %x, %X, %o, %ld, %lld, etc. * - **Floating point**: %f, %e, %E, %g, %G, %.2f, etc. * - **Strings**: %s, %.*s, etc. * - **Characters**: %c * - **Pointers**: %p * - **Width/precision**: %10d, %-10s, %.2f, %*.*s, etc. * * ## Performance Characteristics: * - **Optimistic single-pass**: Often avoids the cost of measuring format size * - **Arena allocation**: Uses fast arena allocation (~1ns vs ~20-270ns for * malloc) * - **Memory efficient**: Returns unused space to arena via realloc() * - **Fallback safety**: Two-pass approach handles any format that doesn't fit * * @param arena Arena allocator for memory management * @param fmt Printf-style format string * @param ... Variable arguments matching format specifiers * @return std::string_view pointing to arena-allocated formatted string * @note Aborts program on formatting errors (never returns invalid data) * @note GCC format attribute enables compile-time format string validation * * ## Usage Examples: * ```cpp * ArenaAllocator arena(1024); * * // Basic formatting * auto msg = format(arena, "Hello %s!", "World"); * // msg == "Hello World!" * * // Numeric formatting with precision * auto value = format(arena, "Pi: %.3f", 3.14159); * // value == "Pi: 3.142" * * // Mixed types with width/alignment * auto table = format(arena, "%-10s %5d %8.2f", "Item", 42, 99.95); * // table == "Item 42 99.95" * * // Error messages * auto error = format(arena, "Error %d: %s (line %d)", 404, "Not found", 123); * // error == "Error 404: Not found (line 123)" * ``` * * ## When to Use: * - **Printf familiarity**: When you prefer printf-style format strings * - **Runtime flexibility**: Format strings from variables, config, or user * input * - **Complex formatting**: Precision, width, alignment, padding * - **Debugging**: Quick formatted output for logging/debugging * - **Mixed precision**: Different numeric precision requirements * * ## When to Use static_format() Instead: * - **Hot paths**: Performance-critical code where every nanosecond counts * - **Simple concatenation**: Basic string + number + string combinations * - **Compile-time optimization**: When all types/values known at compile time * - **Template contexts**: Where compile-time buffer sizing is beneficial * * ## Optimization Details: * The function uses `ArenaAllocator::allocate_remaining_space()` to claim all * available arena space and attempt formatting. If successful, it shrinks the * allocation to the actual size used. If formatting fails (doesn't fit), it * falls back to the traditional two-pass approach: measure size, allocate * exactly, then format. * * This strategy optimizes for the common case where available arena space is * sufficient, while maintaining correctness for all cases. */ std::string_view format(ArenaAllocator &arena, const char *fmt, ...) __attribute__((format(printf, 2, 3))); namespace detail { template struct StringTerm { explicit constexpr StringTerm(const char *s) : s(s) {} static constexpr int kMaxLength = kLen; void write(char *&buf) const { std::memcpy(buf, s, kLen); buf += kLen; } private: const char *s; }; template constexpr StringTerm term(const char (&array)[kLen]) { return StringTerm{array}; } template constexpr int decimal_length(IntType x) { static_assert(std::is_integral_v, "decimal_length requires integral type"); if constexpr (std::is_signed_v) { // Handle negative values by using unsigned equivalent using Unsigned = std::make_unsigned_t; // Safe conversion: cast to unsigned first, then negate in unsigned // arithmetic auto abs_x = x < 0 ? -static_cast(x) : static_cast(x); int result = 0; do { ++result; abs_x /= 10; } while (abs_x); return result; } else { int result = 0; do { ++result; x /= 10; } while (x); return result; } } template struct IntTerm { static constexpr bool kSigned = std::is_signed_v; using Unsigned = std::make_unsigned_t; explicit constexpr IntTerm(IntType v) : v(v) {} static constexpr int kMaxLength = decimal_length(Unsigned(-1)) + (kSigned ? 1 : 0); void write(char *&buf) const { char itoa_buf[kMaxLength]; auto x = static_cast(v); if constexpr (kSigned) { if (v < 0) { *buf++ = '-'; x = -static_cast(v); } } int i = kMaxLength; do { itoa_buf[--i] = static_cast('0' + (x % 10)); x /= 10; } while (x); while (i < kMaxLength) { *buf++ = itoa_buf[i++]; } } private: IntType v; }; template constexpr IntTerm term(IntType s) { return IntTerm{s}; } struct DoubleTerm { explicit constexpr DoubleTerm(double s) : s(s) {} static constexpr int kMaxLength = 24; void write(char *&buf) const; private: double s; }; // Variable template for compile-time max length access template inline constexpr int max_decimal_length_v = decltype(term(T{}))::kMaxLength; inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); } } // namespace detail /** * @brief Compile-time optimized formatting for high-performance code paths. * * This function provides ultra-fast string formatting by calculating buffer * sizes at compile time and using specialized term handlers for each type. * It's designed for performance-critical code where formatting overhead * matters. * * Unlike the runtime `format()` function, `static_format()` processes all * arguments at compile time to determine exact memory requirements and uses * optimized term writers for maximum speed. * * ## Supported Types: * - **String literals**: C-style string literals and arrays * - **Integers**: All integral types (int, int64_t, uint32_t, etc.) * - **Floating point**: double (uses high-precision Grisu2 algorithm) * - **Custom types**: Via specialization of `detail::term()` * * ## Performance Characteristics: * - **Compile-time buffer sizing**: Buffer size calculated at compile time (no * runtime measurement) * - **Optimized arena allocation**: Uses pre-calculated exact buffer sizes with * arena allocator * - **Specialized type handling**: Fast paths for common types via template * specialization * - **Memory efficient**: Uses arena.realloc() to return unused space to the * arena * * @tparam Ts Types of the arguments to format (auto-deduced) * @param arena Arena allocator for memory management * @param ts Arguments to format - can be string literals, integers, doubles * @return std::string_view pointing to arena-allocated formatted string * * ## Usage Examples: * ```cpp * ArenaAllocator arena(1024); * * // String concatenation * auto result1 = static_format(arena, "Hello ", "World", "!"); * // result1 == "Hello World!" * * // Mixed types * auto result2 = static_format(arena, "Count: ", 42, ", Rate: ", 3.14); * // result2 == "Count: 42, Rate: 3.14" * * // Error messages * auto error = static_format(arena, "Error ", 404, ": ", "Not found"); * // error == "Error 404: Not found" * ``` * * ## When to Use: * - **Hot paths**: Performance-critical code where formatting speed matters * - **Known types**: When argument types are known at compile time * - **Simple formatting**: Concatenation and basic type conversion * - **Template code**: Where compile-time optimization is beneficial * * ## When to Use format() Instead: * - **Printf-style formatting**: When you need format specifiers like "%d", * "%.2f" * - **Runtime flexibility**: When format strings come from variables/config * - **Complex formatting**: When you need padding, precision, etc. * - **Convenience**: For quick debugging or non-critical paths * * @note All arguments are passed by forwarding reference for optimal * performance * @note Memory is arena-allocated and automatically sized to exact requirements * @note Compile-time errors occur if unsupported types are used * @note This function is constexpr-friendly and optimizes well in release * builds */ template std::string_view static_format(ArenaAllocator &arena, Ts &&...ts) { constexpr int upper_bound = (decltype(detail::term(ts))::kMaxLength + ...); char *result = arena.allocate(upper_bound); char *buf = result; (detail::term(ts).write(buf), ...); const int size = static_cast(buf - result); return std::string_view( arena.realloc(result, upper_bound, upper_bound - size), static_cast(size)); }