scan.h
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 27 explicit FMT_CONSTEXPR scan_parse_context(string_view format) 28 : format_(format) {} 29 30 FMT_CONSTEXPR iterator begin() const { return format_.begin(); } 31 FMT_CONSTEXPR iterator end() const { return format_.end(); } 32 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 45 explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {} 46 47 iterator begin() const { return input_.data(); } 48 iterator end() const { return begin() + input_.size(); } 49 50 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 86 FMT_CONSTEXPR scan_arg() : type(scan_type::none_type), int_value(nullptr) {} 87 FMT_CONSTEXPR scan_arg(int& value) 88 : type(scan_type::int_type), int_value(&value) {} 89 FMT_CONSTEXPR scan_arg(unsigned& value) 90 : type(scan_type::uint_type), uint_value(&value) {} 91 FMT_CONSTEXPR scan_arg(long long& value) 92 : type(scan_type::long_long_type), long_long_value(&value) {} 93 FMT_CONSTEXPR scan_arg(unsigned long long& value) 94 : type(scan_type::ulong_long_type), ulong_long_value(&value) {} 95 FMT_CONSTEXPR scan_arg(std::string& value) 96 : type(scan_type::string_type), string(&value) {} 97 FMT_CONSTEXPR scan_arg(fmt::string_view& value) 98 : type(scan_type::string_view_type), string_view(&value) {} 99 template <typename T> 100 FMT_CONSTEXPR scan_arg(T& value) : type(scan_type::custom_type) { 101 custom.value = &value; 102 custom.scan = scan_custom_arg<T>; 103 } 104 105 private: 106 template <typename T> 107 static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx, 108 scan_context& ctx) { 109 scanner<T> s; 110 parse_ctx.advance_to(s.parse(parse_ctx)); 111 ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx)); 112 } 113 }; 114 } // namespace detail 115 116 struct scan_args { 117 int size; 118 const detail::scan_arg* data; 119 120 template <size_t N> 121 FMT_CONSTEXPR scan_args(const std::array<detail::scan_arg, N>& store) 122 : size(N), data(store.data()) { 123 static_assert(N < INT_MAX, "too many arguments"); 124 } 125 }; 126 127 namespace detail { 128 129 struct scan_handler : error_handler { 130 private: 131 scan_parse_context parse_ctx_; 132 scan_context scan_ctx_; 133 scan_args args_; 134 int next_arg_id_; 135 scan_arg arg_; 136 137 template <typename T = unsigned> T read_uint() { 138 T value = 0; 139 auto it = scan_ctx_.begin(), end = scan_ctx_.end(); 140 while (it != end) { 141 char c = *it++; 142 if (c < '0' || c > '9') on_error("invalid input"); 143 // TODO: check overflow 144 value = value * 10 + static_cast<unsigned>(c - '0'); 145 } 146 scan_ctx_.advance_to(it); 147 return value; 148 } 149 150 template <typename T = int> T read_int() { 151 auto it = scan_ctx_.begin(), end = scan_ctx_.end(); 152 bool negative = it != end && *it == '-'; 153 if (negative) ++it; 154 scan_ctx_.advance_to(it); 155 const auto value = read_uint<typename std::make_unsigned<T>::type>(); 156 if (negative) return -static_cast<T>(value); 157 return static_cast<T>(value); 158 } 159 160 public: 161 FMT_CONSTEXPR scan_handler(string_view format, string_view input, 162 scan_args args) 163 : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {} 164 165 const char* pos() const { return scan_ctx_.begin(); } 166 167 void on_text(const char* begin, const char* end) { 168 auto size = to_unsigned(end - begin); 169 auto it = scan_ctx_.begin(); 170 if (it + size > scan_ctx_.end() || !std::equal(begin, end, it)) 171 on_error("invalid input"); 172 scan_ctx_.advance_to(it + size); 173 } 174 175 FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); } 176 FMT_CONSTEXPR int on_arg_id(int id) { 177 if (id >= args_.size) on_error("argument index out of range"); 178 arg_ = args_.data[id]; 179 return id; 180 } 181 FMT_CONSTEXPR int on_arg_id(string_view id) { 182 if (id.data()) on_error("invalid format"); 183 return 0; 184 } 185 186 void on_replacement_field(int, const char*) { 187 auto it = scan_ctx_.begin(), end = scan_ctx_.end(); 188 switch (arg_.type) { 189 case scan_type::int_type: 190 *arg_.int_value = read_int(); 191 break; 192 case scan_type::uint_type: 193 *arg_.uint_value = read_uint(); 194 break; 195 case scan_type::long_long_type: 196 *arg_.long_long_value = read_int<long long>(); 197 break; 198 case scan_type::ulong_long_type: 199 *arg_.ulong_long_value = read_uint<unsigned long long>(); 200 break; 201 case scan_type::string_type: 202 while (it != end && *it != ' ') arg_.string->push_back(*it++); 203 scan_ctx_.advance_to(it); 204 break; 205 case scan_type::string_view_type: { 206 auto s = it; 207 while (it != end && *it != ' ') ++it; 208 *arg_.string_view = fmt::string_view(s, to_unsigned(it - s)); 209 scan_ctx_.advance_to(it); 210 break; 211 } 212 case scan_type::none_type: 213 case scan_type::custom_type: 214 assert(false); 215 } 216 } 217 218 const char* on_format_specs(int, const char* begin, const char*) { 219 if (arg_.type != scan_type::custom_type) return begin; 220 parse_ctx_.advance_to(begin); 221 arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); 222 return parse_ctx_.begin(); 223 } 224 }; 225 } // namespace detail 226 227 template <typename... Args> 228 std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) { 229 return {{args...}}; 230 } 231 232 string_view::iterator vscan(string_view input, string_view format_str, 233 scan_args args) { 234 detail::scan_handler h(format_str, input, args); 235 detail::parse_format_string<false>(format_str, h); 236 return input.begin() + (h.pos() - &*input.begin()); 237 } 238 239 template <typename... Args> 240 string_view::iterator scan(string_view input, string_view format_str, 241 Args&... args) { 242 return vscan(input, format_str, make_scan_args(args...)); 243 } 244 FMT_END_NAMESPACE