From 879838a539604d129df39428742f9b4bd2ba79ac Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 20 Jun 2014 07:34:02 -0700 Subject: [PATCH] Implement integer precision. --- format.cc | 72 +++++++-------------------------------------- format.h | 61 ++++++++++++++++++++++++++++++++++++-- test/printf-test.cc | 48 ++++++++++++++++-------------- 3 files changed, 94 insertions(+), 87 deletions(-) diff --git a/format.cc b/format.cc index e2d8896e..e78a4b15 100644 --- a/format.cc +++ b/format.cc @@ -350,45 +350,6 @@ typename fmt::BasicWriter::CharPtr return content; } -template -typename fmt::BasicWriter::CharPtr - fmt::BasicWriter::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(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 template void fmt::BasicWriter::FormatDouble( @@ -637,7 +598,7 @@ unsigned fmt::BasicWriter::PrintfParser::ParseHeader( Char c = *s; if (c >= '0' && c <= '9') { // 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); if (*s == '$') { // value is an argument index ++s; @@ -668,7 +629,7 @@ unsigned fmt::BasicWriter::PrintfParser::ParseHeader( return arg_index; } -// FIXME: this doesnt' depend on template argument +// TODO: move to a base class that doesn't depend on template argument template const typename fmt::BasicWriter::ArgInfo &fmt::BasicWriter::PrintfParser::HandleArgIndex( @@ -738,16 +699,11 @@ void fmt::BasicWriter::PrintfParser::Format( } // Parse precision. - int precision = -1; - /*if (*s == '.') { + if (*s == '.') { ++s; - precision = 0; - if ('0' <= *s && *s <= '9') { - unsigned value = ParseNonnegativeInt(s); - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - precision = value; - } else if (*s == '{') { + if ('0' <= *s && *s <= '9') + spec.precision_ = internal::ParseNonnegativeInt(s, error); + /*else if (*s == '*') { ++s; ++num_open_braces_; const ArgInfo &precision_arg = ParseArgIndex(s); @@ -792,8 +748,8 @@ void fmt::BasicWriter::PrintfParser::Format( if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { ReportError(s, "precision specifier requires floating-point argument"); - } - }*/ + }*/ + } // Parse type. if (!*s) @@ -825,10 +781,10 @@ void fmt::BasicWriter::PrintfParser::Format( writer.FormatInt(arg.ulong_long_value, spec); break; case DOUBLE: - writer.FormatDouble(arg.double_value, spec, precision); + writer.FormatDouble(arg.double_value, spec, spec.precision_); break; case LONG_DOUBLE: - writer.FormatDouble(arg.long_double_value, spec, precision); + writer.FormatDouble(arg.long_double_value, spec, spec.precision_); break; case CHAR: { if (spec.type_ && spec.type_ != 'c') @@ -1175,10 +1131,6 @@ template fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); -template fmt::BasicWriter::CharPtr - fmt::BasicWriter::PrepareFilledBuffer(unsigned num_digits, - const AlignSpec &spec, const char *prefix, unsigned prefix_size); - template void fmt::BasicWriter::FormatParser::Format( BasicWriter &writer, BasicStringRef format, std::size_t num_args, const ArgInfo *args); @@ -1193,10 +1145,6 @@ template fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); -template fmt::BasicWriter::CharPtr - fmt::BasicWriter::PrepareFilledBuffer(unsigned num_digits, - const AlignSpec &spec, const char *prefix, unsigned prefix_size); - template void fmt::BasicWriter::FormatParser::Format( BasicWriter &writer, BasicStringRef format, std::size_t num_args, const ArgInfo *args); diff --git a/format.h b/format.h index d7a9d0d9..88120bdb 100644 --- a/format.h +++ b/format.h @@ -586,6 +586,7 @@ template struct TypeSpec : EmptySpec { Alignment align() const { return ALIGN_DEFAULT; } unsigned width() const { return 0; } + int precision() const { return -1; } bool sign_flag() const { return false; } bool plus_flag() const { return false; } @@ -616,6 +617,8 @@ struct AlignSpec : WidthSpec { : WidthSpec(width, fill), align_(align) {} Alignment align() const { return align_; } + + int precision() const { return -1; } }; // An alignment and type specifier. @@ -633,15 +636,19 @@ struct AlignTypeSpec : AlignSpec { // A full format specifier. struct FormatSpec : AlignSpec { unsigned flags_; + int precision_; char type_; - FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), type_(type) {} + FormatSpec( + 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 plus_flag() const { return (flags_ & PLUS_FLAG) != 0; } bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; } + int precision() const { return precision_; } + char type() const { return type_; } }; @@ -860,14 +867,16 @@ class BasicWriter { return p + size - 1; } + template 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. template void FormatInt(T value, const Spec &spec); // Formats a floating-point number (double or long double). + // TODO: use precision from spec template void FormatDouble(T value, const FormatSpec &spec, int precision); @@ -1294,6 +1303,52 @@ typename BasicWriter::CharPtr BasicWriter::FormatString( return out; } +template +template +typename fmt::BasicWriter::CharPtr +fmt::BasicWriter::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(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 template void BasicWriter::FormatInt(T value, const Spec &spec) { diff --git a/test/printf-test.cc b/test/printf-test.cc index fd26d6a8..8655af98 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -133,27 +133,6 @@ TEST(PrintfTest, DefaultAlignRight) { 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) { EXPECT_PRINTF("00042", "%05d", 42); EXPECT_PRINTF("-0042", "%05d", -42); @@ -227,6 +206,31 @@ TEST(PrintfTest, HashFlag) { 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