• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Formatting library for C++ - scanning API proof of concept
2 //
3 // Copyright (c) 2019 - present, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #include <array>
9 
10 #include "fmt/format.h"
11 
12 FMT_BEGIN_NAMESPACE
13 template <typename T, typename Char = char> struct scanner {
14   // A deleted default constructor indicates a disabled scanner.
15   scanner() = delete;
16 };
17 
18 class scan_parse_context {
19  private:
20   string_view format_;
21 
22  public:
23   using iterator = string_view::iterator;
24 
scan_parse_context(string_view format)25   explicit FMT_CONSTEXPR scan_parse_context(string_view format)
26       : format_(format) {}
27 
begin()28   FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
end()29   FMT_CONSTEXPR iterator end() const { return format_.end(); }
30 
advance_to(iterator it)31   void advance_to(iterator it) {
32     format_.remove_prefix(internal::to_unsigned(it - begin()));
33   }
34 };
35 
36 struct scan_context {
37  private:
38   string_view input_;
39 
40  public:
41   using iterator = const char*;
42 
scan_contextscan_context43   explicit scan_context(string_view input) : input_(input) {}
44 
beginscan_context45   iterator begin() const { return input_.data(); }
endscan_context46   iterator end() const { return begin() + input_.size(); }
47 
advance_toscan_context48   void advance_to(iterator it) {
49     input_.remove_prefix(internal::to_unsigned(it - begin()));
50   }
51 };
52 
53 namespace internal {
54 enum class scan_type {
55   none_type,
56   int_type,
57   uint_type,
58   long_long_type,
59   ulong_long_type,
60   string_type,
61   string_view_type,
62   custom_type
63 };
64 
65 struct custom_scan_arg {
66   void* value;
67   void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
68 };
69 
70 class scan_arg {
71  public:
72   scan_type type;
73   union {
74     int* int_value;
75     unsigned* uint_value;
76     long long* long_long_value;
77     unsigned long long* ulong_long_value;
78     std::string* string;
79     fmt::string_view* string_view;
80     custom_scan_arg custom;
81     // TODO: more types
82   };
83 
scan_arg()84   scan_arg() : type(scan_type::none_type) {}
scan_arg(int & value)85   scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
scan_arg(unsigned & value)86   scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
scan_arg(long long & value)87   scan_arg(long long& value)
88       : type(scan_type::long_long_type), long_long_value(&value) {}
scan_arg(unsigned long long & value)89   scan_arg(unsigned long long& value)
90       : type(scan_type::ulong_long_type), ulong_long_value(&value) {}
scan_arg(std::string & value)91   scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
scan_arg(fmt::string_view & value)92   scan_arg(fmt::string_view& value)
93       : type(scan_type::string_view_type), string_view(&value) {}
scan_arg(T & value)94   template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
95     custom.value = &value;
96     custom.scan = scan_custom_arg<T>;
97   }
98 
99  private:
100   template <typename T>
scan_custom_arg(void * arg,scan_parse_context & parse_ctx,scan_context & ctx)101   static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
102                               scan_context& ctx) {
103     scanner<T> s;
104     parse_ctx.advance_to(s.parse(parse_ctx));
105     ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
106   }
107 };
108 }  // namespace internal
109 
110 struct scan_args {
111   int size;
112   const internal::scan_arg* data;
113 
114   template <size_t N>
scan_argsscan_args115   scan_args(const std::array<internal::scan_arg, N>& store)
116       : size(N), data(store.data()) {
117     static_assert(N < INT_MAX, "too many arguments");
118   }
119 };
120 
121 namespace internal {
122 
123 struct scan_handler : error_handler {
124  private:
125   scan_parse_context parse_ctx_;
126   scan_context scan_ctx_;
127   scan_args args_;
128   int next_arg_id_;
129   scan_arg arg_;
130 
read_uintscan_handler131   template <typename T = unsigned> T read_uint() {
132     T value = 0;
133     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
134     while (it != end) {
135       char c = *it++;
136       if (c < '0' || c > '9') on_error("invalid input");
137       // TODO: check overflow
138       value = value * 10 + (c - '0');
139     }
140     scan_ctx_.advance_to(it);
141     return value;
142   }
143 
read_intscan_handler144   template <typename T = int> T read_int() {
145     T value = 0;
146     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
147     bool negative = it != end && *it == '-';
148     if (negative) ++it;
149     scan_ctx_.advance_to(it);
150     value = read_uint<typename std::make_unsigned<T>::type>();
151     if (negative) value = -value;
152     return value;
153   }
154 
155  public:
scan_handlerscan_handler156   scan_handler(string_view format, string_view input, scan_args args)
157       : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
158 
posscan_handler159   const char* pos() const { return scan_ctx_.begin(); }
160 
on_textscan_handler161   void on_text(const char* begin, const char* end) {
162     auto size = end - begin;
163     auto it = scan_ctx_.begin();
164     if (it + size > scan_ctx_.end() ||
165         !std::equal(begin, end, make_checked(it, size))) {
166       on_error("invalid input");
167     }
168     scan_ctx_.advance_to(it + size);
169   }
170 
on_arg_idscan_handler171   void on_arg_id() { on_arg_id(next_arg_id_++); }
on_arg_idscan_handler172   void on_arg_id(int id) {
173     if (id >= args_.size) on_error("argument index out of range");
174     arg_ = args_.data[id];
175   }
on_arg_idscan_handler176   void on_arg_id(string_view) { on_error("invalid format"); }
177 
on_replacement_fieldscan_handler178   void on_replacement_field(const char*) {
179     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
180     switch (arg_.type) {
181     case scan_type::int_type:
182       *arg_.int_value = read_int();
183       break;
184     case scan_type::uint_type:
185       *arg_.uint_value = read_uint();
186       break;
187     case scan_type::long_long_type:
188       *arg_.long_long_value = read_int<long long>();
189       break;
190     case scan_type::ulong_long_type:
191       *arg_.ulong_long_value = read_uint<unsigned long long>();
192       break;
193     case scan_type::string_type:
194       while (it != end && *it != ' ') arg_.string->push_back(*it++);
195       scan_ctx_.advance_to(it);
196       break;
197     case scan_type::string_view_type: {
198       auto s = it;
199       while (it != end && *it != ' ') ++it;
200       *arg_.string_view = fmt::string_view(s, it - s);
201       scan_ctx_.advance_to(it);
202       break;
203     }
204     case scan_type::none_type:
205     case scan_type::custom_type:
206       assert(false);
207     }
208   }
209 
on_format_specsscan_handler210   const char* on_format_specs(const char* begin, const char*) {
211     if (arg_.type != scan_type::custom_type) return begin;
212     parse_ctx_.advance_to(begin);
213     arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
214     return parse_ctx_.begin();
215   }
216 };
217 }  // namespace internal
218 
219 template <typename... Args>
make_scan_args(Args &...args)220 std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
221   return {{args...}};
222 }
223 
vscan(string_view input,string_view format_str,scan_args args)224 string_view::iterator vscan(string_view input, string_view format_str,
225                             scan_args args) {
226   internal::scan_handler h(format_str, input, args);
227   internal::parse_format_string<false>(format_str, h);
228   return input.begin() + (h.pos() - &*input.begin());
229 }
230 
231 template <typename... Args>
scan(string_view input,string_view format_str,Args &...args)232 string_view::iterator scan(string_view input, string_view format_str,
233                            Args&... args) {
234   return vscan(input, format_str, make_scan_args(args...));
235 }
236 FMT_END_NAMESPACE
237