mirror of
https://github.com/fmtlib/fmt.git
synced 2025-08-03 12:44:49 +02:00
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.
This commit is contained in:
@@ -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);
|
||||||
|
35
format.cc
35
format.cc
@@ -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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
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(
|
||||||
|
52
format.h
52
format.h
@@ -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) {
|
|||||||
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> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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}; };
|
|||||||
|
|
||||||
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) {
|
|||||||
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 {
|
|||||||
|
|
||||||
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) {
|
|||||||
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 {
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user