Add sentinel support

This commit is contained in:
Victor Zverovich 2024-01-01 07:25:01 -08:00
parent 13fa26745d
commit c068c7c622

View File

@ -65,29 +65,31 @@ class scan_buffer {
// Fills the buffer with more input if available. // Fills the buffer with more input if available.
virtual void consume() = 0; virtual void consume() = 0;
class sentinel {};
class iterator { class iterator {
private: private:
const char** ptr_; const char** ptr_;
scan_buffer* buf_; // This could be merged with ptr_. scan_buffer* buf_; // This could be merged with ptr_.
char value_; char value_;
static auto sentinel() -> const char** { static auto get_sentinel() -> const char** {
static const char* ptr = nullptr; static const char* ptr = nullptr;
return &ptr; return &ptr;
} }
friend class scan_buffer; friend class scan_buffer;
friend auto operator==(iterator lhs, iterator rhs) -> bool { friend auto operator==(iterator lhs, sentinel) -> bool {
return *lhs.ptr_ == *rhs.ptr_; return *lhs.ptr_ == nullptr;
} }
friend auto operator!=(iterator lhs, iterator rhs) -> bool { friend auto operator!=(iterator lhs, sentinel) -> bool {
return *lhs.ptr_ != *rhs.ptr_; return *lhs.ptr_ != nullptr;
} }
iterator(scan_buffer* buf) : buf_(buf) { iterator(scan_buffer* buf) : buf_(buf) {
if (buf->ptr_ == buf->end_) { if (buf->ptr_ == buf->end_) {
ptr_ = sentinel(); ptr_ = get_sentinel();
return; return;
} }
ptr_ = &buf->ptr_; ptr_ = &buf->ptr_;
@ -97,10 +99,10 @@ class scan_buffer {
friend scan_buffer& get_buffer(iterator it) { return *it.buf_; } friend scan_buffer& get_buffer(iterator it) { return *it.buf_; }
public: public:
iterator() : ptr_(sentinel()), buf_(nullptr) {} iterator() : ptr_(get_sentinel()), buf_(nullptr) {}
auto operator++() -> iterator& { auto operator++() -> iterator& {
if (!buf_->try_consume()) ptr_ = sentinel(); if (!buf_->try_consume()) ptr_ = get_sentinel();
value_ = *buf_->ptr_; value_ = *buf_->ptr_;
return *this; return *this;
} }
@ -126,12 +128,12 @@ class scan_buffer {
const char*& ptr = it.buf_->ptr_; const char*& ptr = it.buf_->ptr_;
ptr += n; ptr += n;
it.value_ = *ptr; it.value_ = *ptr;
if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel(); if (ptr == it.buf_->end_) it.ptr_ = iterator::get_sentinel();
return it; return it;
} }
auto begin() -> iterator { return this; } auto begin() -> iterator { return this; }
auto end() -> iterator { return {}; } auto end() -> sentinel { return {}; }
auto is_contiguous() const -> bool { return contiguous_; } auto is_contiguous() const -> bool { return contiguous_; }
@ -422,6 +424,7 @@ class scan_context {
public: public:
using iterator = detail::scan_buffer::iterator; using iterator = detail::scan_buffer::iterator;
using sentinel = detail::scan_buffer::sentinel;
explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args) explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
: buf_(buf), args_(args) {} : buf_(buf), args_(args) {}
@ -431,7 +434,7 @@ class scan_context {
} }
auto begin() const -> iterator { return buf_.begin(); } auto begin() const -> iterator { return buf_.begin(); }
auto end() const -> iterator { return buf_.end(); } auto end() const -> sentinel { return {}; }
void advance_to(iterator) { buf_.consume(); } void advance_to(iterator) { buf_.consume(); }
}; };
@ -442,7 +445,7 @@ const char* parse_scan_specs(const char* begin, const char* end,
format_specs<>& specs, scan_type) { format_specs<>& specs, scan_type) {
while (begin != end) { while (begin != end) {
switch (to_ascii(*begin)) { switch (to_ascii(*begin)) {
// TODO: parse scan format specifiers // TODO: parse more scan format specifiers
case 'x': case 'x':
specs.type = presentation_type::hex_lower; specs.type = presentation_type::hex_lower;
break; break;
@ -454,14 +457,14 @@ const char* parse_scan_specs(const char* begin, const char* end,
} }
struct default_arg_scanner { struct default_arg_scanner {
// TODO: storing a range can be more efficient
using iterator = scan_buffer::iterator; using iterator = scan_buffer::iterator;
using sentinel = scan_buffer::sentinel;
iterator begin; iterator begin;
iterator end;
template <typename T = unsigned> template <typename T = unsigned>
auto read_uint(iterator it, T& value) -> iterator { auto read_uint(iterator it, T& value) -> iterator {
if (it == end) return it; if (it == sentinel()) return it;
char c = *it; char c = *it;
if (c < '0' || c > '9') throw_format_error("invalid input"); if (c < '0' || c > '9') throw_format_error("invalid input");
@ -475,7 +478,7 @@ struct default_arg_scanner {
c = *++it; c = *++it;
++num_digits; ++num_digits;
if (c < '0' || c > '9') break; if (c < '0' || c > '9') break;
} while (it != end); } while (it != sentinel());
// Check overflow. // Check overflow.
if (num_digits <= std::numeric_limits<int>::digits10) { if (num_digits <= std::numeric_limits<int>::digits10) {
@ -494,10 +497,10 @@ struct default_arg_scanner {
template <typename T = int> template <typename T = int>
auto read_int(iterator it, T& value) -> iterator { auto read_int(iterator it, T& value) -> iterator {
bool negative = it != end && *it == '-'; bool negative = it != sentinel() && *it == '-';
if (negative) { if (negative) {
++it; ++it;
if (it == end) throw_format_error("invalid input"); if (it == sentinel()) throw_format_error("invalid input");
} }
using unsigned_type = typename std::make_unsigned<T>::type; using unsigned_type = typename std::make_unsigned<T>::type;
unsigned_type abs_value = 0; unsigned_type abs_value = 0;
@ -521,7 +524,7 @@ struct default_arg_scanner {
} }
auto operator()(std::string& value) -> iterator { auto operator()(std::string& value) -> iterator {
iterator it = begin; iterator it = begin;
while (it != end && *it != ' ') value.push_back(*it++); while (it != sentinel() && *it != ' ') value.push_back(*it++);
return it; return it;
} }
auto operator()(fmt::string_view& value) -> iterator { auto operator()(fmt::string_view& value) -> iterator {
@ -541,12 +544,18 @@ struct default_arg_scanner {
struct arg_scanner { struct arg_scanner {
using iterator = scan_buffer::iterator; using iterator = scan_buffer::iterator;
using sentinel = scan_buffer::sentinel;
iterator begin; iterator begin;
iterator end;
const format_specs<>& specs; const format_specs<>& specs;
template <typename T> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T&&) -> iterator {
// TODO: implement
return begin;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T&&) -> iterator { auto operator()(T&&) -> iterator {
// TODO: implement // TODO: implement
return begin; return begin;
@ -559,6 +568,8 @@ struct scan_handler : error_handler {
scan_context scan_ctx_; scan_context scan_ctx_;
int next_arg_id_; int next_arg_id_;
using sentinel = scan_buffer::sentinel;
public: public:
FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf, FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf,
scan_args args) scan_args args)
@ -568,9 +579,9 @@ struct scan_handler : error_handler {
void on_text(const char* begin, const char* end) { void on_text(const char* begin, const char* end) {
if (begin == end) return; if (begin == end) return;
auto it = scan_ctx_.begin(), scan_end = scan_ctx_.end(); auto it = scan_ctx_.begin();
for (; begin != end; ++begin, ++it) { for (; begin != end; ++begin, ++it) {
if (it == scan_end || *begin != *it) on_error("invalid input"); if (it == sentinel() || *begin != *it) on_error("invalid input");
} }
scan_ctx_.advance_to(it); scan_ctx_.advance_to(it);
} }
@ -587,9 +598,9 @@ struct scan_handler : error_handler {
void on_replacement_field(int arg_id, const char*) { void on_replacement_field(int arg_id, const char*) {
scan_arg arg = scan_ctx_.arg(arg_id); scan_arg arg = scan_ctx_.arg(arg_id);
auto it = scan_ctx_.begin(), end = scan_ctx_.end(); auto it = scan_ctx_.begin();
while (it != end && is_whitespace(*it)) ++it; while (it != sentinel() && is_whitespace(*it)) ++it;
scan_ctx_.advance_to(arg.visit(default_arg_scanner{it, end})); scan_ctx_.advance_to(arg.visit(default_arg_scanner{it}));
} }
auto on_format_specs(int arg_id, const char* begin, const char* end) -> const auto on_format_specs(int arg_id, const char* begin, const char* end) -> const
@ -600,13 +611,12 @@ struct scan_handler : error_handler {
auto specs = format_specs<>(); auto specs = format_specs<>();
begin = parse_scan_specs(begin, end, specs, arg.type()); begin = parse_scan_specs(begin, end, specs, arg.type());
if (begin == end || *begin != '}') on_error("missing '}' in format string"); if (begin == end || *begin != '}') on_error("missing '}' in format string");
auto s = arg_scanner{scan_ctx_.begin(), scan_ctx_.end(), specs}; auto s = arg_scanner{scan_ctx_.begin(), specs};
scan_ctx_.advance_to(arg.visit(s)); scan_ctx_.advance_to(arg.visit(s));
return begin; return begin;
} }
void on_error(const char* message) { void on_error(const char* message) {
scan_ctx_.advance_to(scan_ctx_.end());
error_handler::on_error(message); error_handler::on_error(message);
} }
}; };