diff --git a/include/fmt/format.h b/include/fmt/format.h index 5398a23a..93c8698b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1393,56 +1393,74 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, }); } +template class digit_grouping { + private: + thousands_sep_result sep_; + std::string::const_iterator group_; + int pos_; + + public: + explicit digit_grouping(locale_ref loc) { + if (loc) { + sep_ = thousands_sep(loc); + reset(); + } else { + sep_.thousands_sep = Char(); + } + } + + void reset() { + group_ = sep_.grouping.begin(); + pos_ = 0; + } + + // Returns the next digit group separator position. + int next() { + if (!sep_.thousands_sep) return max_value(); + if (group_ == sep_.grouping.end()) return pos_ += sep_.grouping.back(); + if (*group_ <= 0 || *group_ == max_value()) return max_value(); + pos_ += *group_++; + return pos_; + } + + int count_separators(int num_digits) { + int count = 0; + while (num_digits > next()) ++count; + reset(); + return count; + } + + Char separator() const { return sep_.thousands_sep; } +}; + template auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, const basic_format_specs& specs, locale_ref loc) -> bool { static_assert(std::is_same, UInt>::value, ""); - const auto sep_size = 1; - auto ts = thousands_sep(loc); - if (!ts.thousands_sep) return false; int num_digits = count_digits(value); - int size = num_digits, n = num_digits; - const std::string& groups = ts.grouping; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && n > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - n -= *group; - ++group; - } - if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); char digits[40]; format_decimal(digits, value, num_digits); - basic_memory_buffer buffer; - if (prefix != 0) ++size; - const auto usize = to_unsigned(size); - buffer.resize(usize); - basic_string_view s(&ts.thousands_sep, sep_size); - // Index of a decimal digit with the least significant digit having index 0. - int digit_index = 0; - group = groups.cbegin(); - auto p = buffer.data() + size - 1; - for (int i = num_digits - 1; i > 0; --i) { - *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - continue; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; + + auto grouping = digit_grouping(loc); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + auto buffer = basic_memory_buffer(); + buffer.resize(size); + int separator_pos = grouping.next(); + auto p = buffer.data() + size; + for (int i = 0; i < num_digits; ++i) { + if (i == separator_pos) { + *--p = grouping.separator(); + separator_pos = grouping.next(); } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); - p -= s.size(); + *--p = static_cast(digits[num_digits - i - 1]); } - *p-- = static_cast(*digits); - if (prefix != 0) *p = static_cast(prefix); - auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, [=](reserve_iterator it) { - return copy_str(data, data + size, it); - }); + if (prefix != 0) *--p = static_cast(prefix); + out = write_padded(out, specs, size, size, + [=](reserve_iterator it) { + return copy_str(p, p + size, it); + }); return true; } @@ -1669,7 +1687,7 @@ inline auto write_significand(OutputIt out, const char* significand, template auto write_float(OutputIt out, const DecimalFP& fp, const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) -> OutputIt { + locale_ref loc) -> OutputIt { auto significand = fp.significand; int significand_size = get_significand_size(fp); static const Char zero = static_cast('0'); @@ -1677,6 +1695,9 @@ auto write_float(OutputIt out, const DecimalFP& fp, size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); using iterator = reserve_iterator; + Char decimal_point = + fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + int output_exp = fp.exponent + significand_size - 1; auto use_exp_format = [=]() { if (fspecs.format == float_format::exp) return true; @@ -1808,10 +1829,8 @@ auto write(OutputIt out, T value, basic_format_specs specs, fspecs.use_grisu = is_fast_float(); int exp = format_float(promote_float(value), precision, fspecs, buffer); fspecs.precision = precision; - Char point = - fspecs.locale ? decimal_point(loc) : static_cast('.'); auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, point); + return write_float(out, fp, specs, fspecs, loc); } template OutputIt { return write_nonfinite(out, std::isinf(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, static_cast('.')); + return write_float(out, dec, specs, fspecs, {}); } template