Parameterize BasicFormatter on char type.
This commit is contained in:
parent
dbfd021ae2
commit
687301c516
266
format.cc
266
format.cc
@ -64,32 +64,6 @@ using fmt::StringRef;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
struct IsLongDouble { enum {VALUE = 0}; };
|
||||
|
||||
template <>
|
||||
struct IsLongDouble<long double> { enum {VALUE = 1}; };
|
||||
|
||||
const char DIGITS[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
|
||||
// Fills the padding around the content and returns the pointer to the
|
||||
// content area.
|
||||
char *FillPadding(char *buffer,
|
||||
unsigned total_size, std::size_t content_size, char fill) {
|
||||
std::size_t padding = total_size - content_size;
|
||||
std::size_t left_padding = padding / 2;
|
||||
std::fill_n(buffer, left_padding, fill);
|
||||
buffer += left_padding;
|
||||
char *content = buffer;
|
||||
std::fill_n(buffer + content_size, padding - left_padding, fill);
|
||||
return content;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
int SignBit(double value) {
|
||||
if (value < 0) return 1;
|
||||
@ -101,237 +75,23 @@ int SignBit(double value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void BasicFormatter::FormatDecimal(
|
||||
char *buffer, uint64_t value, unsigned num_digits) {
|
||||
--num_digits;
|
||||
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[num_digits] = DIGITS[index + 1];
|
||||
buffer[num_digits - 1] = DIGITS[index];
|
||||
num_digits -= 2;
|
||||
}
|
||||
if (value < 10) {
|
||||
*buffer = static_cast<char>('0' + value);
|
||||
return;
|
||||
}
|
||||
unsigned index = static_cast<unsigned>(value * 2);
|
||||
buffer[1] = DIGITS[index + 1];
|
||||
buffer[0] = DIGITS[index];
|
||||
}
|
||||
const char fmt::internal::DIGITS[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
|
||||
void BasicFormatter::ReportUnknownType(char code, const char *type) {
|
||||
void fmt::internal::ReportUnknownType(char code, const char *type) {
|
||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
||||
throw fmt::FormatError(fmt::str(
|
||||
fmt::Format("unknown format code '{0}' for {1}") << code << type));
|
||||
fmt::Format("unknown format code '{}' for {}") << code << type));
|
||||
}
|
||||
throw fmt::FormatError(
|
||||
fmt::str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
|
||||
fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}")
|
||||
<< static_cast<unsigned>(code) << type));
|
||||
}
|
||||
|
||||
char *BasicFormatter::PrepareFilledBuffer(
|
||||
unsigned size, const AlignSpec &spec, char sign) {
|
||||
unsigned width = spec.width();
|
||||
if (width <= size) {
|
||||
char *p = GrowBuffer(size);
|
||||
*p = sign;
|
||||
return p + size - 1;
|
||||
}
|
||||
char *p = GrowBuffer(width);
|
||||
char *end = p + width;
|
||||
Alignment align = spec.align();
|
||||
if (align == ALIGN_LEFT) {
|
||||
*p = sign;
|
||||
p += size;
|
||||
std::fill(p, end, spec.fill());
|
||||
} else if (align == ALIGN_CENTER) {
|
||||
p = FillPadding(p, width, size, spec.fill());
|
||||
*p = sign;
|
||||
p += size;
|
||||
} else {
|
||||
if (align == ALIGN_NUMERIC) {
|
||||
if (sign) {
|
||||
*p++ = sign;
|
||||
--size;
|
||||
}
|
||||
} else {
|
||||
*(end - size) = sign;
|
||||
}
|
||||
std::fill(p, end - size, spec.fill());
|
||||
p = end;
|
||||
}
|
||||
return p - 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void BasicFormatter::FormatDouble(
|
||||
T value, const FormatSpec &spec, int precision) {
|
||||
// Check type.
|
||||
char type = spec.type();
|
||||
bool upper = false;
|
||||
switch (type) {
|
||||
case 0:
|
||||
type = 'g';
|
||||
break;
|
||||
case 'e': case 'f': case 'g':
|
||||
break;
|
||||
case 'F':
|
||||
#ifdef _MSC_VER
|
||||
// MSVC's printf doesn't support 'F'.
|
||||
type = 'f';
|
||||
#endif
|
||||
// Fall through.
|
||||
case 'E': case 'G':
|
||||
upper = true;
|
||||
break;
|
||||
default:
|
||||
ReportUnknownType(type, "double");
|
||||
break;
|
||||
}
|
||||
|
||||
char sign = 0;
|
||||
// Use SignBit instead of value < 0 because the latter is always
|
||||
// false for NaN.
|
||||
if (SignBit(value)) {
|
||||
sign = '-';
|
||||
value = -value;
|
||||
} else if (spec.sign_flag()) {
|
||||
sign = spec.plus_flag() ? '+' : ' ';
|
||||
}
|
||||
|
||||
if (value != value) {
|
||||
// Format NaN ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
std::size_t size = 4;
|
||||
const char *nan = upper ? " NAN" : " nan";
|
||||
if (!sign) {
|
||||
--size;
|
||||
++nan;
|
||||
}
|
||||
char *out = FormatString(nan, size, spec);
|
||||
if (sign)
|
||||
*out = sign;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isinf(value)) {
|
||||
// Format infinity ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
std::size_t size = 4;
|
||||
const char *inf = upper ? " INF" : " inf";
|
||||
if (!sign) {
|
||||
--size;
|
||||
++inf;
|
||||
}
|
||||
char *out = FormatString(inf, size, spec);
|
||||
if (sign)
|
||||
*out = sign;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t offset = buffer_.size();
|
||||
unsigned width = spec.width();
|
||||
if (sign) {
|
||||
buffer_.reserve(buffer_.size() + std::max(width, 1u));
|
||||
if (width > 0)
|
||||
--width;
|
||||
++offset;
|
||||
}
|
||||
|
||||
// Build format string.
|
||||
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||
char format[MAX_FORMAT_SIZE];
|
||||
char *format_ptr = format;
|
||||
*format_ptr++ = '%';
|
||||
unsigned width_for_sprintf = width;
|
||||
if (spec.hash_flag())
|
||||
*format_ptr++ = '#';
|
||||
if (spec.align() == ALIGN_CENTER) {
|
||||
width_for_sprintf = 0;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_LEFT)
|
||||
*format_ptr++ = '-';
|
||||
if (width != 0)
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (precision >= 0) {
|
||||
*format_ptr++ = '.';
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (IsLongDouble<T>::VALUE)
|
||||
*format_ptr++ = 'L';
|
||||
*format_ptr++ = type;
|
||||
*format_ptr = '\0';
|
||||
|
||||
// Format using snprintf.
|
||||
for (;;) {
|
||||
size_t size = buffer_.capacity() - offset;
|
||||
int n = 0;
|
||||
char *start = &buffer_[offset];
|
||||
if (width_for_sprintf == 0) {
|
||||
n = precision < 0 ?
|
||||
snprintf(start, size, format, value) :
|
||||
snprintf(start, size, format, precision, value);
|
||||
} else {
|
||||
n = precision < 0 ?
|
||||
snprintf(start, size, format, width_for_sprintf, value) :
|
||||
snprintf(start, size, format, width_for_sprintf, precision, value);
|
||||
}
|
||||
if (n >= 0 && offset + n < buffer_.capacity()) {
|
||||
if (sign) {
|
||||
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
|
||||
*start != ' ') {
|
||||
*(start - 1) = sign;
|
||||
sign = 0;
|
||||
} else {
|
||||
*(start - 1) = spec.fill();
|
||||
}
|
||||
++n;
|
||||
}
|
||||
if (spec.align() == ALIGN_CENTER &&
|
||||
spec.width() > static_cast<unsigned>(n)) {
|
||||
char *p = GrowBuffer(spec.width());
|
||||
std::copy(p, p + n, p + (spec.width() - n) / 2);
|
||||
FillPadding(p, spec.width(), n, spec.fill());
|
||||
return;
|
||||
}
|
||||
if (spec.fill() != ' ' || sign) {
|
||||
while (*start == ' ')
|
||||
*start++ = spec.fill();
|
||||
if (sign)
|
||||
*(start - 1) = sign;
|
||||
}
|
||||
GrowBuffer(n);
|
||||
return;
|
||||
}
|
||||
buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
char *BasicFormatter::FormatString(
|
||||
const char *s, std::size_t size, const FormatSpec &spec) {
|
||||
char *out = 0;
|
||||
if (spec.width() > size) {
|
||||
out = GrowBuffer(spec.width());
|
||||
if (spec.align() == ALIGN_RIGHT) {
|
||||
std::fill_n(out, spec.width() - size, spec.fill());
|
||||
out += spec.width() - size;
|
||||
} else if (spec.align() == ALIGN_CENTER) {
|
||||
out = FillPadding(out, spec.width(), size, spec.fill());
|
||||
} else {
|
||||
std::fill_n(out + size, spec.width() - size, spec.fill());
|
||||
}
|
||||
} else {
|
||||
out = GrowBuffer(size);
|
||||
}
|
||||
std::copy(s, s + size, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Throws Exception(message) if format contains '}', otherwise throws
|
||||
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
|
||||
// should override other errors.
|
||||
@ -573,7 +333,7 @@ void Formatter::DoFormat() {
|
||||
break;
|
||||
case CHAR: {
|
||||
if (spec.type_ && spec.type_ != 'c')
|
||||
ReportUnknownType(spec.type_, "char");
|
||||
internal::ReportUnknownType(spec.type_, "char");
|
||||
char *out = 0;
|
||||
if (spec.width_ > 1) {
|
||||
out = GrowBuffer(spec.width_);
|
||||
@ -593,7 +353,7 @@ void Formatter::DoFormat() {
|
||||
}
|
||||
case STRING: {
|
||||
if (spec.type_ && spec.type_ != 's')
|
||||
ReportUnknownType(spec.type_, "string");
|
||||
internal::ReportUnknownType(spec.type_, "string");
|
||||
const char *str = arg.string.value;
|
||||
size_t size = arg.string.size;
|
||||
if (size == 0) {
|
||||
@ -607,14 +367,14 @@ void Formatter::DoFormat() {
|
||||
}
|
||||
case POINTER:
|
||||
if (spec.type_ && spec.type_ != 'p')
|
||||
ReportUnknownType(spec.type_, "pointer");
|
||||
internal::ReportUnknownType(spec.type_, "pointer");
|
||||
spec.flags_= HASH_FLAG;
|
||||
spec.type_ = 'x';
|
||||
FormatInt(reinterpret_cast<uintptr_t>(arg.pointer_value), spec);
|
||||
break;
|
||||
case CUSTOM:
|
||||
if (spec.type_)
|
||||
ReportUnknownType(spec.type_, "object");
|
||||
internal::ReportUnknownType(spec.type_, "object");
|
||||
(this->*arg.custom.format)(arg.custom.value, spec);
|
||||
break;
|
||||
default:
|
||||
|
308
format.h
308
format.h
@ -136,6 +136,16 @@ struct IntTraits<int> : SignedIntTraits<int, unsigned> {};
|
||||
template <>
|
||||
struct IntTraits<long> : SignedIntTraits<long, unsigned long> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsLongDouble { enum {VALUE = 0}; };
|
||||
|
||||
template <>
|
||||
struct IsLongDouble<long double> { enum {VALUE = 1}; };
|
||||
|
||||
extern const char DIGITS[];
|
||||
|
||||
void ReportUnknownType(char code, const char *type);
|
||||
|
||||
class ArgInserter;
|
||||
class FormatterProxy;
|
||||
}
|
||||
@ -341,6 +351,7 @@ DEFINE_INT_FORMATTERS(long)
|
||||
DEFINE_INT_FORMATTERS(unsigned)
|
||||
DEFINE_INT_FORMATTERS(unsigned long)
|
||||
|
||||
template <typename Char>
|
||||
class BasicFormatter {
|
||||
private:
|
||||
// Returns the number of decimal digits in n. Trailing zeros are not counted
|
||||
@ -360,29 +371,30 @@ class BasicFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
static void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits);
|
||||
static void FormatDecimal(Char *buffer, uint64_t value, unsigned num_digits);
|
||||
|
||||
protected:
|
||||
static void ReportUnknownType(char code, const char *type);
|
||||
static Char *FillPadding(Char *buffer,
|
||||
unsigned total_size, std::size_t content_size, char fill);
|
||||
|
||||
enum { INLINE_BUFFER_SIZE = 500 };
|
||||
mutable internal::Array<char, INLINE_BUFFER_SIZE> buffer_; // Output buffer.
|
||||
mutable internal::Array<Char, INLINE_BUFFER_SIZE> buffer_; // Output buffer.
|
||||
|
||||
// Grows the buffer by n characters and returns a pointer to the newly
|
||||
// allocated area.
|
||||
char *GrowBuffer(std::size_t n) {
|
||||
Char *GrowBuffer(std::size_t n) {
|
||||
std::size_t size = buffer_.size();
|
||||
buffer_.resize(size + n);
|
||||
return &buffer_[size];
|
||||
}
|
||||
|
||||
char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) {
|
||||
char *p = GrowBuffer(size);
|
||||
Char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) {
|
||||
Char *p = GrowBuffer(size);
|
||||
*p = sign;
|
||||
return p + size - 1;
|
||||
}
|
||||
|
||||
char *PrepareFilledBuffer(unsigned size, const AlignSpec &spec, char sign);
|
||||
Char *PrepareFilledBuffer(unsigned size, const AlignSpec &spec, char sign);
|
||||
|
||||
// Formats an integer.
|
||||
template <typename T>
|
||||
@ -406,13 +418,13 @@ class BasicFormatter {
|
||||
Returns a pointer to the output buffer content. No terminating null
|
||||
character is appended.
|
||||
*/
|
||||
const char *data() const { return &buffer_[0]; }
|
||||
const Char *data() const { return &buffer_[0]; }
|
||||
|
||||
/**
|
||||
Returns a pointer to the output buffer content with terminating null
|
||||
character appended.
|
||||
*/
|
||||
const char *c_str() const {
|
||||
const Char *c_str() const {
|
||||
std::size_t size = buffer_.size();
|
||||
buffer_.reserve(size + 1);
|
||||
buffer_[size] = '\0';
|
||||
@ -422,7 +434,9 @@ class BasicFormatter {
|
||||
/**
|
||||
Returns the content of the output buffer as an `std::string`.
|
||||
*/
|
||||
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
|
||||
std::basic_string<Char> str() const {
|
||||
return std::basic_string<Char>(&buffer_[0], buffer_.size());
|
||||
}
|
||||
|
||||
BasicFormatter &operator<<(int value) {
|
||||
return *this << IntFormatter<int, TypeSpec<0> >(value, TypeSpec<0>());
|
||||
@ -431,12 +445,12 @@ class BasicFormatter {
|
||||
return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>());
|
||||
}
|
||||
|
||||
BasicFormatter &operator<<(char value) {
|
||||
BasicFormatter &operator<<(Char value) {
|
||||
*GrowBuffer(1) = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BasicFormatter &operator<<(const char *value) {
|
||||
BasicFormatter &operator<<(const Char *value) {
|
||||
std::size_t size = std::strlen(value);
|
||||
std::strncpy(GrowBuffer(size), value, size);
|
||||
return *this;
|
||||
@ -445,7 +459,7 @@ class BasicFormatter {
|
||||
template <typename T, typename Spec>
|
||||
BasicFormatter &operator<<(const IntFormatter<T, Spec> &f);
|
||||
|
||||
void Write(const std::string &s, const FormatSpec &spec) {
|
||||
void Write(const std::basic_string<char> &s, const FormatSpec &spec) {
|
||||
FormatString(s.data(), s.size(), spec);
|
||||
}
|
||||
|
||||
@ -454,8 +468,249 @@ class BasicFormatter {
|
||||
}
|
||||
};
|
||||
|
||||
// Fills the padding around the content and returns the pointer to the
|
||||
// content area.
|
||||
template <typename Char>
|
||||
Char *BasicFormatter<Char>::FillPadding(Char *buffer,
|
||||
unsigned total_size, std::size_t content_size, char fill) {
|
||||
std::size_t padding = total_size - content_size;
|
||||
std::size_t left_padding = padding / 2;
|
||||
std::fill_n(buffer, left_padding, fill);
|
||||
buffer += left_padding;
|
||||
char *content = buffer;
|
||||
std::fill_n(buffer + content_size, padding - left_padding, fill);
|
||||
return content;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void BasicFormatter<Char>::FormatDecimal(
|
||||
Char *buffer, uint64_t value, unsigned num_digits) {
|
||||
--num_digits;
|
||||
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[num_digits] = internal::DIGITS[index + 1];
|
||||
buffer[num_digits - 1] = internal::DIGITS[index];
|
||||
num_digits -= 2;
|
||||
}
|
||||
if (value < 10) {
|
||||
*buffer = static_cast<char>('0' + value);
|
||||
return;
|
||||
}
|
||||
unsigned index = static_cast<unsigned>(value * 2);
|
||||
buffer[1] = internal::DIGITS[index + 1];
|
||||
buffer[0] = internal::DIGITS[index];
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
Char *BasicFormatter<Char>::PrepareFilledBuffer(
|
||||
unsigned size, const AlignSpec &spec, char sign) {
|
||||
unsigned width = spec.width();
|
||||
if (width <= size) {
|
||||
char *p = GrowBuffer(size);
|
||||
*p = sign;
|
||||
return p + size - 1;
|
||||
}
|
||||
char *p = GrowBuffer(width);
|
||||
char *end = p + width;
|
||||
Alignment align = spec.align();
|
||||
if (align == ALIGN_LEFT) {
|
||||
*p = sign;
|
||||
p += size;
|
||||
std::fill(p, end, spec.fill());
|
||||
} else if (align == ALIGN_CENTER) {
|
||||
p = FillPadding(p, width, size, spec.fill());
|
||||
*p = sign;
|
||||
p += size;
|
||||
} else {
|
||||
if (align == ALIGN_NUMERIC) {
|
||||
if (sign) {
|
||||
*p++ = sign;
|
||||
--size;
|
||||
}
|
||||
} else {
|
||||
*(end - size) = sign;
|
||||
}
|
||||
std::fill(p, end - size, spec.fill());
|
||||
p = end;
|
||||
}
|
||||
return p - 1;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
template <typename T>
|
||||
void BasicFormatter<Char>::FormatDouble(
|
||||
T value, const FormatSpec &spec, int precision) {
|
||||
// Check type.
|
||||
char type = spec.type();
|
||||
bool upper = false;
|
||||
switch (type) {
|
||||
case 0:
|
||||
type = 'g';
|
||||
break;
|
||||
case 'e': case 'f': case 'g':
|
||||
break;
|
||||
case 'F':
|
||||
#ifdef _MSC_VER
|
||||
// MSVC's printf doesn't support 'F'.
|
||||
type = 'f';
|
||||
#endif
|
||||
// Fall through.
|
||||
case 'E': case 'G':
|
||||
upper = true;
|
||||
break;
|
||||
default:
|
||||
internal::ReportUnknownType(type, "double");
|
||||
break;
|
||||
}
|
||||
|
||||
char sign = 0;
|
||||
// Use SignBit instead of value < 0 because the latter is always
|
||||
// false for NaN.
|
||||
if (SignBit(value)) {
|
||||
sign = '-';
|
||||
value = -value;
|
||||
} else if (spec.sign_flag()) {
|
||||
sign = spec.plus_flag() ? '+' : ' ';
|
||||
}
|
||||
|
||||
if (value != value) {
|
||||
// Format NaN ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
std::size_t size = 4;
|
||||
const char *nan = upper ? " NAN" : " nan";
|
||||
if (!sign) {
|
||||
--size;
|
||||
++nan;
|
||||
}
|
||||
char *out = FormatString(nan, size, spec);
|
||||
if (sign)
|
||||
*out = sign;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isinf(value)) {
|
||||
// Format infinity ourselves because sprintf's output is not consistent
|
||||
// across platforms.
|
||||
std::size_t size = 4;
|
||||
const char *inf = upper ? " INF" : " inf";
|
||||
if (!sign) {
|
||||
--size;
|
||||
++inf;
|
||||
}
|
||||
char *out = FormatString(inf, size, spec);
|
||||
if (sign)
|
||||
*out = sign;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t offset = buffer_.size();
|
||||
unsigned width = spec.width();
|
||||
if (sign) {
|
||||
buffer_.reserve(buffer_.size() + std::max(width, 1u));
|
||||
if (width > 0)
|
||||
--width;
|
||||
++offset;
|
||||
}
|
||||
|
||||
// Build format string.
|
||||
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||
char format[MAX_FORMAT_SIZE];
|
||||
char *format_ptr = format;
|
||||
*format_ptr++ = '%';
|
||||
unsigned width_for_sprintf = width;
|
||||
if (spec.hash_flag())
|
||||
*format_ptr++ = '#';
|
||||
if (spec.align() == ALIGN_CENTER) {
|
||||
width_for_sprintf = 0;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_LEFT)
|
||||
*format_ptr++ = '-';
|
||||
if (width != 0)
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (precision >= 0) {
|
||||
*format_ptr++ = '.';
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (internal::IsLongDouble<T>::VALUE)
|
||||
*format_ptr++ = 'L';
|
||||
*format_ptr++ = type;
|
||||
*format_ptr = '\0';
|
||||
|
||||
// Format using snprintf.
|
||||
for (;;) {
|
||||
size_t size = buffer_.capacity() - offset;
|
||||
int n = 0;
|
||||
char *start = &buffer_[offset];
|
||||
if (width_for_sprintf == 0) {
|
||||
n = precision < 0 ?
|
||||
snprintf(start, size, format, value) :
|
||||
snprintf(start, size, format, precision, value);
|
||||
} else {
|
||||
n = precision < 0 ?
|
||||
snprintf(start, size, format, width_for_sprintf, value) :
|
||||
snprintf(start, size, format, width_for_sprintf, precision, value);
|
||||
}
|
||||
if (n >= 0 && offset + n < buffer_.capacity()) {
|
||||
if (sign) {
|
||||
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
|
||||
*start != ' ') {
|
||||
*(start - 1) = sign;
|
||||
sign = 0;
|
||||
} else {
|
||||
*(start - 1) = spec.fill();
|
||||
}
|
||||
++n;
|
||||
}
|
||||
if (spec.align() == ALIGN_CENTER &&
|
||||
spec.width() > static_cast<unsigned>(n)) {
|
||||
char *p = GrowBuffer(spec.width());
|
||||
std::copy(p, p + n, p + (spec.width() - n) / 2);
|
||||
FillPadding(p, spec.width(), n, spec.fill());
|
||||
return;
|
||||
}
|
||||
if (spec.fill() != ' ' || sign) {
|
||||
while (*start == ' ')
|
||||
*start++ = spec.fill();
|
||||
if (sign)
|
||||
*(start - 1) = sign;
|
||||
}
|
||||
GrowBuffer(n);
|
||||
return;
|
||||
}
|
||||
buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
char *BasicFormatter<Char>::FormatString(
|
||||
const char *s, std::size_t size, const FormatSpec &spec) {
|
||||
char *out = 0;
|
||||
if (spec.width() > size) {
|
||||
out = GrowBuffer(spec.width());
|
||||
if (spec.align() == ALIGN_RIGHT) {
|
||||
std::fill_n(out, spec.width() - size, spec.fill());
|
||||
out += spec.width() - size;
|
||||
} else if (spec.align() == ALIGN_CENTER) {
|
||||
out = FillPadding(out, spec.width(), size, spec.fill());
|
||||
} else {
|
||||
std::fill_n(out + size, spec.width() - size, spec.fill());
|
||||
}
|
||||
} else {
|
||||
out = GrowBuffer(size);
|
||||
}
|
||||
std::copy(s, s + size, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
template <typename T, typename Spec>
|
||||
BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||
BasicFormatter<Char> &BasicFormatter<Char>::operator<<(
|
||||
const IntFormatter<T, Spec> &f) {
|
||||
T value = f.value();
|
||||
unsigned size = 0;
|
||||
char sign = 0;
|
||||
@ -472,7 +727,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||
switch (f.type()) {
|
||||
case 0: case 'd': {
|
||||
unsigned num_digits = BasicFormatter::CountDigits(abs_value);
|
||||
char *p = PrepareFilledBuffer(size + num_digits, f, sign) - num_digits + 1;
|
||||
Char *p = PrepareFilledBuffer(size + num_digits, f, sign) - num_digits + 1;
|
||||
BasicFormatter::FormatDecimal(p, abs_value, num_digits);
|
||||
break;
|
||||
}
|
||||
@ -483,7 +738,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||
do {
|
||||
++size;
|
||||
} while ((n >>= 4) != 0);
|
||||
char *p = PrepareFilledBuffer(size, f, sign);
|
||||
Char *p = PrepareFilledBuffer(size, f, sign);
|
||||
n = abs_value;
|
||||
const char *digits = f.type() == 'x' ?
|
||||
"0123456789abcdef" : "0123456789ABCDEF";
|
||||
@ -503,7 +758,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||
do {
|
||||
++size;
|
||||
} while ((n >>= 3) != 0);
|
||||
char *p = PrepareFilledBuffer(size, f, sign);
|
||||
Char *p = PrepareFilledBuffer(size, f, sign);
|
||||
n = abs_value;
|
||||
do {
|
||||
*p-- = '0' + (n & 7);
|
||||
@ -513,16 +768,16 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BasicFormatter::ReportUnknownType(f.type(), "integer");
|
||||
internal::ReportUnknownType(f.type(), "integer");
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// The default formatting function.
|
||||
template <typename T>
|
||||
void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) {
|
||||
std::ostringstream os;
|
||||
template <typename Char, typename T>
|
||||
void Format(BasicFormatter<Char> &f, const FormatSpec &spec, const T &value) {
|
||||
std::basic_ostringstream<Char> os;
|
||||
os << value;
|
||||
f.Write(os.str(), spec);
|
||||
}
|
||||
@ -551,7 +806,7 @@ void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) {
|
||||
The buffer can be accessed using :meth:`data` or :meth:`c_str`.
|
||||
\endrst
|
||||
*/
|
||||
class Formatter : public BasicFormatter {
|
||||
class Formatter : public BasicFormatter<char> {
|
||||
private:
|
||||
enum Type {
|
||||
// Numeric types should go first.
|
||||
@ -704,8 +959,13 @@ class Formatter : public BasicFormatter {
|
||||
internal::ArgInserter operator()(StringRef format);
|
||||
};
|
||||
|
||||
inline std::string str(const BasicFormatter &f) { return f.str(); }
|
||||
inline const char *c_str(const BasicFormatter &f) { return f.c_str(); }
|
||||
template <typename Char>
|
||||
inline std::basic_string<Char> str(const BasicFormatter<Char> &f) {
|
||||
return f.str();
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline const Char *c_str(const BasicFormatter<Char> &f) { return f.c_str(); }
|
||||
|
||||
std::string str(internal::FormatterProxy p);
|
||||
const char *c_str(internal::FormatterProxy p);
|
||||
|
@ -862,7 +862,9 @@ class Date {
|
||||
return os;
|
||||
}
|
||||
|
||||
friend BasicFormatter &operator<<(BasicFormatter &f, const Date &d) {
|
||||
template <typename Char>
|
||||
friend BasicFormatter<Char> &operator<<(
|
||||
BasicFormatter<Char> &f, const Date &d) {
|
||||
return f << d.year_ << '-' << d.month_ << '-' << d.day_;
|
||||
}
|
||||
};
|
||||
@ -877,7 +879,8 @@ TEST(FormatterTest, FormatUsingIOStreams) {
|
||||
|
||||
class Answer {};
|
||||
|
||||
void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) {
|
||||
template <typename Char>
|
||||
void Format(fmt::BasicFormatter<Char> &f, const fmt::FormatSpec &spec, Answer) {
|
||||
f.Write("42", spec);
|
||||
}
|
||||
|
||||
@ -918,7 +921,7 @@ TEST(FormatterTest, FormatterAppend) {
|
||||
|
||||
TEST(FormatterTest, FormatterExamples) {
|
||||
using fmt::hex;
|
||||
EXPECT_EQ("0000cafe", str(BasicFormatter() << pad(hex(0xcafe), 8, '0')));
|
||||
EXPECT_EQ("0000cafe", str(BasicFormatter<char>() << pad(hex(0xcafe), 8, '0')));
|
||||
|
||||
std::string message = str(Format("The answer is {}") << 42);
|
||||
EXPECT_EQ("The answer is 42", message);
|
||||
@ -1068,11 +1071,11 @@ TEST(TempFormatterTest, Examples) {
|
||||
|
||||
TEST(StrTest, oct) {
|
||||
using fmt::oct;
|
||||
EXPECT_EQ("12", (BasicFormatter() << oct(static_cast<short>(012))).str());
|
||||
EXPECT_EQ("12", (BasicFormatter() << oct(012)).str());
|
||||
EXPECT_EQ("34", (BasicFormatter() << oct(034u)).str());
|
||||
EXPECT_EQ("56", (BasicFormatter() << oct(056l)).str());
|
||||
EXPECT_EQ("70", (BasicFormatter() << oct(070ul)).str());
|
||||
EXPECT_EQ("12", str(BasicFormatter<char>() << oct(static_cast<short>(012))));
|
||||
EXPECT_EQ("12", str(BasicFormatter<char>() << oct(012)));
|
||||
EXPECT_EQ("34", str(BasicFormatter<char>() << oct(034u)));
|
||||
EXPECT_EQ("56", str(BasicFormatter<char>() << oct(056l)));
|
||||
EXPECT_EQ("70", str(BasicFormatter<char>() << oct(070ul)));
|
||||
}
|
||||
|
||||
TEST(StrTest, hex) {
|
||||
@ -1082,17 +1085,17 @@ TEST(StrTest, hex) {
|
||||
// This shouldn't compile:
|
||||
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
|
||||
|
||||
EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str());
|
||||
EXPECT_EQ("babe", (BasicFormatter() << hex(0xbabeu)).str());
|
||||
EXPECT_EQ("dead", (BasicFormatter() << hex(0xdeadl)).str());
|
||||
EXPECT_EQ("beef", (BasicFormatter() << hex(0xbeeful)).str());
|
||||
EXPECT_EQ("cafe", str(BasicFormatter<char>() << hex(0xcafe)));
|
||||
EXPECT_EQ("babe", str(BasicFormatter<char>() << hex(0xbabeu)));
|
||||
EXPECT_EQ("dead", str(BasicFormatter<char>() << hex(0xdeadl)));
|
||||
EXPECT_EQ("beef", str(BasicFormatter<char>() << hex(0xbeeful)));
|
||||
}
|
||||
|
||||
TEST(StrTest, hexu) {
|
||||
EXPECT_EQ("CAFE", (BasicFormatter() << hexu(0xcafe)).str());
|
||||
EXPECT_EQ("BABE", (BasicFormatter() << hexu(0xbabeu)).str());
|
||||
EXPECT_EQ("DEAD", (BasicFormatter() << hexu(0xdeadl)).str());
|
||||
EXPECT_EQ("BEEF", (BasicFormatter() << hexu(0xbeeful)).str());
|
||||
EXPECT_EQ("CAFE", str(BasicFormatter<char>() << hexu(0xcafe)));
|
||||
EXPECT_EQ("BABE", str(BasicFormatter<char>() << hexu(0xbabeu)));
|
||||
EXPECT_EQ("DEAD", str(BasicFormatter<char>() << hexu(0xdeadl)));
|
||||
EXPECT_EQ("BEEF", str(BasicFormatter<char>() << hexu(0xbeeful)));
|
||||
}
|
||||
|
||||
class ISO8601DateFormatter {
|
||||
@ -1101,8 +1104,9 @@ class ISO8601DateFormatter {
|
||||
public:
|
||||
ISO8601DateFormatter(const Date &d) : date_(&d) {}
|
||||
|
||||
friend BasicFormatter &operator<<(
|
||||
BasicFormatter &f, const ISO8601DateFormatter &d) {
|
||||
template <typename Char>
|
||||
friend BasicFormatter<Char> &operator<<(
|
||||
BasicFormatter<Char> &f, const ISO8601DateFormatter &d) {
|
||||
return f << pad(d.date_->year(), 4, '0') << '-'
|
||||
<< pad(d.date_->month(), 2, '0') << '-' << pad(d.date_->day(), 2, '0');
|
||||
}
|
||||
@ -1112,17 +1116,17 @@ ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
|
||||
|
||||
TEST(StrTest, pad) {
|
||||
using fmt::hex;
|
||||
EXPECT_EQ(" cafe", (BasicFormatter() << pad(hex(0xcafe), 8)).str());
|
||||
EXPECT_EQ(" babe", (BasicFormatter() << pad(hex(0xbabeu), 8)).str());
|
||||
EXPECT_EQ(" dead", (BasicFormatter() << pad(hex(0xdeadl), 8)).str());
|
||||
EXPECT_EQ(" beef", (BasicFormatter() << pad(hex(0xbeeful), 8)).str());
|
||||
EXPECT_EQ(" cafe", str(BasicFormatter<char>() << pad(hex(0xcafe), 8)));
|
||||
EXPECT_EQ(" babe", str(BasicFormatter<char>() << pad(hex(0xbabeu), 8)));
|
||||
EXPECT_EQ(" dead", str(BasicFormatter<char>() << pad(hex(0xdeadl), 8)));
|
||||
EXPECT_EQ(" beef", str(BasicFormatter<char>() << pad(hex(0xbeeful), 8)));
|
||||
|
||||
EXPECT_EQ(" 11", (BasicFormatter() << pad(11, 7)).str());
|
||||
EXPECT_EQ(" 22", (BasicFormatter() << pad(22u, 7)).str());
|
||||
EXPECT_EQ(" 33", (BasicFormatter() << pad(33l, 7)).str());
|
||||
EXPECT_EQ(" 44", (BasicFormatter() << pad(44lu, 7)).str());
|
||||
EXPECT_EQ(" 11", str(BasicFormatter<char>() << pad(11, 7)));
|
||||
EXPECT_EQ(" 22", str(BasicFormatter<char>() << pad(22u, 7)));
|
||||
EXPECT_EQ(" 33", str(BasicFormatter<char>() << pad(33l, 7)));
|
||||
EXPECT_EQ(" 44", str(BasicFormatter<char>() << pad(44lu, 7)));
|
||||
|
||||
BasicFormatter f;
|
||||
BasicFormatter<char> f;
|
||||
f.Clear();
|
||||
f << pad(42, 5, '0');
|
||||
EXPECT_EQ("00042", f.str());
|
||||
@ -1137,8 +1141,12 @@ TEST(StrTest, pad) {
|
||||
TEST(StrTest, NoConflictWithIOManip) {
|
||||
using namespace std;
|
||||
using namespace fmt;
|
||||
EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str());
|
||||
EXPECT_EQ("12", (BasicFormatter() << oct(012)).str());
|
||||
EXPECT_EQ("cafe", str(BasicFormatter<char>() << hex(0xcafe)));
|
||||
EXPECT_EQ("12", str(BasicFormatter<char>() << oct(012)));
|
||||
}
|
||||
|
||||
TEST(StrTest, BasicFormatterWChar) {
|
||||
EXPECT_EQ(L"cafe", str(BasicFormatter<wchar_t>() << fmt::hex(0xcafe)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
Loading…
Reference in New Issue
Block a user