diff --git a/include/fmt/format.h b/include/fmt/format.h index 79bf5256..fc649f48 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -794,13 +794,13 @@ inline auto code_point_index(string_view s, size_t n) -> size_t { size_t result = s.size(); const char* begin = s.begin(); for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { - if (n != 0) { - --n; - return true; - } - result = to_unsigned(sv.begin() - begin); - return false; - }); + if (n != 0) { + --n; + return true; + } + result = to_unsigned(sv.begin() - begin); + return false; + }); return result; } @@ -2113,23 +2113,66 @@ template class digit_grouping { } }; +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, const format_specs& specs, const digit_grouping& grouping) -> OutputIt { static_assert(std::is_same, UInt>::value, ""); - int num_digits = count_digits(value); char digits[40]; - format_decimal(digits, value, num_digits); - unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + int num_digits; + + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + num_digits = count_digits(value); + format_decimal(digits, value, num_digits); + break; + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + num_digits = count_digits<4>(value); + format_uint<4, Char>(digits, value, num_digits, upper); + break; + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_uint<1, Char>(digits, value, num_digits); + break; + } + case presentation_type::oct: { + num_digits = count_digits<3>(value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // 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>(digits, value, num_digits); + break; + } + case presentation_type::chr: + return write_char(out, static_cast(value), specs); + default: + throw_format_error("invalid format specifier"); + } + + unsigned size = to_unsigned((prefix != 0 ? prefix >> 24 : 0) + num_digits + grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) { - char sign = static_cast(prefix); - *it++ = static_cast(sign); - } + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); }); } @@ -2143,11 +2186,6 @@ inline auto write_loc(OutputIt, loc_value, const format_specs&, return false; } -FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { - prefix |= prefix != 0 ? value << 8 : value; - prefix += (1u + (value > 0xff ? 1 : 0)) << 24; -} - template struct write_int_arg { UInt abs_value; unsigned prefix; diff --git a/test/format-test.cc b/test/format-test.cc index 34eb28a3..6eaab7c6 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1333,6 +1333,9 @@ TEST(format_test, format_oct) { TEST(format_test, format_int_locale) { EXPECT_EQ("1234", fmt::format("{:L}", 1234)); + EXPECT_EQ("7,5bc,d15", fmt::format(std::locale("en_US.UTF-8"), "{:Lx}", 123456789)); + EXPECT_EQ("-0b111,010,110,111,100,110,100,010,101", fmt::format(std::locale("en_US.UTF-8"), "{:#Lb}", -123456789)); + EXPECT_EQ(" 24", fmt::format(std::locale("en_US.UTF-8"), "{:10Lo}", 20)); } TEST(format_test, format_float) {