summaryrefslogtreecommitdiffstats
path: root/src/fmt/test/scan.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/fmt/test/scan.h')
-rw-r--r--src/fmt/test/scan.h241
1 files changed, 241 insertions, 0 deletions
diff --git a/src/fmt/test/scan.h b/src/fmt/test/scan.h
new file mode 100644
index 000000000..41748ae89
--- /dev/null
+++ b/src/fmt/test/scan.h
@@ -0,0 +1,241 @@
+// Formatting library for C++ - scanning API proof of concept
+//
+// Copyright (c) 2019 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#include <array>
+#include <cassert>
+#include <climits>
+
+#include "fmt/format.h"
+
+FMT_BEGIN_NAMESPACE
+template <typename T, typename Char = char> struct scanner {
+ // A deleted default constructor indicates a disabled scanner.
+ scanner() = delete;
+};
+
+class scan_parse_context {
+ private:
+ string_view format_;
+
+ public:
+ using iterator = string_view::iterator;
+
+ explicit FMT_CONSTEXPR scan_parse_context(string_view format)
+ : format_(format) {}
+
+ FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
+ FMT_CONSTEXPR iterator end() const { return format_.end(); }
+
+ void advance_to(iterator it) {
+ format_.remove_prefix(detail::to_unsigned(it - begin()));
+ }
+};
+
+struct scan_context {
+ private:
+ string_view input_;
+
+ public:
+ using iterator = const char*;
+
+ explicit scan_context(string_view input) : input_(input) {}
+
+ iterator begin() const { return input_.data(); }
+ iterator end() const { return begin() + input_.size(); }
+
+ void advance_to(iterator it) {
+ input_.remove_prefix(detail::to_unsigned(it - begin()));
+ }
+};
+
+namespace detail {
+enum class scan_type {
+ none_type,
+ int_type,
+ uint_type,
+ long_long_type,
+ ulong_long_type,
+ string_type,
+ string_view_type,
+ custom_type
+};
+
+struct custom_scan_arg {
+ void* value;
+ void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
+};
+
+class scan_arg {
+ public:
+ scan_type type;
+ union {
+ int* int_value;
+ unsigned* uint_value;
+ long long* long_long_value;
+ unsigned long long* ulong_long_value;
+ std::string* string;
+ fmt::string_view* string_view;
+ custom_scan_arg custom;
+ // TODO: more types
+ };
+
+ scan_arg() : type(scan_type::none_type) {}
+ scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
+ scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
+ scan_arg(long long& value)
+ : type(scan_type::long_long_type), long_long_value(&value) {}
+ scan_arg(unsigned long long& value)
+ : type(scan_type::ulong_long_type), ulong_long_value(&value) {}
+ scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
+ scan_arg(fmt::string_view& value)
+ : type(scan_type::string_view_type), string_view(&value) {}
+ template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
+ custom.value = &value;
+ custom.scan = scan_custom_arg<T>;
+ }
+
+ private:
+ template <typename T>
+ static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
+ scan_context& ctx) {
+ scanner<T> s;
+ parse_ctx.advance_to(s.parse(parse_ctx));
+ ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
+ }
+};
+} // namespace detail
+
+struct scan_args {
+ int size;
+ const detail::scan_arg* data;
+
+ template <size_t N>
+ scan_args(const std::array<detail::scan_arg, N>& store)
+ : size(N), data(store.data()) {
+ static_assert(N < INT_MAX, "too many arguments");
+ }
+};
+
+namespace detail {
+
+struct scan_handler : error_handler {
+ private:
+ scan_parse_context parse_ctx_;
+ scan_context scan_ctx_;
+ scan_args args_;
+ int next_arg_id_;
+ scan_arg arg_;
+
+ template <typename T = unsigned> T read_uint() {
+ T value = 0;
+ auto it = scan_ctx_.begin(), end = scan_ctx_.end();
+ while (it != end) {
+ char c = *it++;
+ if (c < '0' || c > '9') on_error("invalid input");
+ // TODO: check overflow
+ value = value * 10 + static_cast<unsigned>(c - '0');
+ }
+ scan_ctx_.advance_to(it);
+ return value;
+ }
+
+ template <typename T = int> T read_int() {
+ auto it = scan_ctx_.begin(), end = scan_ctx_.end();
+ bool negative = it != end && *it == '-';
+ if (negative) ++it;
+ scan_ctx_.advance_to(it);
+ const auto value = read_uint<typename std::make_unsigned<T>::type>();
+ if (negative) return -static_cast<T>(value);
+ return static_cast<T>(value);
+ }
+
+ public:
+ scan_handler(string_view format, string_view input, scan_args args)
+ : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
+
+ const char* pos() const { return scan_ctx_.begin(); }
+
+ void on_text(const char* begin, const char* end) {
+ auto size = to_unsigned(end - begin);
+ auto it = scan_ctx_.begin();
+ if (it + size > scan_ctx_.end() ||
+ !std::equal(begin, end, make_checked(it, size))) {
+ on_error("invalid input");
+ }
+ scan_ctx_.advance_to(it + size);
+ }
+
+ FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); }
+ FMT_CONSTEXPR int on_arg_id(int id) {
+ if (id >= args_.size) on_error("argument index out of range");
+ arg_ = args_.data[id];
+ return id;
+ }
+ FMT_CONSTEXPR int on_arg_id(string_view id) {
+ if (id.data()) on_error("invalid format");
+ return 0;
+ }
+
+ void on_replacement_field(int, const char*) {
+ auto it = scan_ctx_.begin(), end = scan_ctx_.end();
+ switch (arg_.type) {
+ case scan_type::int_type:
+ *arg_.int_value = read_int();
+ break;
+ case scan_type::uint_type:
+ *arg_.uint_value = read_uint();
+ break;
+ case scan_type::long_long_type:
+ *arg_.long_long_value = read_int<long long>();
+ break;
+ case scan_type::ulong_long_type:
+ *arg_.ulong_long_value = read_uint<unsigned long long>();
+ break;
+ case scan_type::string_type:
+ while (it != end && *it != ' ') arg_.string->push_back(*it++);
+ scan_ctx_.advance_to(it);
+ break;
+ case scan_type::string_view_type: {
+ auto s = it;
+ while (it != end && *it != ' ') ++it;
+ *arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
+ scan_ctx_.advance_to(it);
+ break;
+ }
+ case scan_type::none_type:
+ case scan_type::custom_type:
+ assert(false);
+ }
+ }
+
+ const char* on_format_specs(int, const char* begin, const char*) {
+ if (arg_.type != scan_type::custom_type) return begin;
+ parse_ctx_.advance_to(begin);
+ arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
+ return parse_ctx_.begin();
+ }
+};
+} // namespace detail
+
+template <typename... Args>
+std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
+ return {{args...}};
+}
+
+string_view::iterator vscan(string_view input, string_view format_str,
+ scan_args args) {
+ detail::scan_handler h(format_str, input, args);
+ detail::parse_format_string<false>(format_str, h);
+ return input.begin() + (h.pos() - &*input.begin());
+}
+
+template <typename... 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