scan.h 6.8 KB

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