diff --git a/test/scan-test.cc b/test/scan-test.cc index b190fd579f2c3..ea494f6acc692 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -29,6 +29,8 @@ TEST(scan_test, read_int) { EXPECT_EQ(n, 42); fmt::scan("-42", "{}", n); EXPECT_EQ(n, -42); + fmt::scan("42", "{:}", n); + EXPECT_EQ(n, 42); EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n), fmt::format_error, "number is too big"); } diff --git a/test/scan.h b/test/scan.h index 7d022ef4533c3..c3bb277e2cb7b 100644 --- a/test/scan.h +++ b/test/scan.h @@ -147,6 +147,9 @@ class scan_buffer { } }; +using scan_iterator = scan_buffer::iterator; +using scan_sentinel = scan_buffer::sentinel; + class string_scan_buffer : public scan_buffer { private: void consume() override {} @@ -456,77 +459,78 @@ const char* parse_scan_specs(const char* begin, const char* end, return begin; } +template ::value)> +auto read(scan_iterator it, T& value) -> scan_iterator { + if (it == scan_sentinel()) return it; + char c = *it; + if (c < '0' || c > '9') throw_format_error("invalid input"); + + int num_digits = 0; + T n = 0, prev = 0; + char prev_digit = c; + do { + prev = n; + n = n * 10 + static_cast(c - '0'); + prev_digit = c; + c = *++it; + ++num_digits; + if (c < '0' || c > '9') break; + } while (it != scan_sentinel()); + + // Check overflow. + if (num_digits <= std::numeric_limits::digits10) { + value = n; + return it; + } + unsigned max = to_unsigned((std::numeric_limits::max)()); + if (num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(prev_digit - '0') <= max) { + value = n; + } else { + throw_format_error("number is too big"); + } + return it; +} + +template ::value)> +auto read(scan_iterator it, T& value) -> scan_iterator { + bool negative = it != scan_sentinel() && *it == '-'; + if (negative) { + ++it; + if (it == scan_sentinel()) throw_format_error("invalid input"); + } + using unsigned_type = typename std::make_unsigned::type; + unsigned_type abs_value = 0; + it = read(it, abs_value); + auto n = static_cast(abs_value); + value = negative ? -n : n; + return it; +} + struct default_arg_scanner { using iterator = scan_buffer::iterator; using sentinel = scan_buffer::sentinel; iterator begin; - template - auto read_uint(iterator it, T& value) -> iterator { - if (it == sentinel()) return it; - char c = *it; - if (c < '0' || c > '9') throw_format_error("invalid input"); - - int num_digits = 0; - T n = 0, prev = 0; - char prev_digit = c; - do { - prev = n; - n = n * 10 + static_cast(c - '0'); - prev_digit = c; - c = *++it; - ++num_digits; - if (c < '0' || c > '9') break; - } while (it != sentinel()); - - // Check overflow. - if (num_digits <= std::numeric_limits::digits10) { - value = n; - return it; - } - unsigned max = to_unsigned((std::numeric_limits::max)()); - if (num_digits == std::numeric_limits::digits10 + 1 && - prev * 10ull + unsigned(prev_digit - '0') <= max) { - value = n; - } else { - throw_format_error("number is too big"); - } - return it; - } - - template - auto read_int(iterator it, T& value) -> iterator { - bool negative = it != sentinel() && *it == '-'; - if (negative) { - ++it; - if (it == sentinel()) throw_format_error("invalid input"); - } - using unsigned_type = typename std::make_unsigned::type; - unsigned_type abs_value = 0; - it = read_uint(it, abs_value); - auto n = static_cast(abs_value); - value = negative ? -n : n; - return it; - } - auto operator()(int& value) -> iterator { - return read_int(begin, value); + return read(begin, value); } auto operator()(unsigned& value) -> iterator { - return read_uint(begin, value); + return read(begin, value); } auto operator()(long long& value) -> iterator { - return read_int(begin, value); + return read(begin, value); } auto operator()(unsigned long long& value) -> iterator { - return read_uint(begin, value); + return read(begin, value); } auto operator()(std::string& value) -> iterator { iterator it = begin; while (it != sentinel() && *it != ' ') value.push_back(*it++); return it; } + auto operator()(fmt::string_view& value) -> iterator { auto range = to_contiguous(begin); // This could also be checked at compile time in scan. @@ -537,6 +541,7 @@ struct default_arg_scanner { value = {range.begin, size}; return advance(begin, size); } + auto operator()(monostate) -> iterator { return begin; } @@ -550,16 +555,20 @@ struct arg_scanner { const format_specs<>& specs; template ::value)> - auto operator()(T&&) -> iterator { - // TODO: implement - return begin; + auto operator()(T& value) -> iterator { + // TODO: handle specs + return read(begin, value); } template ::value)> - auto operator()(T&&) -> iterator { + auto operator()(T&) -> iterator { // TODO: implement return begin; } + + auto operator()(monostate) -> iterator { + return begin; + } }; struct scan_handler : error_handler {