Implement left alignment.

This commit is contained in:
Victor Zverovich 2012-12-22 14:05:56 -08:00
parent c5f4ecb25a
commit be6e54de36
3 changed files with 63 additions and 42 deletions

View File

@ -53,12 +53,7 @@ using fmt::FormatSpec;
namespace { namespace {
// Flags. // Flags.
enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 }; enum { PLUS_FLAG = 1, HEX_PREFIX_FLAG = 2 };
// Alignment.
enum Alignment {
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
};
void ReportUnknownType(char code, const char *type) { void ReportUnknownType(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) { if (std::isprint(static_cast<unsigned char>(code))) {
@ -111,6 +106,34 @@ void Formatter::ReportError(const char *s, const std::string &message) const {
throw fmt::FormatError("unmatched '{' in format"); throw fmt::FormatError("unmatched '{' in format");
} }
char *Formatter::PrepareFilledBuffer(
unsigned size, FormatSpec spec, char sign) {
if (spec.width <= size) {
char *p = GrowBuffer(size);
*p = sign;
return p + size - 1;
}
char *p = GrowBuffer(spec.width);
char *end = p + spec.width;
if (spec.align == ALIGN_LEFT) {
*p = sign;
p += size;
std::fill(p, end, spec.fill);
} else if (spec.align == ALIGN_NUMERIC) {
if (sign) {
*p++ = sign;
--size;
}
std::fill(p, end - size, spec.fill);
p = end;
} else {
*(end - size) = sign;
std::fill(p, end - size, spec.fill);
p = end;
}
return p - 1;
}
template <typename T> template <typename T>
void Formatter::FormatInt(T value, FormatSpec spec) { void Formatter::FormatInt(T value, FormatSpec spec) {
unsigned size = 0; unsigned size = 0;
@ -125,16 +148,13 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
sign = '+'; sign = '+';
++size; ++size;
} }
size_t start = buffer_.size();
char *p = 0;
switch (spec.type) { switch (spec.type) {
case 0: case 'd': { case 0: case 'd': {
UnsignedType n = abs_value; UnsignedType n = abs_value;
do { do {
++size; ++size;
} while ((n /= 10) != 0); } while ((n /= 10) != 0);
unsigned width = std::max(spec.width, size); char *p = PrepareFilledBuffer(size, spec, sign);
p = GrowBuffer(width) + width - 1;
n = abs_value; n = abs_value;
do { do {
*p-- = '0' + (n % 10); *p-- = '0' + (n % 10);
@ -148,8 +168,7 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
do { do {
++size; ++size;
} while ((n >>= 4) != 0); } while ((n >>= 4) != 0);
unsigned width = std::max(spec.width, size); char *p = PrepareFilledBuffer(size, spec, sign);
p = GrowBuffer(width) + width - 1;
n = abs_value; n = abs_value;
const char *digits = spec.type == 'x' ? const char *digits = spec.type == 'x' ?
"0123456789abcdef" : "0123456789ABCDEF"; "0123456789abcdef" : "0123456789ABCDEF";
@ -167,8 +186,7 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
do { do {
++size; ++size;
} while ((n >>= 3) != 0); } while ((n >>= 3) != 0);
unsigned width = std::max(spec.width, size); char *p = PrepareFilledBuffer(size, spec, sign);
p = GrowBuffer(width) + width - 1;
n = abs_value; n = abs_value;
do { do {
*p-- = '0' + (n & 7); *p-- = '0' + (n & 7);
@ -179,13 +197,6 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
ReportUnknownType(spec.type, "integer"); ReportUnknownType(spec.type, "integer");
break; break;
} }
if (sign) {
if ((spec.flags & ZERO_FLAG) != 0)
buffer_[start++] = sign;
else
*p-- = sign;
}
std::fill(&buffer_[start], p + 1, spec.fill);
} }
template <typename T> template <typename T>
@ -210,14 +221,16 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) {
} }
// Build format string. // Build format string.
enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg enum { MAX_FORMAT_SIZE = 10}; // longest format: %+0*.*Lg
char format[MAX_FORMAT_SIZE]; char format[MAX_FORMAT_SIZE];
char *format_ptr = format; char *format_ptr = format;
unsigned width = spec.width; unsigned width = spec.width;
*format_ptr++ = '%'; *format_ptr++ = '%';
if ((spec.flags & PLUS_FLAG) != 0) if ((spec.flags & PLUS_FLAG) != 0)
*format_ptr++ = '+'; *format_ptr++ = '+';
if ((spec.flags & ZERO_FLAG) != 0) if (spec.align == ALIGN_LEFT)
*format_ptr++ = '-';
else if (spec.align == ALIGN_NUMERIC)
*format_ptr++ = '0'; *format_ptr++ = '0';
if (width != 0) if (width != 0)
*format_ptr++ = '*'; *format_ptr++ = '*';
@ -307,23 +320,23 @@ void Formatter::DoFormat() {
// Parse fill and alignment. // Parse fill and alignment.
if (char c = *s) { if (char c = *s) {
const char *p = s + 1; const char *p = s + 1;
Alignment align = ALIGN_DEFAULT; spec.align = ALIGN_DEFAULT;
do { do {
switch (*p) { switch (*p) {
case '<': case '<':
align = ALIGN_LEFT; spec.align = ALIGN_LEFT;
break; break;
case '>': case '>':
align = ALIGN_RIGHT; spec.align = ALIGN_RIGHT;
break; break;
case '=': case '=':
align = ALIGN_NUMERIC; spec.align = ALIGN_NUMERIC;
break; break;
case '^': case '^':
align = ALIGN_CENTER; spec.align = ALIGN_CENTER;
break; break;
} }
if (align != ALIGN_DEFAULT) { if (spec.align != ALIGN_DEFAULT) {
if (p != s && c != '{' && c != '}') { if (p != s && c != '{' && c != '}') {
s += 2; s += 2;
spec.fill = c; spec.fill = c;
@ -350,7 +363,7 @@ void Formatter::DoFormat() {
if (*s == '0') { if (*s == '0') {
if (arg.type > LAST_NUMERIC_TYPE) if (arg.type > LAST_NUMERIC_TYPE)
ReportError(s, "format specifier '0' requires numeric argument"); ReportError(s, "format specifier '0' requires numeric argument");
spec.flags |= ZERO_FLAG; spec.align = ALIGN_NUMERIC;
spec.fill = '0'; spec.fill = '0';
} }
// Zero may be parsed again as a part of the width, but it is simpler // Zero may be parsed again as a part of the width, but it is simpler

View File

@ -123,13 +123,18 @@ class FormatError : public std::runtime_error {
: std::runtime_error(message) {} : std::runtime_error(message) {}
}; };
enum Alignment {
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
};
struct FormatSpec { struct FormatSpec {
Alignment align;
unsigned flags; unsigned flags;
unsigned width; unsigned width;
char type; char type;
char fill; char fill;
FormatSpec() : flags(0), width(0), type(0), fill(' ') {} FormatSpec() : align(ALIGN_DEFAULT), flags(0), width(0), type(0), fill(' ') {}
}; };
// Formatter provides string formatting functionality similar to Python's // Formatter provides string formatting functionality similar to Python's
@ -266,6 +271,8 @@ class Formatter {
void ReportError(const char *s, const std::string &message) const; void ReportError(const char *s, const std::string &message) const;
char *PrepareFilledBuffer(unsigned size, FormatSpec spec, char sign);
// Formats an integer. // Formats an integer.
template <typename T> template <typename T>
void FormatInt(T value, FormatSpec spec); void FormatInt(T value, FormatSpec spec);

View File

@ -259,19 +259,20 @@ TEST(FormatterTest, EmptySpecs) {
EXPECT_EQ("42", str(Format("{0:}") << 42)); EXPECT_EQ("42", str(Format("{0:}") << 42));
} }
TEST(FormatterTest, Align) { TEST(FormatterTest, LeftAlign) {
// TODO EXPECT_EQ("42 ", str(Format("{0:<4}") << 42));
EXPECT_EQ(" 42", str(Format("{0:>4}") << 42)); EXPECT_EQ("42 ", str(Format("{0:<4o}") << 042));
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42)); EXPECT_EQ("42 ", str(Format("{0:<4x}") << 0x42));
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42u)); EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42));
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42l)); EXPECT_EQ("42 ", str(Format("{0:<5}") << 42u));
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42ul)); EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42l));
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0)); EXPECT_EQ("42 ", str(Format("{0:<5}") << 42ul));
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0l)); EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42.0));
EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42.0l));
EXPECT_EQ("c ", str(Format("{0:<5}") << 'c')); EXPECT_EQ("c ", str(Format("{0:<5}") << 'c'));
EXPECT_EQ("abc ", str(Format("{0:<5}") << "abc")); EXPECT_EQ("abc ", str(Format("{0:<5}") << "abc"));
EXPECT_EQ(" 0xface", EXPECT_EQ("0xface ",
str(Format("{0:>8}") << reinterpret_cast<void*>(0xface))); str(Format("{0:<8}") << reinterpret_cast<void*>(0xface)));
EXPECT_EQ("def ", str(Format("{0:<5}") << TestString("def"))); EXPECT_EQ("def ", str(Format("{0:<5}") << TestString("def")));
} }