• 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 #include <cassert>
10 #include <climits>
11 
12 #include "fmt/format.h"
13 
14 FMT_BEGIN_NAMESPACE
15 template <typename T, typename Char = char> struct scanner {
16   // A deleted default constructor indicates a disabled scanner.
17   scanner() = delete;
18 };
19 
20 class scan_parse_context {
21  private:
22   string_view format_;
23 
24  public:
25   using iterator = string_view::iterator;
26 
scan_parse_context(string_view format)27   explicit FMT_CONSTEXPR scan_parse_context(string_view format)
28       : format_(format) {}
29 
begin()30   FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
end()31   FMT_CONSTEXPR iterator end() const { return format_.end(); }
32 
advance_to(iterator it)33   void advance_to(iterator it) {
34     format_.remove_prefix(detail::to_unsigned(it - begin()));
35   }
36 };
37 
38 struct scan_context {
39  private:
40   string_view input_;
41 
42  public:
43   using iterator = const char*;
44 
scan_contextscan_context45   explicit scan_context(string_view input) : input_(input) {}
46 
beginscan_context47   iterator begin() const { return input_.data(); }
endscan_context48   iterator end() const { return begin() + input_.size(); }
49 
advance_toscan_context50   void advance_to(iterator it) {
51     input_.remove_prefix(detail::to_unsigned(it - begin()));
52   }
53 };
54 
55 namespace detail {
56 enum class scan_type {
57   none_type,
58   int_type,
59   uint_type,
60   long_long_type,
61   ulong_long_type,
62   string_type,
63   string_view_type,
64   custom_type
65 };
66 
67 struct custom_scan_arg {
68   void* value;
69   void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
70 };
71 
72 class scan_arg {
73  public:
74   scan_type type;
75   union {
76     int* int_value;
77     unsigned* uint_value;
78     long long* long_long_value;
79     unsigned long long* ulong_long_value;
80     std::string* string;
81     fmt::string_view* string_view;
82     custom_scan_arg custom;
83     // TODO: more types
84   };
85 
scan_arg()86   scan_arg() : type(scan_type::none_type) {}
scan_arg(int & value)87   scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
scan_arg(unsigned & value)88   scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
scan_arg(long long & value)89   scan_arg(long long& value)
90       : type(scan_type::long_long_type), long_long_value(&value) {}
scan_arg(unsigned long long & value)91   scan_arg(unsigned long long& value)
92       : type(scan_type::ulong_long_type), ulong_long_value(&value) {}
scan_arg(std::string & value)93   scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
scan_arg(fmt::string_view & value)94   scan_arg(fmt::string_view& value)
95       : type(scan_type::string_view_type), string_view(&value) {}
scan_arg(T & value)96   template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
97     custom.value = &value;
98     custom.scan = scan_custom_arg<T>;
99   }
100 
101  private:
102   template <typename T>
scan_custom_arg(void * arg,scan_parse_context & parse_ctx,scan_context & ctx)103   static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
104                               scan_context& ctx) {
105     scanner<T> s;
106     parse_ctx.advance_to(s.parse(parse_ctx));
107     ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
108   }
109 };
110 }  // namespace detail
111 
112 struct scan_args {
113   int size;
114   const detail::scan_arg* data;
115 
116   template <size_t N>
scan_argsscan_args117   scan_args(const std::array<detail::scan_arg, N>& store)
118       : size(N), data(store.data()) {
119     static_assert(N < INT_MAX, "too many arguments");
120   }
121 };
122 
123 namespace detail {
124 
125 struct scan_handler : error_handler {
126  private:
127   scan_parse_context parse_ctx_;
128   scan_context scan_ctx_;
129   scan_args args_;
130   int next_arg_id_;
131   scan_arg arg_;
132 
read_uintscan_handler133   template <typename T = unsigned> T read_uint() {
134     T value = 0;
135     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
136     while (it != end) {
137       char c = *it++;
138       if (c < '0' || c > '9') on_error("invalid input");
139       // TODO: check overflow
140       value = value * 10 + static_cast<unsigned>(c - '0');
141     }
142     scan_ctx_.advance_to(it);
143     return value;
144   }
145 
read_intscan_handler146   template <typename T = int> T read_int() {
147     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
148     bool negative = it != end && *it == '-';
149     if (negative) ++it;
150     scan_ctx_.advance_to(it);
151     const auto value = read_uint<typename std::make_unsigned<T>::type>();
152     if (negative) return -static_cast<T>(value);
153     return static_cast<T>(value);
154   }
155 
156  public:
scan_handlerscan_handler157   scan_handler(string_view format, string_view input, scan_args args)
158       : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
159 
posscan_handler160   const char* pos() const { return scan_ctx_.begin(); }
161 
on_textscan_handler162   void on_text(const char* begin, const char* end) {
163     auto size = to_unsigned(end - begin);
164     auto it = scan_ctx_.begin();
165     if (it + size > scan_ctx_.end() ||
166         !std::equal(begin, end, make_checked(it, size))) {
167       on_error("invalid input");
168     }
169     scan_ctx_.advance_to(it + size);
170   }
171 
on_arg_idscan_handler172   int on_arg_id() { return on_arg_id(next_arg_id_++); }
on_arg_idscan_handler173   int on_arg_id(int id) {
174     if (id >= args_.size) on_error("argument index out of range");
175     arg_ = args_.data[id];
176     return id;
177   }
on_arg_idscan_handler178   int on_arg_id(string_view) { return on_error("invalid format"), 0; }
179 
on_replacement_fieldscan_handler180   void on_replacement_field(int, const char*) {
181     auto it = scan_ctx_.begin(), end = scan_ctx_.end();
182     switch (arg_.type) {
183     case scan_type::int_type:
184       *arg_.int_value = read_int();
185       break;
186     case scan_type::uint_type:
187       *arg_.uint_value = read_uint();
188       break;
189     case scan_type::long_long_type:
190       *arg_.long_long_value = read_int<long long>();
191       break;
192     case scan_type::ulong_long_type:
193       *arg_.ulong_long_value = read_uint<unsigned long long>();
194       break;
195     case scan_type::string_type:
196       while (it != end && *it != ' ') arg_.string->push_back(*it++);
197       scan_ctx_.advance_to(it);
198       break;
199     case scan_type::string_view_type: {
200       auto s = it;
201       while (it != end && *it != ' ') ++it;
202       *arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
203       scan_ctx_.advance_to(it);
204       break;
205     }
206     case scan_type::none_type:
207     case scan_type::custom_type:
208       assert(false);
209     }
210   }
211 
on_format_specsscan_handler212   const char* on_format_specs(int, const char* begin, const char*) {
213     if (arg_.type != scan_type::custom_type) return begin;
214     parse_ctx_.advance_to(begin);
215     arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
216     return parse_ctx_.begin();
217   }
218 };
219 }  // namespace detail
220 
221 template <typename... Args>
make_scan_args(Args &...args)222 std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
223   return {{args...}};
224 }
225 
vscan(string_view input,string_view format_str,scan_args args)226 string_view::iterator vscan(string_view input, string_view format_str,
227                             scan_args args) {
228   detail::scan_handler h(format_str, input, args);
229   detail::parse_format_string<false>(format_str, h);
230   return input.begin() + (h.pos() - &*input.begin());
231 }
232 
233 template <typename... Args>
scan(string_view input,string_view format_str,Args &...args)234 string_view::iterator scan(string_view input, string_view format_str,
235                            Args&... args) {
236   return vscan(input, format_str, make_scan_args(args...));
237 }
238 FMT_END_NAMESPACE
239