From 65d47e5e099b96249b2b253f1584c7ae37370748 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 9 Sep 2013 06:51:03 -0700 Subject: [PATCH] Add FormatInt for fast stand-alone integer formatting. --- format.cc | 12 ++++++------ format.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/format.cc b/format.cc index 92791f7f..15c115f1 100644 --- a/format.cc +++ b/format.cc @@ -108,14 +108,14 @@ struct CharTraits { swprintf(buffer, size, format, width, precision, value); } }; +} -const char DIGITS[] = +const char fmt::internal::DIGITS[] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; -} void fmt::internal::ReportUnknownType(char code, const char *type) { if (std::isprint(static_cast(code))) { @@ -154,8 +154,8 @@ void fmt::BasicWriter::FormatDecimal( // "Three Optimization Tips for C++". See speed-test for a comparison. unsigned index = (value % 100) * 2; value /= 100; - buffer[num_digits] = DIGITS[index + 1]; - buffer[num_digits - 1] = DIGITS[index]; + buffer[num_digits] = internal::DIGITS[index + 1]; + buffer[num_digits - 1] = internal::DIGITS[index]; num_digits -= 2; } if (value < 10) { @@ -163,8 +163,8 @@ void fmt::BasicWriter::FormatDecimal( return; } unsigned index = static_cast(value * 2); - buffer[1] = DIGITS[index + 1]; - buffer[0] = DIGITS[index]; + buffer[1] = internal::DIGITS[index + 1]; + buffer[0] = internal::DIGITS[index]; } template diff --git a/format.h b/format.h index ed2eb6ae..94bc3474 100644 --- a/format.h +++ b/format.h @@ -188,6 +188,8 @@ inline unsigned CountDigits(uint64_t n) { } } +extern const char DIGITS[]; + template class FormatterProxy; } @@ -1055,6 +1057,56 @@ class Formatter : private Action, public BasicFormatter { } }; +/** + Fast integer formatter. + */ +class FormatInt { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; + char buffer_[BUFFER_SIZE]; + char *str_; + + // Formats value in reverse and returns the number of digits. + char *FormatDecimal(uint64_t value) { + char *buffer_end = buffer_ + BUFFER_SIZE; + *--buffer_end = '\0'; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + *--buffer_end = internal::DIGITS[index + 1]; + *--buffer_end = internal::DIGITS[index]; + } + if (value < 10) { + *--buffer_end = static_cast('0' + value); + return buffer_end; + } + unsigned index = static_cast(value * 2); + *--buffer_end = internal::DIGITS[index + 1]; + *--buffer_end = internal::DIGITS[index]; + return buffer_end; + } + + public: + explicit FormatInt(int value) { + uint64_t abs_value = value; + bool negative = value < 0; + if (negative) + abs_value = 0 - value; + str_ = FormatDecimal(abs_value); + if (negative) + *--str_ = '-'; + } + explicit FormatInt(unsigned value) : str_(FormatDecimal(value)) {} + + const char *c_str() const { return str_; } + std::string str() const { return str_; } +}; + /** \rst Formats a string similarly to Python's `str.format