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