scan.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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 <tuple>
  11. #include "fmt/format-inl.h"
  12. FMT_BEGIN_NAMESPACE
  13. namespace detail {
  14. inline auto is_whitespace(char c) -> bool { return c == ' ' || c == '\n'; }
  15. // If c is a hex digit returns its numeric value, otherwise -1.
  16. inline auto to_hex_digit(char c) -> int {
  17. if (c >= '0' && c <= '9') return c - '0';
  18. if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  19. if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  20. return -1;
  21. }
  22. struct maybe_contiguous_range {
  23. const char* begin;
  24. const char* end;
  25. explicit operator bool() const { return begin != nullptr; }
  26. };
  27. class scan_buffer {
  28. private:
  29. const char* ptr_;
  30. const char* end_;
  31. bool contiguous_;
  32. protected:
  33. scan_buffer(const char* ptr, const char* end, bool contiguous)
  34. : ptr_(ptr), end_(end), contiguous_(contiguous) {}
  35. ~scan_buffer() = default;
  36. void set(span<const char> buf) {
  37. ptr_ = buf.data;
  38. end_ = buf.data + buf.size;
  39. }
  40. auto ptr() const -> const char* { return ptr_; }
  41. public:
  42. scan_buffer(const scan_buffer&) = delete;
  43. void operator=(const scan_buffer&) = delete;
  44. // Fills the buffer with more input if available.
  45. virtual void consume() = 0;
  46. class sentinel {};
  47. class iterator {
  48. private:
  49. const char** ptr_;
  50. scan_buffer* buf_; // This could be merged with ptr_.
  51. char value_;
  52. static auto get_sentinel() -> const char** {
  53. static const char* ptr = nullptr;
  54. return &ptr;
  55. }
  56. friend class scan_buffer;
  57. friend auto operator==(iterator lhs, sentinel) -> bool {
  58. return *lhs.ptr_ == nullptr;
  59. }
  60. friend auto operator!=(iterator lhs, sentinel) -> bool {
  61. return *lhs.ptr_ != nullptr;
  62. }
  63. iterator(scan_buffer* buf) : buf_(buf) {
  64. if (buf->ptr_ == buf->end_) {
  65. ptr_ = get_sentinel();
  66. return;
  67. }
  68. ptr_ = &buf->ptr_;
  69. value_ = *buf->ptr_;
  70. }
  71. friend scan_buffer& get_buffer(iterator it) { return *it.buf_; }
  72. public:
  73. iterator() : ptr_(get_sentinel()), buf_(nullptr) {}
  74. auto operator++() -> iterator& {
  75. if (!buf_->try_consume()) ptr_ = get_sentinel();
  76. value_ = *buf_->ptr_;
  77. return *this;
  78. }
  79. auto operator++(int) -> iterator {
  80. iterator copy = *this;
  81. ++*this;
  82. return copy;
  83. }
  84. auto operator*() const -> char { return value_; }
  85. auto base() const -> const char* { return buf_->ptr_; }
  86. friend auto to_contiguous(iterator it) -> maybe_contiguous_range;
  87. friend auto advance(iterator it, size_t n) -> iterator;
  88. };
  89. friend auto to_contiguous(iterator it) -> maybe_contiguous_range {
  90. if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_};
  91. return {nullptr, nullptr};
  92. }
  93. friend auto advance(iterator it, size_t n) -> iterator {
  94. FMT_ASSERT(it.buf_->is_contiguous(), "");
  95. const char*& ptr = it.buf_->ptr_;
  96. ptr += n;
  97. it.value_ = *ptr;
  98. if (ptr == it.buf_->end_) it.ptr_ = iterator::get_sentinel();
  99. return it;
  100. }
  101. auto begin() -> iterator { return this; }
  102. auto end() -> sentinel { return {}; }
  103. auto is_contiguous() const -> bool { return contiguous_; }
  104. // Tries consuming a single code unit. Returns true iff there is more input.
  105. auto try_consume() -> bool {
  106. FMT_ASSERT(ptr_ != end_, "");
  107. ++ptr_;
  108. if (ptr_ != end_) return true;
  109. consume();
  110. return ptr_ != end_;
  111. }
  112. };
  113. using scan_iterator = scan_buffer::iterator;
  114. using scan_sentinel = scan_buffer::sentinel;
  115. class string_scan_buffer final : public scan_buffer {
  116. private:
  117. void consume() override {}
  118. public:
  119. explicit string_scan_buffer(string_view s)
  120. : scan_buffer(s.begin(), s.end(), true) {}
  121. };
  122. class file_scan_buffer final : public scan_buffer {
  123. private:
  124. template <typename F, FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 &&
  125. !FMT_USE_FALLBACK_FILE)>
  126. static auto get_file(F* f, int) -> glibc_file<F> {
  127. return f;
  128. }
  129. template <typename F,
  130. FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
  131. static auto get_file(F* f, int) -> apple_file<F> {
  132. return f;
  133. }
  134. static auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
  135. decltype(get_file(static_cast<FILE*>(nullptr), 0)) file_;
  136. // Fills the buffer if it is empty.
  137. void fill() {
  138. span<const char> buf = file_.get_read_buffer();
  139. if (buf.size == 0) {
  140. int c = file_.get();
  141. // Put the character back since we are only filling the buffer.
  142. if (c != EOF) file_.unget(static_cast<char>(c));
  143. buf = file_.get_read_buffer();
  144. }
  145. set(buf);
  146. }
  147. void consume() override {
  148. // Consume the current buffer content.
  149. size_t n = to_unsigned(ptr() - file_.get_read_buffer().data);
  150. for (size_t i = 0; i != n; ++i) file_.get();
  151. fill();
  152. }
  153. public:
  154. explicit file_scan_buffer(FILE* f)
  155. : scan_buffer(nullptr, nullptr, false), file_(f) {
  156. flockfile(f);
  157. fill();
  158. }
  159. ~file_scan_buffer() {
  160. FILE* f = file_;
  161. funlockfile(f);
  162. }
  163. };
  164. } // namespace detail
  165. template <typename T, typename Char = char> struct scanner {
  166. // A deleted default constructor indicates a disabled scanner.
  167. scanner() = delete;
  168. };
  169. class scan_parse_context {
  170. private:
  171. string_view format_;
  172. public:
  173. using iterator = string_view::iterator;
  174. FMT_CONSTEXPR explicit scan_parse_context(string_view format)
  175. : format_(format) {}
  176. FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
  177. FMT_CONSTEXPR auto end() const -> iterator { return format_.end(); }
  178. void advance_to(iterator it) {
  179. format_.remove_prefix(detail::to_unsigned(it - begin()));
  180. }
  181. };
  182. namespace detail {
  183. enum class scan_type {
  184. none_type,
  185. int_type,
  186. uint_type,
  187. long_long_type,
  188. ulong_long_type,
  189. string_type,
  190. string_view_type,
  191. custom_type
  192. };
  193. template <typename Context> struct custom_scan_arg {
  194. void* value;
  195. void (*scan)(void* arg, scan_parse_context& parse_ctx, Context& ctx);
  196. };
  197. } // namespace detail
  198. // A scan argument. Context is a template parameter for the compiled API where
  199. // output can be unbuffered.
  200. template <typename Context> class basic_scan_arg {
  201. private:
  202. using scan_type = detail::scan_type;
  203. scan_type type_;
  204. union {
  205. int* int_value_;
  206. unsigned* uint_value_;
  207. long long* long_long_value_;
  208. unsigned long long* ulong_long_value_;
  209. std::string* string_;
  210. string_view* string_view_;
  211. detail::custom_scan_arg<Context> custom_;
  212. // TODO: more types
  213. };
  214. template <typename T>
  215. static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
  216. Context& ctx) {
  217. auto s = scanner<T>();
  218. parse_ctx.advance_to(s.parse(parse_ctx));
  219. ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
  220. }
  221. public:
  222. FMT_CONSTEXPR basic_scan_arg()
  223. : type_(scan_type::none_type), int_value_(nullptr) {}
  224. FMT_CONSTEXPR basic_scan_arg(int& value)
  225. : type_(scan_type::int_type), int_value_(&value) {}
  226. FMT_CONSTEXPR basic_scan_arg(unsigned& value)
  227. : type_(scan_type::uint_type), uint_value_(&value) {}
  228. FMT_CONSTEXPR basic_scan_arg(long long& value)
  229. : type_(scan_type::long_long_type), long_long_value_(&value) {}
  230. FMT_CONSTEXPR basic_scan_arg(unsigned long long& value)
  231. : type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
  232. FMT_CONSTEXPR basic_scan_arg(std::string& value)
  233. : type_(scan_type::string_type), string_(&value) {}
  234. FMT_CONSTEXPR basic_scan_arg(string_view& value)
  235. : type_(scan_type::string_view_type), string_view_(&value) {}
  236. template <typename T>
  237. FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) {
  238. custom_.value = &value;
  239. custom_.scan = scan_custom_arg<T>;
  240. }
  241. constexpr explicit operator bool() const noexcept {
  242. return type_ != scan_type::none_type;
  243. }
  244. auto type() const -> detail::scan_type { return type_; }
  245. template <typename Visitor>
  246. auto visit(Visitor&& vis) -> decltype(vis(monostate())) {
  247. switch (type_) {
  248. case scan_type::none_type:
  249. break;
  250. case scan_type::int_type:
  251. return vis(*int_value_);
  252. case scan_type::uint_type:
  253. return vis(*uint_value_);
  254. case scan_type::long_long_type:
  255. return vis(*long_long_value_);
  256. case scan_type::ulong_long_type:
  257. return vis(*ulong_long_value_);
  258. case scan_type::string_type:
  259. return vis(*string_);
  260. case scan_type::string_view_type:
  261. return vis(*string_view_);
  262. case scan_type::custom_type:
  263. break;
  264. }
  265. return vis(monostate());
  266. }
  267. auto scan_custom(const char* parse_begin, scan_parse_context& parse_ctx,
  268. Context& ctx) const -> bool {
  269. if (type_ != scan_type::custom_type) return false;
  270. parse_ctx.advance_to(parse_begin);
  271. custom_.scan(custom_.value, parse_ctx, ctx);
  272. return true;
  273. }
  274. };
  275. class scan_context;
  276. using scan_arg = basic_scan_arg<scan_context>;
  277. struct scan_args {
  278. int size;
  279. const scan_arg* data;
  280. template <size_t N>
  281. FMT_CONSTEXPR scan_args(const std::array<scan_arg, N>& store)
  282. : size(N), data(store.data()) {
  283. static_assert(N < INT_MAX, "too many arguments");
  284. }
  285. };
  286. class scan_context {
  287. private:
  288. detail::scan_buffer& buf_;
  289. scan_args args_;
  290. public:
  291. using iterator = detail::scan_iterator;
  292. using sentinel = detail::scan_sentinel;
  293. FMT_CONSTEXPR explicit scan_context(detail::scan_buffer& buf, scan_args args)
  294. : buf_(buf), args_(args) {}
  295. FMT_CONSTEXPR auto arg(int id) const -> scan_arg {
  296. return id < args_.size ? args_.data[id] : scan_arg();
  297. }
  298. auto begin() const -> iterator { return buf_.begin(); }
  299. auto end() const -> sentinel { return {}; }
  300. void advance_to(iterator) { buf_.consume(); }
  301. };
  302. namespace detail {
  303. const char* parse_scan_specs(const char* begin, const char* end,
  304. format_specs& specs, scan_type) {
  305. while (begin != end) {
  306. switch (to_ascii(*begin)) {
  307. // TODO: parse more scan format specifiers
  308. case 'x':
  309. specs.set_type(presentation_type::hex);
  310. ++begin;
  311. break;
  312. case '}':
  313. return begin;
  314. }
  315. }
  316. return begin;
  317. }
  318. template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
  319. auto read(scan_iterator it, T& value) -> scan_iterator {
  320. if (it == scan_sentinel()) return it;
  321. char c = *it;
  322. if (c < '0' || c > '9') report_error("invalid input");
  323. int num_digits = 0;
  324. T n = 0, prev = 0;
  325. char prev_digit = c;
  326. do {
  327. prev = n;
  328. n = n * 10 + static_cast<unsigned>(c - '0');
  329. prev_digit = c;
  330. c = *++it;
  331. ++num_digits;
  332. if (c < '0' || c > '9') break;
  333. } while (it != scan_sentinel());
  334. // Check overflow.
  335. if (num_digits <= std::numeric_limits<int>::digits10) {
  336. value = n;
  337. return it;
  338. }
  339. unsigned max = to_unsigned((std::numeric_limits<int>::max)());
  340. if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
  341. prev * 10ull + unsigned(prev_digit - '0') <= max) {
  342. value = n;
  343. } else {
  344. report_error("number is too big");
  345. }
  346. return it;
  347. }
  348. template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
  349. auto read_hex(scan_iterator it, T& value) -> scan_iterator {
  350. if (it == scan_sentinel()) return it;
  351. int digit = to_hex_digit(*it);
  352. if (digit < 0) report_error("invalid input");
  353. int num_digits = 0;
  354. T n = 0;
  355. do {
  356. n = (n << 4) + static_cast<unsigned>(digit);
  357. ++num_digits;
  358. digit = to_hex_digit(*++it);
  359. if (digit < 0) break;
  360. } while (it != scan_sentinel());
  361. // Check overflow.
  362. if (num_digits <= (std::numeric_limits<T>::digits >> 2))
  363. value = n;
  364. else
  365. report_error("number is too big");
  366. return it;
  367. }
  368. template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
  369. auto read(scan_iterator it, T& value, const format_specs& specs)
  370. -> scan_iterator {
  371. if (specs.type() == presentation_type::hex) return read_hex(it, value);
  372. return read(it, value);
  373. }
  374. template <typename T, FMT_ENABLE_IF(std::is_signed<T>::value)>
  375. auto read(scan_iterator it, T& value, const format_specs& specs = {})
  376. -> scan_iterator {
  377. bool negative = it != scan_sentinel() && *it == '-';
  378. if (negative) {
  379. ++it;
  380. if (it == scan_sentinel()) report_error("invalid input");
  381. }
  382. using unsigned_type = typename std::make_unsigned<T>::type;
  383. unsigned_type abs_value = 0;
  384. it = read(it, abs_value, specs);
  385. auto n = static_cast<T>(abs_value);
  386. value = negative ? -n : n;
  387. return it;
  388. }
  389. auto read(scan_iterator it, std::string& value, const format_specs& = {})
  390. -> scan_iterator {
  391. while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
  392. return it;
  393. }
  394. auto read(scan_iterator it, string_view& value, const format_specs& = {})
  395. -> scan_iterator {
  396. auto range = to_contiguous(it);
  397. // This could also be checked at compile time in scan.
  398. if (!range) report_error("string_view requires contiguous input");
  399. auto p = range.begin;
  400. while (p != range.end && *p != ' ') ++p;
  401. size_t size = to_unsigned(p - range.begin);
  402. value = {range.begin, size};
  403. return advance(it, size);
  404. }
  405. auto read(scan_iterator it, monostate, const format_specs& = {})
  406. -> scan_iterator {
  407. return it;
  408. }
  409. // An argument scanner that uses the default format, e.g. decimal for integers.
  410. struct default_arg_scanner {
  411. scan_iterator it;
  412. template <typename T> FMT_INLINE auto operator()(T&& value) -> scan_iterator {
  413. return read(it, value);
  414. }
  415. };
  416. // An argument scanner with format specifiers.
  417. struct arg_scanner {
  418. scan_iterator it;
  419. const format_specs& specs;
  420. template <typename T> auto operator()(T&& value) -> scan_iterator {
  421. return read(it, value, specs);
  422. }
  423. };
  424. struct scan_handler {
  425. private:
  426. scan_parse_context parse_ctx_;
  427. scan_context scan_ctx_;
  428. int next_arg_id_;
  429. using sentinel = scan_buffer::sentinel;
  430. public:
  431. FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf,
  432. scan_args args)
  433. : parse_ctx_(format), scan_ctx_(buf, args), next_arg_id_(0) {}
  434. auto pos() const -> scan_buffer::iterator { return scan_ctx_.begin(); }
  435. void on_text(const char* begin, const char* end) {
  436. if (begin == end) return;
  437. auto it = scan_ctx_.begin();
  438. for (; begin != end; ++begin, ++it) {
  439. if (it == sentinel() || *begin != *it) on_error("invalid input");
  440. }
  441. scan_ctx_.advance_to(it);
  442. }
  443. FMT_CONSTEXPR auto on_arg_id() -> int { return on_arg_id(next_arg_id_++); }
  444. FMT_CONSTEXPR auto on_arg_id(int id) -> int {
  445. if (!scan_ctx_.arg(id)) on_error("argument index out of range");
  446. return id;
  447. }
  448. FMT_CONSTEXPR auto on_arg_id(string_view id) -> int {
  449. if (id.data()) on_error("invalid format");
  450. return 0;
  451. }
  452. void on_replacement_field(int arg_id, const char* begin) {
  453. scan_arg arg = scan_ctx_.arg(arg_id);
  454. if (arg.scan_custom(begin, parse_ctx_, scan_ctx_)) return;
  455. auto it = scan_ctx_.begin();
  456. while (it != sentinel() && is_whitespace(*it)) ++it;
  457. scan_ctx_.advance_to(arg.visit(default_arg_scanner{it}));
  458. }
  459. auto on_format_specs(int arg_id, const char* begin, const char* end) -> const
  460. char* {
  461. scan_arg arg = scan_ctx_.arg(arg_id);
  462. if (arg.scan_custom(begin, parse_ctx_, scan_ctx_))
  463. return parse_ctx_.begin();
  464. auto specs = format_specs();
  465. begin = parse_scan_specs(begin, end, specs, arg.type());
  466. if (begin == end || *begin != '}') on_error("missing '}' in format string");
  467. scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs}));
  468. return begin;
  469. }
  470. FMT_NORETURN void on_error(const char* message) { report_error(message); }
  471. };
  472. void vscan(detail::scan_buffer& buf, string_view fmt, scan_args args) {
  473. auto h = detail::scan_handler(fmt, buf, args);
  474. detail::parse_format_string(fmt, h);
  475. }
  476. template <size_t I, typename... T, FMT_ENABLE_IF(I == sizeof...(T))>
  477. void make_args(std::array<scan_arg, sizeof...(T)>&, std::tuple<T...>&) {}
  478. template <size_t I, typename... T, FMT_ENABLE_IF(I < sizeof...(T))>
  479. void make_args(std::array<scan_arg, sizeof...(T)>& args,
  480. std::tuple<T...>& values) {
  481. using element_type = typename std::tuple_element<I, std::tuple<T...>>::type;
  482. static_assert(std::is_same<remove_cvref_t<element_type>, element_type>::value,
  483. "");
  484. args[I] = std::get<I>(values);
  485. make_args<I + 1>(args, values);
  486. }
  487. } // namespace detail
  488. template <typename Range, typename... T> class scan_data {
  489. private:
  490. std::tuple<T...> values_;
  491. Range range_;
  492. public:
  493. scan_data() = default;
  494. scan_data(T... values) : values_(std::move(values)...) {}
  495. auto value() const -> decltype(std::get<0>(values_)) {
  496. return std::get<0>(values_);
  497. }
  498. auto values() const -> const std::tuple<T...>& { return values_; }
  499. auto make_args() -> std::array<scan_arg, sizeof...(T)> {
  500. auto args = std::array<scan_arg, sizeof...(T)>();
  501. detail::make_args<0>(args, values_);
  502. return args;
  503. }
  504. auto range() const -> Range { return range_; }
  505. auto begin() const -> decltype(range_.begin()) { return range_.begin(); }
  506. auto end() const -> decltype(range_.end()) { return range_.end(); }
  507. };
  508. template <typename... T>
  509. auto make_scan_args(T&... args) -> std::array<scan_arg, sizeof...(T)> {
  510. return {{args...}};
  511. }
  512. class scan_error {};
  513. // A rudimentary version of std::expected for testing the API shape.
  514. template <typename T, typename E> class expected {
  515. private:
  516. T value_;
  517. bool has_value_ = true;
  518. public:
  519. expected(T value) : value_(std::move(value)) {}
  520. explicit operator bool() const { return has_value_; }
  521. auto operator->() const -> const T* { return &value_; }
  522. auto error() -> E const { return E(); }
  523. };
  524. template <typename Range, typename... T>
  525. using scan_result = expected<scan_data<Range, T...>, scan_error>;
  526. auto vscan(string_view input, string_view fmt, scan_args args)
  527. -> string_view::iterator {
  528. auto&& buf = detail::string_scan_buffer(input);
  529. detail::vscan(buf, fmt, args);
  530. return input.begin() + (buf.begin().base() - input.data());
  531. }
  532. // Scans the input and stores the results (in)to args.
  533. template <typename... T>
  534. auto scan_to(string_view input, string_view fmt, T&... args)
  535. -> string_view::iterator {
  536. return vscan(input, fmt, make_scan_args(args...));
  537. }
  538. template <typename... T>
  539. auto scan(string_view input, string_view fmt)
  540. -> scan_result<string_view, T...> {
  541. auto data = scan_data<string_view, T...>();
  542. vscan(input, fmt, data.make_args());
  543. return data;
  544. }
  545. template <typename Range, typename... T,
  546. FMT_ENABLE_IF(!std::is_convertible<Range, string_view>::value)>
  547. auto scan_to(Range&& input, string_view fmt, T&... args)
  548. -> decltype(std::begin(input)) {
  549. auto it = std::begin(input);
  550. detail::vscan(get_buffer(it), fmt, make_scan_args(args...));
  551. return it;
  552. }
  553. template <typename... T>
  554. auto scan_to(FILE* f, string_view fmt, T&... args) -> bool {
  555. auto&& buf = detail::file_scan_buffer(f);
  556. detail::vscan(buf, fmt, make_scan_args(args...));
  557. return buf.begin() != buf.end();
  558. }
  559. FMT_END_NAMESPACE