mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-12 14:10:07 +00:00
Check for overflow when parsing argument index. Improve error handling. Fix overload issues. More tests.
This commit is contained in:
parent
5f3ed207da
commit
63539c03b0
29
format.cc
29
format.cc
@ -6,16 +6,22 @@
|
||||
#include "format.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
static void CheckClosingBrace(const char *s) {
|
||||
// Throws Exception(message) if s contains '}' and FormatError reporting
|
||||
// unmatched '{' otherwise. The idea is that unmatched '{' should override
|
||||
// other errors.
|
||||
template <typename Exception>
|
||||
static void Throw(const char *s, const char *message) {
|
||||
while (*s && *s != '}')
|
||||
++s;
|
||||
if (!*s)
|
||||
throw fmt::FormatError("unmatched '{' in format");
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -54,18 +60,17 @@ void fmt::Formatter::Format() {
|
||||
// Parse argument index.
|
||||
unsigned arg_index = 0;
|
||||
if ('0' <= *s && *s <= '9') {
|
||||
// TODO: check overflow
|
||||
do {
|
||||
arg_index = arg_index * 10 + (*s++ - '0');
|
||||
unsigned index = arg_index * 10 + (*s++ - '0');
|
||||
if (index < arg_index) // Check if index wrapped around.
|
||||
Throw<FormatError>(s, "argument index is too big"); // TODO: test
|
||||
arg_index = index;
|
||||
} while ('0' <= *s && *s <= '9');
|
||||
} else {
|
||||
CheckClosingBrace(s);
|
||||
throw FormatError("missing argument index in format string");
|
||||
}
|
||||
if (arg_index >= args_.size()) {
|
||||
CheckClosingBrace(s);
|
||||
throw std::out_of_range("argument index is out of range in format");
|
||||
Throw<FormatError>(s, "missing argument index in format string");
|
||||
}
|
||||
if (arg_index >= args_.size())
|
||||
Throw<std::out_of_range>(s, "argument index is out of range in format");
|
||||
|
||||
char arg_format[8]; // longest format: %+0*.*ld
|
||||
char *arg_format_ptr = arg_format;
|
||||
@ -86,6 +91,7 @@ void fmt::Formatter::Format() {
|
||||
*arg_format_ptr++ = '*';
|
||||
width = 0;
|
||||
do {
|
||||
// TODO: check overflow
|
||||
width = width * 10 + (*s++ - '0');
|
||||
} while ('0' <= *s && *s <= '9');
|
||||
}
|
||||
@ -98,6 +104,7 @@ void fmt::Formatter::Format() {
|
||||
precision = 0;
|
||||
if ('0' <= *s && *s <= '9') {
|
||||
do {
|
||||
// TODO: check overflow
|
||||
precision = precision * 10 + (*s++ - '0');
|
||||
} while ('0' <= *s && *s <= '9');
|
||||
} else {
|
||||
@ -180,6 +187,9 @@ void fmt::Formatter::Format() {
|
||||
*arg_format_ptr = '\0';
|
||||
FormatArg(arg_format, arg.pointer_value, width, precision);
|
||||
break;
|
||||
case OTHER:
|
||||
(this->*arg.format)(arg.other_value);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
@ -192,4 +202,3 @@ fmt::ArgFormatter::~ArgFormatter() {
|
||||
if (!formatter_) return;
|
||||
FinishFormatting();
|
||||
}
|
||||
|
||||
|
53
format.h
53
format.h
@ -31,7 +31,8 @@ class Formatter {
|
||||
std::vector<char> buffer_; // Output buffer.
|
||||
|
||||
enum Type {
|
||||
CHAR, INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, STRING, WSTRING, POINTER
|
||||
CHAR, INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE,
|
||||
STRING, WSTRING, POINTER, OTHER
|
||||
};
|
||||
|
||||
struct Arg {
|
||||
@ -46,6 +47,10 @@ class Formatter {
|
||||
const char *string_value;
|
||||
const wchar_t *wstring_value;
|
||||
const void *pointer_value;
|
||||
struct {
|
||||
const void *other_value;
|
||||
void (Formatter::*format)(const void *value);
|
||||
};
|
||||
};
|
||||
|
||||
explicit Arg(char value) : type(CHAR), int_value(value) {}
|
||||
@ -59,6 +64,8 @@ class Formatter {
|
||||
explicit Arg(const char *value) : type(STRING), string_value(value) {}
|
||||
explicit Arg(const wchar_t *value) : type(WSTRING), wstring_value(value) {}
|
||||
explicit Arg(const void *value) : type(POINTER), pointer_value(value) {}
|
||||
explicit Arg(const void *value, void (Formatter::*format)(const void *))
|
||||
: type(OTHER), format(format) {}
|
||||
};
|
||||
|
||||
std::vector<Arg> args_;
|
||||
@ -76,6 +83,12 @@ class Formatter {
|
||||
template <typename T>
|
||||
void FormatArg(const char *format, const T &arg, int width, int precision);
|
||||
|
||||
template <typename T>
|
||||
void FormatOtherArg(const void *value) {
|
||||
const T &typed_value = *static_cast<const T*>(value);
|
||||
// TODO: format value
|
||||
}
|
||||
|
||||
void Format();
|
||||
|
||||
public:
|
||||
@ -88,8 +101,20 @@ class Formatter {
|
||||
|
||||
const char *c_str() const { return &buffer_[0]; }
|
||||
std::size_t size() const { return buffer_.size() - 1; }
|
||||
|
||||
void Swap(Formatter &f) {
|
||||
buffer_.swap(f.buffer_);
|
||||
args_.swap(f.args_);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct AddPtrConst { typedef T Value; };
|
||||
|
||||
// Convert "T*" into "const T*".
|
||||
template <typename T>
|
||||
struct AddPtrConst<T*> { typedef const T* Value; };
|
||||
|
||||
class ArgFormatter {
|
||||
private:
|
||||
friend class Formatter;
|
||||
@ -177,6 +202,15 @@ class ArgFormatter {
|
||||
// arbitrary pointers. If you want to output a pointer cast it to void*.
|
||||
template <typename T>
|
||||
ArgFormatter &operator<<(const T *value);
|
||||
|
||||
// If T is a pointer type, say "U*", AddPtrConst<T>::Value will be
|
||||
// "const U*". This additional const ensures that operator<<(const void *)
|
||||
// and not this method is called both for "const void*" and "void*".
|
||||
template <typename T>
|
||||
ArgFormatter &operator<<(const typename AddPtrConst<T>::Value &value) {
|
||||
formatter_->Add(Formatter::Arg(&value, &Formatter::FormatOtherArg<T>));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
@ -203,24 +237,29 @@ Formatter::FormatWithCallback(const char *format) {
|
||||
return ArgFormatterWithCallback<Callback>(*this);
|
||||
}
|
||||
|
||||
class Format : public ArgFormatter {
|
||||
class FullFormat : public ArgFormatter {
|
||||
private:
|
||||
Formatter formatter_;
|
||||
mutable Formatter formatter_;
|
||||
|
||||
// Do not implement.
|
||||
Format(const Format&);
|
||||
Format& operator=(const Format&);
|
||||
FullFormat& operator=(const FullFormat&);
|
||||
|
||||
public:
|
||||
explicit Format(const char *format) : ArgFormatter(formatter_) {
|
||||
explicit FullFormat(const char *format) : ArgFormatter(formatter_) {
|
||||
ArgFormatter::operator=(formatter_(format));
|
||||
}
|
||||
|
||||
~Format() {
|
||||
FullFormat(const FullFormat& other) : ArgFormatter(other) {
|
||||
formatter_.Swap(other.formatter_);
|
||||
}
|
||||
|
||||
~FullFormat() {
|
||||
FinishFormatting();
|
||||
}
|
||||
};
|
||||
|
||||
inline FullFormat Format(const char *format) { return FullFormat(format); }
|
||||
|
||||
class Print : public ArgFormatter {
|
||||
private:
|
||||
Formatter formatter_;
|
||||
|
@ -68,14 +68,31 @@ TEST(FormatterTest, FormatArgs) {
|
||||
EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad"));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, InvalidFormat) {
|
||||
TEST(FormatterTest, FormatErrors) {
|
||||
//Format("{");
|
||||
EXPECT_THROW_MSG(Format("{"), FormatError, "unmatched '{' in format");
|
||||
EXPECT_THROW_MSG(Format("{}"), FormatError,
|
||||
"missing argument index in format string");
|
||||
EXPECT_THROW_MSG(Format("{"), FormatError, "unmatched '{' in format");
|
||||
EXPECT_THROW_MSG(Format("{0"), FormatError, "unmatched '{' in format");
|
||||
EXPECT_THROW_MSG(Format("{0}"), std::out_of_range,
|
||||
"argument index is out of range in format");
|
||||
char format[256];
|
||||
std::sprintf(format, "{%u", UINT_MAX);
|
||||
EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
|
||||
std::sprintf(format, "{%u}", UINT_MAX);
|
||||
EXPECT_THROW_MSG(Format(format), std::out_of_range,
|
||||
"argument index is out of range in format");
|
||||
if (ULONG_MAX > UINT_MAX) {
|
||||
std::sprintf(format, "{%lu", UINT_MAX + 1l);
|
||||
EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
|
||||
std::sprintf(format, "{%lu}", UINT_MAX + 1l);
|
||||
EXPECT_THROW_MSG(Format(format), FormatError, "argument index is too big");
|
||||
} else {
|
||||
std::sprintf(format, "{%u0", UINT_MAX);
|
||||
EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
|
||||
std::sprintf(format, "{%u0}", UINT_MAX);
|
||||
EXPECT_THROW_MSG(Format(format), FormatError, "argument index is too big");
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user