1 // Formatting library for C++ - scanning API proof of concept
3 // Copyright (c) 2019 - present, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
12 #include "fmt/format.h"
15 template <typename T
, typename Char
= char> struct scanner
{
16 // A deleted default constructor indicates a disabled scanner.
20 class scan_parse_context
{
25 using iterator
= string_view::iterator
;
27 explicit FMT_CONSTEXPR
scan_parse_context(string_view format
)
30 FMT_CONSTEXPR iterator
begin() const { return format_
.begin(); }
31 FMT_CONSTEXPR iterator
end() const { return format_
.end(); }
33 void advance_to(iterator it
) {
34 format_
.remove_prefix(detail::to_unsigned(it
- begin()));
43 using iterator
= const char*;
45 explicit scan_context(string_view input
) : input_(input
) {}
47 iterator
begin() const { return input_
.data(); }
48 iterator
end() const { return begin() + input_
.size(); }
50 void advance_to(iterator it
) {
51 input_
.remove_prefix(detail::to_unsigned(it
- begin()));
56 enum class scan_type
{
67 struct custom_scan_arg
{
69 void (*scan
)(void* arg
, scan_parse_context
& parse_ctx
, scan_context
& ctx
);
78 long long* long_long_value
;
79 unsigned long long* ulong_long_value
;
81 fmt::string_view
* string_view
;
82 custom_scan_arg custom
;
86 scan_arg() : type(scan_type::none_type
) {}
87 scan_arg(int& value
) : type(scan_type::int_type
), int_value(&value
) {}
88 scan_arg(unsigned& value
) : type(scan_type::uint_type
), uint_value(&value
) {}
89 scan_arg(long long& value
)
90 : type(scan_type::long_long_type
), long_long_value(&value
) {}
91 scan_arg(unsigned long long& value
)
92 : type(scan_type::ulong_long_type
), ulong_long_value(&value
) {}
93 scan_arg(std::string
& value
) : type(scan_type::string_type
), string(&value
) {}
94 scan_arg(fmt::string_view
& value
)
95 : type(scan_type::string_view_type
), string_view(&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
>;
102 template <typename T
>
103 static void scan_custom_arg(void* arg
, scan_parse_context
& parse_ctx
,
106 parse_ctx
.advance_to(s
.parse(parse_ctx
));
107 ctx
.advance_to(s
.scan(*static_cast<T
*>(arg
), ctx
));
110 } // namespace detail
114 const detail::scan_arg
* data
;
117 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");
125 struct scan_handler
: error_handler
{
127 scan_parse_context parse_ctx_
;
128 scan_context scan_ctx_
;
133 template <typename T
= unsigned> T
read_uint() {
135 auto it
= scan_ctx_
.begin(), end
= scan_ctx_
.end();
138 if (c
< '0' || c
> '9') on_error("invalid input");
139 // TODO: check overflow
140 value
= value
* 10 + static_cast<unsigned>(c
- '0');
142 scan_ctx_
.advance_to(it
);
146 template <typename T
= int> T
read_int() {
147 auto it
= scan_ctx_
.begin(), end
= scan_ctx_
.end();
148 bool negative
= it
!= end
&& *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
);
157 scan_handler(string_view format
, string_view input
, scan_args args
)
158 : parse_ctx_(format
), scan_ctx_(input
), args_(args
), next_arg_id_(0) {}
160 const char* pos() const { return scan_ctx_
.begin(); }
162 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");
169 scan_ctx_
.advance_to(it
+ size
);
172 void on_arg_id() { on_arg_id(next_arg_id_
++); }
173 void on_arg_id(int id
) {
174 if (id
>= args_
.size
) on_error("argument index out of range");
175 arg_
= args_
.data
[id
];
177 void on_arg_id(string_view
) { on_error("invalid format"); }
179 void on_replacement_field(const char*) {
180 auto it
= scan_ctx_
.begin(), end
= scan_ctx_
.end();
182 case scan_type::int_type
:
183 *arg_
.int_value
= read_int();
185 case scan_type::uint_type
:
186 *arg_
.uint_value
= read_uint();
188 case scan_type::long_long_type
:
189 *arg_
.long_long_value
= read_int
<long long>();
191 case scan_type::ulong_long_type
:
192 *arg_
.ulong_long_value
= read_uint
<unsigned long long>();
194 case scan_type::string_type
:
195 while (it
!= end
&& *it
!= ' ') arg_
.string
->push_back(*it
++);
196 scan_ctx_
.advance_to(it
);
198 case scan_type::string_view_type
: {
200 while (it
!= end
&& *it
!= ' ') ++it
;
201 *arg_
.string_view
= fmt::string_view(s
, to_unsigned(it
- s
));
202 scan_ctx_
.advance_to(it
);
205 case scan_type::none_type
:
206 case scan_type::custom_type
:
211 const char* on_format_specs(const char* begin
, const char*) {
212 if (arg_
.type
!= scan_type::custom_type
) return begin
;
213 parse_ctx_
.advance_to(begin
);
214 arg_
.custom
.scan(arg_
.custom
.value
, parse_ctx_
, scan_ctx_
);
215 return parse_ctx_
.begin();
218 } // namespace detail
220 template <typename
... Args
>
221 std::array
<detail::scan_arg
, sizeof...(Args
)> make_scan_args(Args
&... args
) {
225 string_view::iterator
vscan(string_view input
, string_view format_str
,
227 detail::scan_handler
h(format_str
, input
, args
);
228 detail::parse_format_string
<false>(format_str
, h
);
229 return input
.begin() + (h
.pos() - &*input
.begin());
232 template <typename
... Args
>
233 string_view::iterator
scan(string_view input
, string_view format_str
,
235 return vscan(input
, format_str
, make_scan_args(args
...));