Commit 879838a5 authored by Victor Zverovich's avatar Victor Zverovich

Implement integer precision.

parent cb743c02
...@@ -350,45 +350,6 @@ typename fmt::BasicWriter<Char>::CharPtr ...@@ -350,45 +350,6 @@ typename fmt::BasicWriter<Char>::CharPtr
return content; return content;
} }
template <typename Char>
typename fmt::BasicWriter<Char>::CharPtr
fmt::BasicWriter<Char>::PrepareFilledBuffer(unsigned num_digits,
const AlignSpec &spec, const char *prefix, unsigned prefix_size) {
unsigned size = prefix_size + num_digits;
unsigned width = spec.width();
if (width <= size) {
CharPtr p = GrowBuffer(size);
std::copy(prefix, prefix + prefix_size, p);
return p + size - 1;
}
CharPtr p = GrowBuffer(width);
CharPtr end = p + width;
Alignment align = spec.align();
// TODO: error if fill is not convertible to Char
Char fill = static_cast<Char>(spec.fill());
if (align == ALIGN_LEFT) {
std::copy(prefix, prefix + prefix_size, p);
p += size;
std::fill(p, end, fill);
} else if (align == ALIGN_CENTER) {
p = FillPadding(p, width, size, fill);
std::copy(prefix, prefix + prefix_size, p);
p += size;
} else {
if (align == ALIGN_NUMERIC) {
if (prefix_size != 0) {
p = std::copy(prefix, prefix + prefix_size, p);
size -= prefix_size;
}
} else {
std::copy(prefix, prefix + prefix_size, end - size);
}
std::fill(p, end - size, fill);
p = end;
}
return p - 1;
}
template <typename Char> template <typename Char>
template <typename T> template <typename T>
void fmt::BasicWriter<Char>::FormatDouble( void fmt::BasicWriter<Char>::FormatDouble(
...@@ -637,7 +598,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader( ...@@ -637,7 +598,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader(
Char c = *s; Char c = *s;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with a '0' flag. // preceded with '0' flag(s).
unsigned value = internal::ParseNonnegativeInt(s, error); unsigned value = internal::ParseNonnegativeInt(s, error);
if (*s == '$') { // value is an argument index if (*s == '$') { // value is an argument index
++s; ++s;
...@@ -668,7 +629,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader( ...@@ -668,7 +629,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader(
return arg_index; return arg_index;
} }
// FIXME: this doesnt' depend on template argument // TODO: move to a base class that doesn't depend on template argument
template <typename Char> template <typename Char>
const typename fmt::BasicWriter<Char>::ArgInfo const typename fmt::BasicWriter<Char>::ArgInfo
&fmt::BasicWriter<Char>::PrintfParser::HandleArgIndex( &fmt::BasicWriter<Char>::PrintfParser::HandleArgIndex(
...@@ -738,16 +699,11 @@ void fmt::BasicWriter<Char>::PrintfParser::Format( ...@@ -738,16 +699,11 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
} }
// Parse precision. // Parse precision.
int precision = -1; if (*s == '.') {
/*if (*s == '.') {
++s; ++s;
precision = 0; if ('0' <= *s && *s <= '9')
if ('0' <= *s && *s <= '9') { spec.precision_ = internal::ParseNonnegativeInt(s, error);
unsigned value = ParseNonnegativeInt(s); /*else if (*s == '*') {
if (value > INT_MAX)
ReportError(s, "number is too big in format");
precision = value;
} else if (*s == '{') {
++s; ++s;
++num_open_braces_; ++num_open_braces_;
const ArgInfo &precision_arg = ParseArgIndex(s); const ArgInfo &precision_arg = ParseArgIndex(s);
...@@ -792,8 +748,8 @@ void fmt::BasicWriter<Char>::PrintfParser::Format( ...@@ -792,8 +748,8 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
ReportError(s, ReportError(s,
"precision specifier requires floating-point argument"); "precision specifier requires floating-point argument");
}
}*/ }*/
}
// Parse type. // Parse type.
if (!*s) if (!*s)
...@@ -825,10 +781,10 @@ void fmt::BasicWriter<Char>::PrintfParser::Format( ...@@ -825,10 +781,10 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
writer.FormatInt(arg.ulong_long_value, spec); writer.FormatInt(arg.ulong_long_value, spec);
break; break;
case DOUBLE: case DOUBLE:
writer.FormatDouble(arg.double_value, spec, precision); writer.FormatDouble(arg.double_value, spec, spec.precision_);
break; break;
case LONG_DOUBLE: case LONG_DOUBLE:
writer.FormatDouble(arg.long_double_value, spec, precision); writer.FormatDouble(arg.long_double_value, spec, spec.precision_);
break; break;
case CHAR: { case CHAR: {
if (spec.type_ && spec.type_ != 'c') if (spec.type_ && spec.type_ != 'c')
...@@ -1175,10 +1131,6 @@ template fmt::BasicWriter<char>::CharPtr ...@@ -1175,10 +1131,6 @@ 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 fmt::BasicWriter<char>::CharPtr
fmt::BasicWriter<char>::PrepareFilledBuffer(unsigned num_digits,
const AlignSpec &spec, const char *prefix, unsigned prefix_size);
template void fmt::BasicWriter<char>::FormatParser::Format( template void fmt::BasicWriter<char>::FormatParser::Format(
BasicWriter<char> &writer, BasicStringRef<char> format, BasicWriter<char> &writer, BasicStringRef<char> format,
std::size_t num_args, const ArgInfo *args); std::size_t num_args, const ArgInfo *args);
...@@ -1193,10 +1145,6 @@ template fmt::BasicWriter<wchar_t>::CharPtr ...@@ -1193,10 +1145,6 @@ 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 fmt::BasicWriter<wchar_t>::CharPtr
fmt::BasicWriter<wchar_t>::PrepareFilledBuffer(unsigned num_digits,
const AlignSpec &spec, const char *prefix, unsigned prefix_size);
template void fmt::BasicWriter<wchar_t>::FormatParser::Format( template void fmt::BasicWriter<wchar_t>::FormatParser::Format(
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format, BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
std::size_t num_args, const ArgInfo *args); std::size_t num_args, const ArgInfo *args);
......
...@@ -586,6 +586,7 @@ template <char TYPE> ...@@ -586,6 +586,7 @@ template <char TYPE>
struct TypeSpec : EmptySpec { struct TypeSpec : EmptySpec {
Alignment align() const { return ALIGN_DEFAULT; } Alignment align() const { return ALIGN_DEFAULT; }
unsigned width() const { return 0; } unsigned width() const { return 0; }
int precision() const { return -1; }
bool sign_flag() const { return false; } bool sign_flag() const { return false; }
bool plus_flag() const { return false; } bool plus_flag() const { return false; }
...@@ -616,6 +617,8 @@ struct AlignSpec : WidthSpec { ...@@ -616,6 +617,8 @@ struct AlignSpec : WidthSpec {
: WidthSpec(width, fill), align_(align) {} : WidthSpec(width, fill), align_(align) {}
Alignment align() const { return align_; } Alignment align() const { return align_; }
int precision() const { return -1; }
}; };
// An alignment and type specifier. // An alignment and type specifier.
...@@ -633,15 +636,19 @@ struct AlignTypeSpec : AlignSpec { ...@@ -633,15 +636,19 @@ struct AlignTypeSpec : AlignSpec {
// A full format specifier. // A full format specifier.
struct FormatSpec : AlignSpec { struct FormatSpec : AlignSpec {
unsigned flags_; unsigned flags_;
int precision_;
char type_; char type_;
FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ') FormatSpec(
: AlignSpec(width, fill), flags_(0), type_(type) {} unsigned width = 0, char type = 0, wchar_t fill = ' ')
: AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {}
bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; } bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; }
bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; } bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; }
bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; } bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; }
int precision() const { return precision_; }
char type() const { return type_; } char type() const { return type_; }
}; };
...@@ -860,14 +867,16 @@ class BasicWriter { ...@@ -860,14 +867,16 @@ class BasicWriter {
return p + size - 1; return p + size - 1;
} }
template <typename Spec>
CharPtr PrepareFilledBuffer(unsigned num_digits, CharPtr PrepareFilledBuffer(unsigned num_digits,
const AlignSpec &spec, const char *prefix, unsigned prefix_size); const Spec &spec, const char *prefix, unsigned prefix_size);
// Formats an integer. // Formats an integer.
template <typename T, typename Spec> template <typename T, typename Spec>
void FormatInt(T value, const Spec &spec); void FormatInt(T value, const Spec &spec);
// Formats a floating-point number (double or long double). // Formats a floating-point number (double or long double).
// TODO: use precision from spec
template <typename T> template <typename T>
void FormatDouble(T value, const FormatSpec &spec, int precision); void FormatDouble(T value, const FormatSpec &spec, int precision);
...@@ -1294,6 +1303,52 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::FormatString( ...@@ -1294,6 +1303,52 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::FormatString(
return out; return out;
} }
template <typename Char>
template <typename Spec>
typename fmt::BasicWriter<Char>::CharPtr
fmt::BasicWriter<Char>::PrepareFilledBuffer(unsigned num_digits,
const Spec &spec, const char *prefix, unsigned prefix_size) {
if (spec.precision() >= 0) {
// TODO: fill up to width if necessary
return PrepareFilledBuffer(num_digits,
AlignSpec(spec.precision() + prefix_size, '0', ALIGN_NUMERIC),
prefix, prefix_size);
}
unsigned size = prefix_size + num_digits;
unsigned width = spec.width();
if (width <= size) {
CharPtr p = GrowBuffer(size);
std::copy(prefix, prefix + prefix_size, p);
return p + size - 1;
}
CharPtr p = GrowBuffer(width);
CharPtr end = p + width;
Alignment align = spec.align();
// TODO: error if fill is not convertible to Char
Char fill = static_cast<Char>(spec.fill());
if (align == ALIGN_LEFT) {
std::copy(prefix, prefix + prefix_size, p);
p += size;
std::fill(p, end, fill);
} else if (align == ALIGN_CENTER) {
p = FillPadding(p, width, size, fill);
std::copy(prefix, prefix + prefix_size, p);
p += size;
} else {
if (align == ALIGN_NUMERIC) {
if (prefix_size != 0) {
p = std::copy(prefix, prefix + prefix_size, p);
size -= prefix_size;
}
} else {
std::copy(prefix, prefix + prefix_size, end - size);
}
std::fill(p, end - size, fill);
p = end;
}
return p - 1;
}
template <typename Char> template <typename Char>
template <typename T, typename Spec> template <typename T, typename Spec>
void BasicWriter<Char>::FormatInt(T value, const Spec &spec) { void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
......
...@@ -133,27 +133,6 @@ TEST(PrintfTest, DefaultAlignRight) { ...@@ -133,27 +133,6 @@ TEST(PrintfTest, DefaultAlignRight) {
EXPECT_PRINTF(" abc", "%5s", "abc"); EXPECT_PRINTF(" abc", "%5s", "abc");
} }
TEST(PrintfTest, Width) {
EXPECT_PRINTF(" abc", "%5s", "abc");
// Width cannot be specified twice.
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
"unknown format code '-' for integer");
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%{}d", BIG_NUM)), 42),
FormatError, "number is too big in format");
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%1${}d", BIG_NUM)), 42),
FormatError, "number is too big in format");
}
TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", str(fmt::sprintf("%*d", 5, 42)));
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
"width is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
"argument index is out of range in format");
}
TEST(PrintfTest, ZeroFlag) { TEST(PrintfTest, ZeroFlag) {
EXPECT_PRINTF("00042", "%05d", 42); EXPECT_PRINTF("00042", "%05d", 42);
EXPECT_PRINTF("-0042", "%05d", -42); EXPECT_PRINTF("-0042", "%05d", -42);
...@@ -227,6 +206,31 @@ TEST(PrintfTest, HashFlag) { ...@@ -227,6 +206,31 @@ TEST(PrintfTest, HashFlag) {
EXPECT_PRINTF("x", "%#c", 'x'); EXPECT_PRINTF("x", "%#c", 'x');
} }
// TODO: test '*' width, precision, length and type specifier TEST(PrintfTest, Width) {
EXPECT_PRINTF(" abc", "%5s", "abc");
// Width cannot be specified twice.
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
"unknown format code '-' for integer");
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%{}d", BIG_NUM)), 42),
FormatError, "number is too big in format");
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%1${}d", BIG_NUM)), 42),
FormatError, "number is too big in format");
}
TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", str(fmt::sprintf("%*d", 5, 42)));
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
"width is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
"argument index is out of range in format");
}
TEST(PrintfTest, Precision) {
EXPECT_PRINTF("00042", "%.5d", 42);
}
// TODO: test precision, length and type specifier
#endif #endif
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