From 01c631af9587668a3359980f24a7eacc1102ed16 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 May 2019 07:02:41 -0700 Subject: [PATCH] Implement unsigned and long long parsing --- test/scan-test.cc | 147 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 29 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 5d9fcce0..42417748 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -15,8 +15,22 @@ FMT_BEGIN_NAMESPACE namespace internal { struct scan_arg { - int* value; - // TODO: more types + type arg_type; + union { + int* int_value; + unsigned* uint_value; + long long* long_long_value; + unsigned long long* ulong_long_value; + // TODO: more types + }; + + scan_arg() : arg_type(none_type) {} + scan_arg(int& value) : arg_type(int_type), int_value(&value) {} + scan_arg(unsigned& value) : arg_type(uint_type), uint_value(&value) {} + scan_arg(long long& value) + : arg_type(long_long_type), long_long_value(&value) {} + scan_arg(unsigned long long& value) + : arg_type(ulong_long_type), ulong_long_value(&value) {} }; } // namespace internal @@ -32,62 +46,137 @@ struct scan_args { }; namespace internal { + struct scan_handler : error_handler { - const char* begin; - const char* end; - scan_args args; - int next_arg_id; + private: + const char* begin_; + const char* end_; + scan_args args_; + int next_arg_id_; + scan_arg arg_; + template T read_uint() { + T value = 0; + while (begin_ != end_) { + char c = *begin_++; + if (c < '0' || c > '9') on_error("invalid input"); + // TODO: check overflow + value = value * 10 + (c - '0'); + } + return value; + } + + template T read_int() { + T value = 0; + bool negative = begin_ != end_ && *begin_ == '-'; + if (negative) ++begin_; + value = read_uint::type>(); + if (negative) value = -value; + return value; + } + + public: scan_handler(string_view input, scan_args args) - : begin(input.data()), - end(begin + input.size()), - args(args), - next_arg_id(0) {} + : begin_(input.data()), + end_(begin_ + input.size()), + args_(args), + next_arg_id_(0) {} - void on_text(const char*, const char*) { on_error("invalid format"); } + const char* pos() const { return begin_; } + + void on_text(const char* begin, const char* end) { + auto size = end - begin; + if (begin_ + size > end_ || !std::equal(begin, end, begin_)) + on_error("invalid input"); + begin_ += size; + } void on_arg_id() { - if (next_arg_id >= args.size) on_error("argument index out of range"); + if (next_arg_id_ >= args_.size) on_error("argument index out of range"); + arg_ = args_.data[next_arg_id_++]; } void on_arg_id(unsigned) { on_error("invalid format"); } void on_arg_id(string_view) { on_error("invalid format"); } void on_replacement_field(const char*) { - int value = 0; - while (begin != end) { - char c = *begin++; - if (c < '0' || c > '9') on_error("invalid input"); - value = value * 10 + (c - '0'); + switch (arg_.arg_type) { + case int_type: + *arg_.int_value = read_int(); + break; + case uint_type: + *arg_.uint_value = read_uint(); + break; + case long_long_type: + *arg_.long_long_value = read_int(); + break; + case ulong_long_type: + *arg_.ulong_long_value = read_uint(); + break; + default: + assert(false); } - *args.data[0].value = value; } - const char* on_format_specs(const char* begin, const char*) { - return begin; - } + const char* on_format_specs(const char* begin, const char*) { return begin; } }; } // namespace internal -void vscan(string_view input, string_view format_str, scan_args args) { - internal::parse_format_string(format_str, - internal::scan_handler(input, args)); -} - template std::array make_scan_args(Args&... args) { - return std::array{&args...}; + return std::array{args...}; +} + +string_view::iterator vscan(string_view input, string_view format_str, + scan_args args) { + internal::scan_handler h(input, args); + internal::parse_format_string(format_str, h); + return input.begin() + (h.pos() - &*input.begin()); } template -void scan(string_view input, string_view format_str, Args&... args) { - vscan(input, format_str, make_scan_args(args...)); +string_view::iterator scan(string_view input, string_view format_str, + Args&... args) { + return vscan(input, format_str, make_scan_args(args...)); } FMT_END_NAMESPACE +TEST(ScanTest, ReadText) { + fmt::string_view s = "foo"; + auto end = fmt::scan(s, "foo"); + EXPECT_EQ(end, s.end()); + EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input"); +} + TEST(ScanTest, ReadInt) { int n = 0; fmt::scan("42", "{}", n); EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(ScanTest, ReadLongLong) { + long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(ScanTest, ReadUInt) { + unsigned n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); +} + +TEST(ScanTest, ReadULongLong) { + unsigned long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); } TEST(ScanTest, InvalidFormat) {