Support runtime width specification

This commit is contained in:
jamboree 2015-06-04 13:59:37 +08:00
parent d311927c64
commit 54a6cb34c7
3 changed files with 95 additions and 7 deletions

View File

@ -73,7 +73,7 @@ The general form of a *standard format specifier* is:
fill: <a character other than '{' or '}'>
align: "<" | ">" | "=" | "^"
sign: "+" | "-" | " "
width: `integer`
width: `integer` | "{" `arg_index` "}"
precision: `integer` | "{" `arg_index` "}"
type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X"

View File

@ -1025,16 +1025,46 @@ const Char *fmt::BasicFormatter<Char>::format(
++s;
}
// Parse width and zero flag.
if ('0' <= *s && *s <= '9') {
if (*s == '0') {
// Parse zero flag.
if (*s == '0') {
require_numeric_argument(arg, '0');
spec.align_ = ALIGN_NUMERIC;
spec.fill_ = '0';
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
++s;
}
// Parse width.
if ('0' <= *s && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
} else if (*s == '{') {
++s;
const Arg &width_arg = parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
switch (width_arg.type) {
case Arg::INT:
if (width_arg.int_value < 0)
FMT_THROW(FormatError("negative width"));
value = width_arg.int_value;
break;
case Arg::UINT:
value = width_arg.uint_value;
break;
case Arg::LONG_LONG:
if (width_arg.long_long_value < 0)
FMT_THROW(FormatError("negative width"));
value = width_arg.long_long_value;
break;
case Arg::ULONG_LONG:
value = width_arg.ulong_long_value;
break;
default:
FMT_THROW(FormatError("width is not integer"));
}
if (value > INT_MAX)
FMT_THROW(FormatError("number is too big"));
spec.width_ = static_cast<int>(value);
}
// Parse precision.

View File

@ -902,6 +902,64 @@ TEST(FormatterTest, Width) {
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
}
TEST(FormatterTest, RuntimeWidth) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), FormatError, "number is too big");
std::size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), FormatError, "number is too big");
format_str[size + 1] = '}';
format_str[size + 2] = 0;
EXPECT_THROW_MSG(format(format_str, 0), FormatError, "number is too big");
EXPECT_THROW_MSG(format("{0:{", 0),
FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:{}", 0),
FormatError, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:{x}}", 0),
FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0),
FormatError, "argument index out of range");
EXPECT_THROW_MSG(format("{0:{0:}}", 0),
FormatError, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0, -1),
FormatError, "negative width");
EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1u)),
FormatError, "number is too big");
EXPECT_THROW_MSG(format("{0:{1}}", 0, -1l),
FormatError, "negative width");
if (fmt::internal::check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(format("{0:{1}}", 0, (value + 1)),
FormatError, "number is too big");
}
EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1ul)),
FormatError, "number is too big");
EXPECT_THROW_MSG(format("{0:{1}}", 0, '0'),
FormatError, "width is not integer");
EXPECT_THROW_MSG(format("{0:{1}}", 0, 0.0),
FormatError, "width is not integer");
EXPECT_EQ(" -42", format("{0:{1}}", -42, 4));
EXPECT_EQ(" 42", format("{0:{1}}", 42u, 5));
EXPECT_EQ(" -42", format("{0:{1}}", -42l, 6));
EXPECT_EQ(" 42", format("{0:{1}}", 42ul, 7));
EXPECT_EQ(" -42", format("{0:{1}}", -42ll, 6));
EXPECT_EQ(" 42", format("{0:{1}}", 42ull, 7));
EXPECT_EQ(" -1.23", format("{0:{1}}", -1.23, 8));
EXPECT_EQ(" -1.23", format("{0:{1}}", -1.23l, 9));
EXPECT_EQ(" 0xcafe", format("{0:{1}}", reinterpret_cast<void*>(0xcafe), 10));
EXPECT_EQ("x ", format("{0:{1}}", 'x', 11));
EXPECT_EQ("str ", format("{0:{1}}", "str", 12));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
}
TEST(FormatterTest, Precision) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:.%u", UINT_MAX);