Commit 6f0387fc authored by Victor Zverovich's avatar Victor Zverovich

Expose in-place decimal integer formatting via FormatDec. Improve performance...

Expose in-place decimal integer formatting via FormatDec. Improve performance of CountDigits based on the results from https://github.com/localvoid/cxx-benchmark-count-digits.
parent d473a795
...@@ -1422,6 +1422,20 @@ TEST(FormatIntTest, FormatInt) { ...@@ -1422,6 +1422,20 @@ TEST(FormatIntTest, FormatInt) {
EXPECT_EQ(os.str(), fmt::FormatInt(std::numeric_limits<int64_t>::max()).str()); EXPECT_EQ(os.str(), fmt::FormatInt(std::numeric_limits<int64_t>::max()).str());
} }
TEST(FormatIntTest, FormatDec) {
char buffer[10];
char *ptr = buffer;
fmt::FormatDec(ptr, 42);
EXPECT_EQ(buffer + 2, ptr);
*ptr = '\0';
EXPECT_STREQ("42", buffer);
ptr = buffer;
fmt::FormatDec(ptr, -42);
*ptr = '\0';
EXPECT_EQ(buffer + 3, ptr);
EXPECT_STREQ("-42", buffer);
}
template <typename T> template <typename T>
std::string str(const T &value) { std::string str(const T &value) {
return fmt::str(fmt::Format("{0}") << value); return fmt::str(fmt::Format("{0}") << value);
......
...@@ -116,6 +116,29 @@ const char fmt::internal::DIGITS[] = ...@@ -116,6 +116,29 @@ const char fmt::internal::DIGITS[] =
"4041424344454647484950515253545556575859" "4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879" "6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"; "8081828384858687888990919293949596979899";
const uint64_t fmt::internal::POWERS_OF_10[] = {
0,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000u
};
void fmt::internal::ReportUnknownType(char code, const char *type) { void fmt::internal::ReportUnknownType(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) { if (std::isprint(static_cast<unsigned char>(code))) {
...@@ -145,8 +168,8 @@ typename fmt::BasicWriter<Char>::CharPtr ...@@ -145,8 +168,8 @@ typename fmt::BasicWriter<Char>::CharPtr
} }
template <typename Char> template <typename Char>
void fmt::BasicWriter<Char>::FormatDecimal( void fmt::internal::FormatDecimal(
CharPtr buffer, uint64_t value, unsigned num_digits) { Char *buffer, uint64_t value, unsigned num_digits) {
--num_digits; --num_digits;
while (value >= 100) { while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead // Integer division is slow so do it for a group of two digits instead
...@@ -675,8 +698,8 @@ template fmt::BasicWriter<char>::CharPtr ...@@ -675,8 +698,8 @@ template fmt::BasicWriter<char>::CharPtr
fmt::BasicWriter<char>::FillPadding(CharPtr buffer, fmt::BasicWriter<char>::FillPadding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill); unsigned total_size, std::size_t content_size, wchar_t fill);
template void fmt::BasicWriter<char>::FormatDecimal( template void fmt::internal::FormatDecimal<char>(
CharPtr buffer, uint64_t value, unsigned num_digits); char *buffer, uint64_t value, unsigned num_digits);
template fmt::BasicWriter<char>::CharPtr template fmt::BasicWriter<char>::CharPtr
fmt::BasicWriter<char>::PrepareFilledBuffer( fmt::BasicWriter<char>::PrepareFilledBuffer(
...@@ -707,8 +730,8 @@ template fmt::BasicWriter<wchar_t>::CharPtr ...@@ -707,8 +730,8 @@ template fmt::BasicWriter<wchar_t>::CharPtr
fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer, fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill); unsigned total_size, std::size_t content_size, wchar_t fill);
template void fmt::BasicWriter<wchar_t>::FormatDecimal( template void fmt::internal::FormatDecimal<wchar_t>(
CharPtr buffer, uint64_t value, unsigned num_digits); wchar_t *buffer, uint64_t value, unsigned num_digits);
template fmt::BasicWriter<wchar_t>::CharPtr template fmt::BasicWriter<wchar_t>::CharPtr
fmt::BasicWriter<wchar_t>::PrepareFilledBuffer( fmt::BasicWriter<wchar_t>::PrepareFilledBuffer(
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
// Compatibility with compilers other than clang. // Compatibility with compilers other than clang.
#ifndef __has_feature #ifndef __has_feature
# define __has_feature(x) 0 # define __has_feature(x) 0
# define __has_builtin(x) 0
#endif #endif
#ifndef FMT_USE_INITIALIZER_LIST #ifndef FMT_USE_INITIALIZER_LIST
...@@ -171,8 +172,18 @@ void Array<T, SIZE>::append(const T *begin, const T *end) { ...@@ -171,8 +172,18 @@ void Array<T, SIZE>::append(const T *begin, const T *end) {
template <typename Char> template <typename Char>
class CharTraits; class CharTraits;
template <typename Char>
class BasicCharTraits {
public:
#if _SECURE_SCL
typedef stdext::checked_array_iterator<Char*> CharPtr;
#else
typedef Char *CharPtr;
#endif
};
template <> template <>
class CharTraits<char> { class CharTraits<char> : public BasicCharTraits<char> {
private: private:
// Conversion from wchar_t to char is not supported. // Conversion from wchar_t to char is not supported.
// TODO: rename to ConvertChar // TODO: rename to ConvertChar
...@@ -189,7 +200,7 @@ class CharTraits<char> { ...@@ -189,7 +200,7 @@ class CharTraits<char> {
}; };
template <> template <>
class CharTraits<wchar_t> { class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> {
public: public:
typedef const char *UnsupportedStrType; typedef const char *UnsupportedStrType;
...@@ -233,9 +244,17 @@ struct IsLongDouble<long double> { enum {VALUE = 1}; }; ...@@ -233,9 +244,17 @@ struct IsLongDouble<long double> { enum {VALUE = 1}; };
void ReportUnknownType(char code, const char *type); void ReportUnknownType(char code, const char *type);
extern const uint64_t POWERS_OF_10[];
// Returns the number of decimal digits in n. Leading zeros are not counted // Returns the number of decimal digits in n. Leading zeros are not counted
// except for n == 0 in which case CountDigits returns 1. // except for n == 0 in which case CountDigits returns 1.
inline unsigned CountDigits(uint64_t n) { inline unsigned CountDigits(uint64_t n) {
#if FMT_GCC_VERSION >= 400 or __has_builtin(__builtin_clzll)
// Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
// and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
uint64_t t = (64 - __builtin_clzll(n | 1)) * 1233 >> 12;
return t - (n < POWERS_OF_10[t]) + 1;
#else
unsigned count = 1; unsigned count = 1;
for (;;) { for (;;) {
// Integer division is slow so do it for a group of four digits instead // Integer division is slow so do it for a group of four digits instead
...@@ -248,12 +267,16 @@ inline unsigned CountDigits(uint64_t n) { ...@@ -248,12 +267,16 @@ inline unsigned CountDigits(uint64_t n) {
n /= 10000u; n /= 10000u;
count += 4; count += 4;
} }
#endif
} }
extern const char DIGITS[]; extern const char DIGITS[];
template <typename Char> template <typename Char>
class FormatterProxy; class FormatterProxy;
template <typename Char>
void FormatDecimal(Char *buffer, uint64_t value, unsigned num_digits);
} }
/** /**
...@@ -581,17 +604,14 @@ class BasicWriter { ...@@ -581,17 +604,14 @@ class BasicWriter {
friend class BasicFormatter<Char>; friend class BasicFormatter<Char>;
typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
#if _SECURE_SCL #if _SECURE_SCL
typedef stdext::checked_array_iterator<Char*> CharPtr;
static Char *GetBase(CharPtr p) { return p.base(); } static Char *GetBase(CharPtr p) { return p.base(); }
#else #else
typedef Char *CharPtr;
static Char *GetBase(Char *p) { return p; } static Char *GetBase(Char *p) { return p; }
#endif #endif
static void FormatDecimal(
CharPtr buffer, uint64_t value, unsigned num_digits);
static CharPtr FillPadding(CharPtr buffer, static CharPtr FillPadding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill); unsigned total_size, std::size_t content_size, wchar_t fill);
...@@ -812,7 +832,7 @@ void BasicWriter<Char>::FormatInt(T value, const Spec &spec) { ...@@ -812,7 +832,7 @@ void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
unsigned num_digits = internal::CountDigits(abs_value); unsigned num_digits = internal::CountDigits(abs_value);
CharPtr p = CharPtr p =
PrepareFilledBuffer(size + num_digits, spec, sign) + 1 - num_digits; PrepareFilledBuffer(size + num_digits, spec, sign) + 1 - num_digits;
BasicWriter::FormatDecimal(p, abs_value, num_digits); internal::FormatDecimal(GetBase(p), abs_value, num_digits);
break; break;
} }
case 'x': case 'X': { case 'x': case 'X': {
...@@ -1316,6 +1336,22 @@ class FormatInt { ...@@ -1316,6 +1336,22 @@ class FormatInt {
std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; }
}; };
// Formats a decimal integer value writing into buffer and returns
// a pointer to the end of the formatted string. This function doesn't
// write a terminating null character.
template <typename T>
inline void FormatDec(char *&buffer, T value) {
typedef typename internal::IntTraits<T>::UnsignedType UnsignedType;
UnsignedType abs_value = value;
if (internal::IntTraits<T>::IsNegative(value)) {
*buffer++ = '-';
abs_value = 0 - abs_value;
}
unsigned num_digits = internal::CountDigits(abs_value);
internal::FormatDecimal(buffer, abs_value, num_digits);
buffer += num_digits;
}
/** /**
\rst \rst
Formats a string similarly to Python's `str.format Formats a string similarly to Python's `str.format
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment