diff --git a/format.cc b/format.cc index 1ca17f9c..5f8a86e0 100644 --- a/format.cc +++ b/format.cc @@ -54,7 +54,7 @@ using fmt::StringRef; namespace { // Flags. -enum { PLUS_FLAG = 1, HEX_PREFIX_FLAG = 2 }; +enum { SIGN_FLAG = 1, PLUS_FLAG = 2, HEX_PREFIX_FLAG = 4 }; void ReportUnknownType(char code, const char *type) { if (std::isprint(static_cast(code))) { @@ -129,7 +129,7 @@ void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits) { *buffer = static_cast('0' + value); return; } - unsigned index = value * 2; + unsigned index = static_cast(value * 2); buffer[1] = DIGITS[index + 1]; buffer[0] = DIGITS[index]; } @@ -205,8 +205,8 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) { sign = '-'; ++size; abs_value = 0 - abs_value; - } else if ((spec.flags & PLUS_FLAG) != 0) { - sign = '+'; + } else if ((spec.flags & SIGN_FLAG) != 0) { + sign = (spec.flags & PLUS_FLAG) != 0 ? '+' : ' '; ++size; } switch (spec.type) { @@ -280,8 +280,8 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { if (value < 0) { sign = '-'; value = -value; - } else if ((spec.flags & PLUS_FLAG) != 0) { - sign = '+'; + } else if ((spec.flags & SIGN_FLAG) != 0) { + sign = (spec.flags & PLUS_FLAG) != 0 ? '+' : ' '; } size_t offset = buffer_.size(); unsigned width = spec.width; @@ -401,6 +401,18 @@ const Formatter::Arg &Formatter::ParseArgIndex(const char *&s) const { return *args_[arg_index]; } +void Formatter::CheckSign(const char *&s, const Arg &arg) { + if (arg.type > LAST_NUMERIC_TYPE) { + ReportError(s, + Format("format specifier '{0}' requires numeric argument") << *s); + } + if (arg.type == UINT || arg.type == ULONG) { + ReportError(s, + Format("format specifier '{0}' requires signed argument") << *s); + } + ++s; +} + void Formatter::DoFormat() { const char *start = format_; format_ = 0; @@ -462,18 +474,15 @@ void Formatter::DoFormat() { // Parse sign. switch (*s) { case '+': - spec.flags |= PLUS_FLAG; - // Fall through. + CheckSign(s, arg); + spec.flags |= SIGN_FLAG | PLUS_FLAG; + break; case '-': - if (arg.type > LAST_NUMERIC_TYPE) { - ReportError(s, - Format("format specifier '{0}' requires numeric argument") << *s); - } - if (arg.type == UINT || arg.type == ULONG) { - ReportError(s, - Format("format specifier '{0}' requires signed argument") << *s); - } - ++s; + CheckSign(s, arg); + break; + case ' ': + CheckSign(s, arg); + spec.flags |= SIGN_FLAG; break; } diff --git a/format.h b/format.h index 307a7ea8..96b45dac 100644 --- a/format.h +++ b/format.h @@ -319,6 +319,8 @@ class Formatter { // Parses argument index and returns an argument with this index. const Arg &ParseArgIndex(const char *&s) const; + void CheckSign(const char *&s, const Arg &arg); + void DoFormat(); void CompleteFormatting() { diff --git a/format_test.cc b/format_test.cc index d352791a..ee26238a 100644 --- a/format_test.cc +++ b/format_test.cc @@ -402,6 +402,29 @@ TEST(FormatterTest, MinusSign) { FormatError, "format specifier '-' requires numeric argument"); } +TEST(FormatterTest, SpaceSign) { + EXPECT_EQ(" 42", str(Format("{0: }") << 42)); + EXPECT_EQ("-42", str(Format("{0: }") << -42)); + EXPECT_EQ(" 42", str(Format("{0: }") << 42)); + EXPECT_THROW_MSG(Format("{0: }") << 42u, + FormatError, "format specifier ' ' requires signed argument"); + EXPECT_EQ(" 42", str(Format("{0: }") << 42l)); + EXPECT_THROW_MSG(Format("{0: }") << 42ul, + FormatError, "format specifier ' ' requires signed argument"); + EXPECT_EQ(" 42", str(Format("{0: }") << 42.0)); + EXPECT_EQ(" 42", str(Format("{0: }") << 42.0l)); + EXPECT_THROW_MSG(Format("{0: ") << 'c', + FormatError, "unmatched '{' in format"); + EXPECT_THROW_MSG(Format("{0: }") << 'c', + FormatError, "format specifier ' ' requires numeric argument"); + EXPECT_THROW_MSG(Format("{0: }") << "abc", + FormatError, "format specifier ' ' requires numeric argument"); + EXPECT_THROW_MSG(Format("{0: }") << reinterpret_cast(0x42), + FormatError, "format specifier ' ' requires numeric argument"); + EXPECT_THROW_MSG(Format("{0: }") << TestString(), + FormatError, "format specifier ' ' requires numeric argument"); +} + TEST(FormatterTest, ZeroFlag) { EXPECT_EQ("42", str(Format("{0:0}") << 42)); EXPECT_EQ("-0042", str(Format("{0:05}") << -42));