diff --git a/format.cc b/format.cc index 5737bec9..680cd663 100644 --- a/format.cc +++ b/format.cc @@ -121,6 +121,19 @@ void Formatter::ReportError(const char *s, const std::string &message) const { throw fmt::FormatError("unmatched '{' in format"); } +// Fills the padding around the content and returns the pointer to the +// content area. +char *FillPadding(char *buffer, + unsigned total_size, unsigned content_size, char fill) { + unsigned padding = total_size - content_size; + unsigned 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; +} + char *Formatter::PrepareFilledBuffer( unsigned size, const FormatSpec &spec, char sign) { if (spec.width <= size) { @@ -130,7 +143,15 @@ char *Formatter::PrepareFilledBuffer( } char *p = GrowBuffer(spec.width); char *end = p + spec.width; - if (spec.align != ALIGN_LEFT) { + if (spec.align == ALIGN_LEFT) { + *p = sign; + p += size; + std::fill(p, end, spec.fill); + } else if (spec.align == ALIGN_CENTER) { + p = FillPadding(p, spec.width, size, spec.fill); + *p = sign; + p += size; + } else { if (spec.align == ALIGN_NUMERIC) { if (sign) { *p++ = sign; @@ -141,10 +162,6 @@ char *Formatter::PrepareFilledBuffer( } std::fill(p, end - size, spec.fill); p = end; - } else { - *p = sign; - p += size; - std::fill(p, end, spec.fill); } return p - 1; } @@ -256,10 +273,15 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { char format[MAX_FORMAT_SIZE]; char *format_ptr = format; *format_ptr++ = '%'; - if (spec.align == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; + unsigned width_for_sprintf = width; + 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++ = '*'; @@ -274,14 +296,14 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { size_t size = buffer_.capacity() - offset; int n = 0; char *start = &buffer_[offset]; - if (width == 0) { + 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, value) : - snprintf(start, size, format, width, precision, value); + 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) { @@ -294,6 +316,12 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { } ++n; } + if (spec.align == ALIGN_CENTER && spec.width > static_cast(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; @@ -315,6 +343,8 @@ void Formatter::FormatString( 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); } @@ -520,6 +550,8 @@ void Formatter::DoFormat() { if (spec.align == ALIGN_RIGHT) { std::fill_n(out, spec.width - 1, spec.fill); out += spec.width - 1; + } else if (spec.align == ALIGN_CENTER) { + out = FillPadding(out, spec.width, 1, spec.fill); } else { std::fill_n(out + 1, spec.width - 1, spec.fill); } diff --git a/format_test.cc b/format_test.cc index fc0e4193..8bf8edd9 100644 --- a/format_test.cc +++ b/format_test.cc @@ -320,6 +320,23 @@ TEST(FormatterTest, NumericAlign) { FormatError, "format specifier '=' requires numeric argument"); } +TEST(FormatterTest, CenterAlign) { + EXPECT_EQ(" 42 ", str(Format("{0:^5}") << 42)); + EXPECT_EQ(" 42 ", str(Format("{0:^5o}") << 042)); + EXPECT_EQ(" 42 ", str(Format("{0:^5x}") << 0x42)); + EXPECT_EQ(" -42 ", str(Format("{0:^5}") << -42)); + EXPECT_EQ(" 42 ", str(Format("{0:^5}") << 42u)); + EXPECT_EQ(" -42 ", str(Format("{0:^5}") << -42l)); + EXPECT_EQ(" 42 ", str(Format("{0:^5}") << 42ul)); + EXPECT_EQ(" -42 ", str(Format("{0:^6}") << -42.0)); + EXPECT_EQ(" -42 ", str(Format("{0:^5}") << -42.0l)); + EXPECT_EQ(" c ", str(Format("{0:^5}") << 'c')); + EXPECT_EQ(" abc ", str(Format("{0:^6}") << "abc")); + EXPECT_EQ(" 0xface ", + str(Format("{0:^8}") << reinterpret_cast(0xface))); + EXPECT_EQ(" def ", str(Format("{0:^5}") << TestString("def"))); +} + TEST(FormatterTest, Fill) { EXPECT_THROW_MSG(Format("{0:{<5}") << 'c', FormatError, "unmatched '{' in format");