From 8e3da9da2c7e238baca1c8afb54f5241fdfd423e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 30 Aug 2024 09:07:07 -0700 Subject: [PATCH] Improve binary size --- include/fmt/format.h | 147 +++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 75 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 93fa6600..c29ff9a0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1343,31 +1343,39 @@ FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) return detail::copy_noinline(buffer, buffer + num_digits, out); } -template -FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) -> Char* { - buffer += num_digits; - Char* end = buffer; +template +FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, + int size, bool upper = false) -> Char* { + out += size; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); - } while ((value >>= BASE_BITS) != 0); - return end; + unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= base_bits) != 0); + return out; } -template +FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, + int num_digits, bool upper = false) -> Char* { + do_format_base2e(base_bits, out, value, num_digits, upper); + return out + num_digits; +} + +template ::value)> -FMT_CONSTEXPR inline auto format_uint(OutputIt out, UInt value, int num_digits, - bool upper = false) -> OutputIt { +FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, + int num_digits, bool upper = false) + -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { - format_uint(ptr, value, num_digits, upper); + format_base2e(base_bits, ptr, value, num_digits, upper); return out; } - // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1] = {}; - format_uint(buffer, value, num_digits, upper); + // Make buffer large enough for any base. + char buffer[num_bits()] = {}; + format_base2e(base_bits, buffer, value, num_digits, upper); return detail::copy_noinline(buffer, buffer + num_digits, out); } @@ -1785,7 +1793,7 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); + return format_base2e(4, it, value, num_digits); }; return specs ? write_padded(out, *specs, size, write) : base_iterator(out, write(reserve(out, size))); @@ -1861,7 +1869,7 @@ auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { *out++ = static_cast(prefix); Char buf[width]; fill_n(buf, width, static_cast('0')); - format_uint<4>(buf, cp, width); + format_base2e(4, buf, cp, width); return copy(buf, buf + width, out); } @@ -1981,32 +1989,6 @@ template struct write_int_data { } }; -// Writes an integer in the format -// -// where are written by write_digits(it). -// prefix contains chars in three lower bytes and the size in the fourth byte. -template -FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, - unsigned prefix, - const format_specs& specs, - W write_digits) -> OutputIt { - // Slightly faster check for specs.width == 0 && specs.precision == -1. - if ((specs.width | (specs.precision + 1)) == 0) { - auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - return base_iterator(out, write_digits(it)); - } - auto data = write_int_data(num_digits, prefix, specs); - return write_padded( - out, specs, data.size, [=](reserve_iterator it) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - it = detail::fill_n(it, data.padding, static_cast('0')); - return write_digits(it); - }); -} - template class digit_grouping { private: std::string grouping_; @@ -2097,7 +2079,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); - format_uint<4, char>(appender(buffer), value, num_digits, specs.upper()); + format_base2e(4, appender(buffer), value, num_digits, specs.upper()); break; case presentation_type::oct: num_digits = count_digits<3>(value); @@ -2105,13 +2087,13 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // is not greater than the number of digits. if (specs.alt() && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); - format_uint<3, char>(appender(buffer), value, num_digits); + format_base2e(3, appender(buffer), value, num_digits); break; case presentation_type::bin: if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); num_digits = count_digits<1>(value); - format_uint<1, char>(appender(buffer), value, num_digits); + format_base2e(1, appender(buffer), value, num_digits); break; case presentation_type::chr: return write_char(out, static_cast(value), specs); @@ -2184,6 +2166,12 @@ template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); + + constexpr int buffer_size = num_bits(); + char buffer[buffer_size] = {}; + const char* begin = nullptr; + const char* end = buffer + buffer_size; + auto abs_value = arg.abs_value; auto prefix = arg.prefix; switch (specs.type()) { @@ -2191,46 +2179,53 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { - int num_digits = count_digits(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits); - }); - } - case presentation_type::hex: { + case presentation_type::dec: + begin = do_format_decimal(buffer, abs_value, buffer_size); + break; + case presentation_type::hex: + begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); - int num_digits = count_digits<4>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, specs.upper()); - }); - } + break; case presentation_type::oct: { - int num_digits = count_digits<3>(abs_value); + begin = do_format_base2e(3, buffer, abs_value, buffer_size); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. + auto num_digits = end - begin; if (specs.alt() && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); + break; } - case presentation_type::bin: { + case presentation_type::bin: + begin = do_format_base2e(1, buffer, abs_value, buffer_size); if (specs.alt()) prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); - int num_digits = count_digits<1>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } + break; case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); } + + // Write an integer in the format + // + // prefix contains chars in three lower bytes and the size in the fourth byte. + int num_digits = static_cast(end - begin); + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return base_iterator(out, copy(begin, end, it)); + } + auto data = write_int_data(num_digits, prefix, specs); + return write_padded( + out, specs, data.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, data.padding, static_cast('0')); + return copy(begin, end, it); + }); } + template FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, write_int_arg arg, @@ -2238,6 +2233,7 @@ FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, -> OutputIt { return write_int(out, arg, specs); } + template ::value && !std::is_same::value && @@ -2249,6 +2245,7 @@ FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, return write_int_noinline(out, make_write_int_arg(value, specs.sign()), specs); } + // An inlined version of write used in format string compilation. template ::value && @@ -3121,7 +3118,7 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, char xdigits[num_bits() / 4]; detail::fill_n(xdigits, sizeof(xdigits), '0'); - format_uint<4>(xdigits, f.f, num_xdigits, specs.upper()); + format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); // Remove zero tail while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;