Handle end of input in scan

This commit is contained in:
Victor Zverovich 2023-12-25 07:18:23 -08:00
parent 1fd093add4
commit c5a85f8d7d
2 changed files with 43 additions and 10 deletions

View File

@ -10,6 +10,7 @@
#include <time.h> #include <time.h>
#include <climits> #include <climits>
#include <thread>
#include "fmt/os.h" #include "fmt/os.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
@ -123,6 +124,11 @@ TEST(scan_test, example) {
EXPECT_EQ(value, 42); EXPECT_EQ(value, 42);
} }
TEST(scan_test, end_of_input) {
int value = 0;
fmt::scan("", "{}", value);
}
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
TEST(scan_test, file) { TEST(scan_test, file) {
fmt::file read_end, write_end; fmt::file read_end, write_end;

View File

@ -18,6 +18,26 @@ inline bool is_whitespace(char c) {
return c == ' ' || c == '\n'; return c == ' ' || c == '\n';
} }
template <typename T>
class optional {
private:
T value_;
bool has_value_ = false;
public:
optional() = default;
optional(T value) : value_(std::move(value)), has_value_(true) {}
explicit operator bool() const {
return has_value_;
}
const T& operator*() const {
if (!has_value_) throw std::runtime_error("bad optional access");
return value_;
}
};
struct maybe_contiguous_range { struct maybe_contiguous_range {
const char* begin; const char* begin;
const char* end; const char* end;
@ -374,13 +394,15 @@ struct scan_handler : error_handler {
int next_arg_id_; int next_arg_id_;
scan_arg arg_; scan_arg arg_;
template <typename T = unsigned> auto read_uint() -> T { template <typename T = unsigned> auto read_uint() -> optional<T> {
auto it = scan_ctx_.begin(), end = scan_ctx_.end(); auto it = scan_ctx_.begin(), end = scan_ctx_.end();
char c = it != end ? *it : '\0', prev_digit; if (it == end) return {};
char c = *it;
if (c < '0' || c > '9') on_error("invalid input"); if (c < '0' || c > '9') on_error("invalid input");
int num_digits = 0; int num_digits = 0;
T value = 0, prev = 0; T value = 0, prev = 0;
char prev_digit = c;
do { do {
prev = value; prev = value;
value = value * 10 + static_cast<unsigned>(c - '0'); value = value * 10 + static_cast<unsigned>(c - '0');
@ -401,16 +423,19 @@ struct scan_handler : error_handler {
throw format_error("number is too big"); throw format_error("number is too big");
} }
template <typename T = int> auto read_int() -> T { template <typename T = int> auto read_int() -> optional<T> {
auto it = scan_ctx_.begin(), end = scan_ctx_.end(); auto it = scan_ctx_.begin(), end = scan_ctx_.end();
bool negative = it != end && *it == '-'; bool negative = it != end && *it == '-';
if (negative) { if (negative) {
++it; ++it;
scan_ctx_.advance_to(it); scan_ctx_.advance_to(it);
} }
auto abs_value = read_uint<typename std::make_unsigned<T>::type>(); if (auto abs_value = read_uint<typename std::make_unsigned<T>::type>()) {
auto value = static_cast<T>(abs_value); auto value = static_cast<T>(*abs_value);
return negative ? -value : value; return negative ? -value : value;
}
if (negative) on_error("invalid input");
return {};
} }
public: public:
@ -446,16 +471,18 @@ struct scan_handler : error_handler {
scan_ctx_.advance_to(it); scan_ctx_.advance_to(it);
switch (arg_.type) { switch (arg_.type) {
case scan_type::int_type: case scan_type::int_type:
*arg_.int_value = read_int(); if (auto value = read_int()) *arg_.int_value = *value;
// TODO: stop on end of input
break; break;
case scan_type::uint_type: case scan_type::uint_type:
*arg_.uint_value = read_uint(); if (auto value = read_uint()) *arg_.uint_value = *value;
break; break;
case scan_type::long_long_type: case scan_type::long_long_type:
*arg_.long_long_value = read_int<long long>(); if (auto value = read_int<long long>()) *arg_.long_long_value = *value;
break; break;
case scan_type::ulong_long_type: case scan_type::ulong_long_type:
*arg_.ulong_long_value = read_uint<unsigned long long>(); if (auto value = read_uint<unsigned long long>())
*arg_.ulong_long_value = *value;
break; break;
case scan_type::string_type: case scan_type::string_type:
while (it != end && *it != ' ') arg_.string->push_back(*it++); while (it != end && *it != ' ') arg_.string->push_back(*it++);