Skip to content

Commit

Permalink
Implement empty format specs
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Jan 1, 2024
1 parent c068c7c commit fe108bc
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 56 deletions.
2 changes: 2 additions & 0 deletions test/scan-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
121 changes: 65 additions & 56 deletions test/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down Expand Up @@ -456,77 +459,78 @@ const char* parse_scan_specs(const char* begin, const char* end,
return begin;
}

template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::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<unsigned>(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<int>::digits10) {
value = n;
return it;
}
unsigned max = to_unsigned((std::numeric_limits<int>::max)());
if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
prev * 10ull + unsigned(prev_digit - '0') <= max) {
value = n;
} else {
throw_format_error("number is too big");
}
return it;
}

template <typename T, FMT_ENABLE_IF(std::is_signed<T>::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<T>::type;
unsigned_type abs_value = 0;
it = read(it, abs_value);
auto n = static_cast<T>(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 <typename T = unsigned>
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<unsigned>(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<int>::digits10) {
value = n;
return it;
}
unsigned max = to_unsigned((std::numeric_limits<int>::max)());
if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
prev * 10ull + unsigned(prev_digit - '0') <= max) {
value = n;
} else {
throw_format_error("number is too big");
}
return it;
}

template <typename T = int>
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<T>::type;
unsigned_type abs_value = 0;
it = read_uint<unsigned_type>(it, abs_value);
auto n = static_cast<T>(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<long long>(begin, value);
return read(begin, value);
}
auto operator()(unsigned long long& value) -> iterator {
return read_uint<unsigned long long>(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.
Expand All @@ -537,6 +541,7 @@ struct default_arg_scanner {
value = {range.begin, size};
return advance(begin, size);
}

auto operator()(monostate) -> iterator {
return begin;
}
Expand All @@ -550,16 +555,20 @@ struct arg_scanner {
const format_specs<>& specs;

template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T&&) -> iterator {
// TODO: implement
return begin;
auto operator()(T& value) -> iterator {
// TODO: handle specs
return read(begin, value);
}

template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T&&) -> iterator {
auto operator()(T&) -> iterator {
// TODO: implement
return begin;
}

auto operator()(monostate) -> iterator {
return begin;
}
};

struct scan_handler : error_handler {
Expand Down

0 comments on commit fe108bc

Please sign in to comment.