scnlib  0.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
reader.h
Go to the documentation of this file.
1 // Copyright 2017-2019 Elias Kosunen
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // This file is a part of scnlib:
16 // https://github.com/eliaskosunen/scnlib
17 
18 #ifndef SCN_DETAIL_READER_H
19 #define SCN_DETAIL_READER_H
20 
21 #include "args.h"
22 #include "locale.h"
23 #include "range.h"
24 #include "result.h"
25 #include "small_vector.h"
26 #include "span.h"
27 
28 namespace scn {
30 
38 
40  // read_char
41 
43 
51  template <typename WrappedRange,
54  WrappedRange& r)
55  {
56  if (r.begin() == r.end()) {
57  return error(error::end_of_range, "EOF");
58  }
59  auto ch = *r.begin();
60  r.advance();
61  return {ch};
62  }
63  template <
64  typename WrappedRange,
67  {
68  if (r.begin() == r.end()) {
69  return error(error::end_of_range, "EOF");
70  }
71  auto ch = *r.begin();
72  if (ch) {
73  r.advance();
74  }
75  return ch;
76  }
78 
79  // read_zero_copy
80 
82 
92  template <
93  typename WrappedRange,
95  expected<span<const typename detail::extract_char_type<
96  typename WrappedRange::iterator>::type>>
97  read_zero_copy(WrappedRange& r,
99  {
100  if (r.begin() == r.end()) {
101  return error(error::end_of_range, "EOF");
102  }
103  const auto n_to_read = detail::min(r.size(), n);
104  auto s = make_span(r.data(), static_cast<size_t>(n_to_read)).as_const();
105  r.advance(n_to_read);
106  return s;
107  }
108  template <
109  typename WrappedRange,
111  expected<span<const typename detail::extract_char_type<
112  typename WrappedRange::iterator>::type>>
113  read_zero_copy(WrappedRange& r,
114  detail::ranges::range_difference_t<WrappedRange>)
115  {
116  if (r.begin() == r.end()) {
117  return error(error::end_of_range, "EOF");
118  }
119  return span<const typename detail::extract_char_type<
120  typename WrappedRange::iterator>::type>{};
121  }
123 
124  // read_all_zero_copy
125 
127 
133  template <
134  typename WrappedRange,
136  expected<span<const typename detail::extract_char_type<
137  typename WrappedRange::iterator>::type>>
138  read_all_zero_copy(WrappedRange& r)
139  {
140  if (r.begin() == r.end()) {
141  return error(error::end_of_range, "EOF");
142  }
143  auto s = make_span(r.data(), static_cast<size_t>(r.size())).as_const();
144  r.advance(r.size());
145  return s;
146  }
147  template <
148  typename WrappedRange,
150  expected<span<const typename detail::extract_char_type<
151  typename WrappedRange::iterator>::type>>
152  read_all_zero_copy(WrappedRange& r)
153  {
154  if (r.begin() == r.end()) {
155  return error(error::end_of_range, "EOF");
156  }
157  return span<const typename detail::extract_char_type<
158  typename WrappedRange::iterator>::type>{};
159  }
161 
162  // read_into
163 
165 
172  template <
173  typename WrappedRange,
174  typename OutputIterator,
176  error read_into(WrappedRange& r,
177  OutputIterator& it,
179  {
180  auto s = read_zero_copy(r, n);
181  if (!s) {
182  return s.error();
183  }
184  if (s.value().ssize() != n) {
185  return error(error::end_of_range, "EOF");
186  }
187  it = std::copy(s.value().begin(), s.value().end(), it);
188  return {};
189  }
190  template <typename WrappedRange,
191  typename OutputIterator,
192  typename std::enable_if<!WrappedRange::is_contiguous &&
193  WrappedRange::is_direct>::type* = nullptr>
194  error read_into(WrappedRange& r,
195  OutputIterator& it,
196  detail::ranges::range_difference_t<WrappedRange> n)
197  {
198  if (r.begin() == r.end()) {
199  return error(error::end_of_range, "EOF");
200  }
201  for (detail::ranges::range_difference_t<WrappedRange> i = 0; i < n;
202  ++i) {
203  if (r.begin() == r.end()) {
204  return error(error::end_of_range, "EOF");
205  }
206  *it = *r.begin();
207  ++it;
208  r.advance();
209  }
210  return {};
211  }
212  template <
213  typename WrappedRange,
214  typename OutputIterator,
215  typename std::enable_if<!WrappedRange::is_contiguous &&
216  !WrappedRange::is_direct>::type* = nullptr>
217  error read_into(WrappedRange& r,
218  OutputIterator& it,
219  detail::ranges::range_difference_t<WrappedRange> n)
220  {
221  if (r.begin() == r.end()) {
222  return error(error::end_of_range, "EOF");
223  }
224  for (detail::ranges::range_difference_t<WrappedRange> i = 0; i < n;
225  ++i) {
226  if (r.begin() == r.end()) {
227  return error(error::end_of_range, "EOF");
228  }
229  auto tmp = *r.begin();
230  if (!tmp) {
231  return tmp.error();
232  }
233  *it = tmp.value();
234  r.advance();
235  }
236  return {};
237  }
239 
240  // read_until_space_zero_copy
241 
243 
256  template <
257  typename WrappedRange,
258  typename Predicate,
260  expected<span<const typename detail::extract_char_type<
261  typename WrappedRange::iterator>::type>>
262  read_until_space_zero_copy(WrappedRange& r,
263  Predicate is_space,
264  bool keep_final_space)
265  {
266  auto it = r.begin();
267  const auto end = r.end();
268  if (it == end) {
269  return error(error::end_of_range, "EOF");
270  }
271  for (; it != end; ++it) {
272  if (is_space(*it)) {
273  auto b = r.begin();
274  r.advance_to(it);
275  if (keep_final_space) {
276  ++it;
277  r.advance();
278  }
279  return {{&*b, &*it}};
280  }
281  }
282  auto b = r.begin();
283  r.advance_to(r.end());
284  return {{&*b, &*(r.end() - 1) + 1}};
285  }
286  template <
287  typename WrappedRange,
288  typename Predicate,
290  expected<span<const typename detail::extract_char_type<
291  typename WrappedRange::iterator>::type>>
292  read_until_space_zero_copy(WrappedRange& r, Predicate, bool)
293  {
294  if (r.begin() == r.end()) {
295  return error(error::end_of_range, "EOF");
296  }
297  return span<const typename detail::extract_char_type<
298  typename WrappedRange::iterator>::type>{};
299  }
301 
302  // read_until_space
303 
305 
316  template <
317  typename WrappedRange,
318  typename OutputIterator,
319  typename Predicate,
321  error read_until_space(WrappedRange& r,
322  OutputIterator& out,
323  Predicate is_space,
324  bool keep_final_space)
325  {
326  auto s = read_until_space_zero_copy(r, is_space, keep_final_space);
327  if (!s) {
328  return s.error();
329  }
330  out = std::copy(s.value().begin(), s.value().end(), out);
331  return {};
332  }
333  template <typename WrappedRange,
334  typename OutputIterator,
335  typename Predicate,
336  typename std::enable_if<!WrappedRange::is_contiguous &&
337  WrappedRange::is_direct>::type* = nullptr>
338  error read_until_space(WrappedRange& r,
339  OutputIterator& out,
340  Predicate is_space,
341  bool keep_final_space)
342  {
343  if (r.begin() == r.end()) {
344  return error(error::end_of_range, "EOF");
345  }
346  for (auto it = r.begin(); it != r.end(); ++it, (void)r.advance()) {
347  const auto ch = *it;
348  if (is_space(ch)) {
349  if (keep_final_space) {
350  *out = ch;
351  ++out;
352  }
353  return {};
354  }
355  *out = ch;
356  ++out;
357  }
358  return {};
359  }
360  template <
361  typename WrappedRange,
362  typename OutputIterator,
363  typename Predicate,
364  typename std::enable_if<!WrappedRange::is_contiguous &&
365  !WrappedRange::is_direct>::type* = nullptr>
366  error read_until_space(WrappedRange& r,
367  OutputIterator& out,
368  Predicate is_space,
369  bool keep_final_space)
370  {
371  if (r.begin() == r.end()) {
372  return error(error::end_of_range, "EOF");
373  }
374  for (auto it = r.begin(); it != r.end(); ++it, (void)r.advance()) {
375  auto tmp = *it;
376  if (!tmp) {
377  return tmp.error();
378  }
379  auto ch = tmp.value();
380  if (is_space(ch)) {
381  if (keep_final_space) {
382  *out = ch;
383  ++out;
384  }
385  return {};
386  }
387  *out = ch;
388  ++out;
389  }
390  return {};
391  }
392 
394 
395  // read_until_space_ranged
396 
398 
409  template <typename WrappedRange,
410  typename OutputIterator,
411  typename Sentinel,
412  typename Predicate,
415  OutputIterator& out,
416  Sentinel end,
417  Predicate is_space,
418  bool keep_final_space)
419  {
420  if (r.begin() == r.end()) {
421  return error(error::end_of_range, "EOF");
422  }
423  for (auto it = r.begin(); it != r.end() && out != end;
424  ++it, (void)r.advance()) {
425  const auto ch = *it;
426  if (is_space(ch)) {
427  if (keep_final_space) {
428  *out = ch;
429  ++out;
430  }
431  return {};
432  }
433  *out = ch;
434  ++out;
435  }
436  return {};
437  }
438  template <
439  typename WrappedRange,
440  typename OutputIterator,
441  typename Sentinel,
442  typename Predicate,
444  error read_until_space_ranged(WrappedRange& r,
445  OutputIterator& out,
446  Sentinel end,
447  Predicate is_space,
448  bool keep_final_space)
449  {
450  if (r.begin() == r.end()) {
451  return error(error::end_of_range, "EOF");
452  }
453  for (auto it = r.begin(); it != r.end() && out != end;
454  ++it, (void)r.advance()) {
455  auto tmp = *it;
456  if (!tmp) {
457  return tmp.error();
458  }
459  auto ch = tmp.value();
460  if (is_space(ch)) {
461  if (keep_final_space) {
462  *out = ch;
463  ++out;
464  }
465  return {};
466  }
467  *out = ch;
468  ++out;
469  }
470  return {};
471  }
472 
474 
475  // putback_n
476 
478 
483  template <
484  typename WrappedRange,
485  typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
486  error putback_n(WrappedRange& r,
488  {
489  SCN_EXPECT(n <=
490  detail::ranges::distance(r.begin_underlying(), r.begin()));
491  r.advance(-n);
492  return {};
493  }
494  template <
495  typename WrappedRange,
496  typename std::enable_if<!WrappedRange::is_contiguous>::type* = nullptr>
497  error putback_n(WrappedRange& r,
498  detail::ranges::range_difference_t<WrappedRange> n)
499  {
500  for (detail::ranges::range_difference_t<WrappedRange> i = 0; i < n;
501  ++i) {
502  r.advance(-1);
503  if (r.begin() == r.end()) {
505  "Putback failed");
506  }
507  }
508  return {};
509  }
510 
512 
513  // scan_low
515 
516  struct empty_parser {
517  template <typename ParseCtx>
518  error parse(ParseCtx& pctx)
519  {
520  pctx.arg_begin();
521  if (SCN_UNLIKELY(!pctx)) {
523  "Unexpected format string end");
524  }
525  if (!pctx.check_arg_end()) {
527  "Expected argument end");
528  }
529  pctx.arg_end();
530  return {};
531  }
532  };
533 
534  namespace detail {
535  struct char_scanner {
536  template <typename ParseCtx>
537  error parse(ParseCtx& pctx)
538  {
539  using char_type = typename ParseCtx::char_type;
540 
541  pctx.arg_begin();
542  if (SCN_UNLIKELY(!pctx)) {
544  "Unexpected format string end");
545  }
546  if (pctx.next() == detail::ascii_widen<char_type>('c')) {
547  pctx.advance();
548  }
549  if (!pctx.check_arg_end()) {
551  "Expected argument end");
552  }
553  pctx.arg_end();
554  return {};
555  }
556 
557  template <typename Context>
558  error scan(typename Context::char_type& val, Context& ctx)
559  {
560  auto ch = read_char(ctx.range());
561  if (!ch) {
562  return ch.error();
563  }
564  val = ch.value();
565  return {};
566  }
567  };
568 
569  struct buffer_scanner : public empty_parser {
570  template <typename Context>
572  {
573  using char_type = typename Context::char_type;
574 
575  if (val.size() == 0) {
576  return {};
577  }
578 
579  auto s = read_zero_copy(ctx.range(), val.ssize());
580  if (!s) {
581  return s.error();
582  }
583  if (s.value().size() != 0) {
584  if (s.value().size() != val.size()) {
585  return error(error::end_of_range, "EOF");
586  }
587  std::memcpy(val.begin(), s.value().begin(),
588  s.value().size() * sizeof(char_type));
589  return {};
590  }
591 
593  auto it = buf.begin();
594  auto e = read_into(ctx.range(), it, val.ssize());
595  if (!e) {
596  return e;
597  }
598  buf.erase(it);
599  std::memcpy(val.begin(), buf.begin(),
600  buf.size() * sizeof(char_type));
601  return {};
602  }
603  };
604 
605  struct bool_scanner {
606  template <typename ParseCtx>
607  error parse(ParseCtx& pctx)
608  {
609  // {}: no boolalpha, not localized
610  // l: localized
611  // a: boolalpha
612  using char_type = typename ParseCtx::char_type;
613 
614  pctx.arg_begin();
615  if (SCN_UNLIKELY(!pctx)) {
617  "Unexpected format string end");
618  }
619 
620  bool a = false, n = false;
621  for (auto ch = pctx.next(); pctx && !pctx.check_arg_end();
622  pctx.advance(), ch = pctx.next()) {
623  if (ch == detail::ascii_widen<char_type>('l')) {
624  localized = true;
625  }
626  else if (ch == detail::ascii_widen<char_type>('a')) {
627  a = true;
628  }
629  else if (ch == detail::ascii_widen<char_type>('n')) {
630  n = true;
631  }
632  else {
633  break;
634  }
635  }
636  if (a || n) {
637  allow_alpha = a;
638  allow_num = n;
639  }
640 
643  "boolalpha-mode cannot be disabled with 'l' "
644  "(localized) specifier with bool");
645  }
646 
647  if (pctx.next() == detail::ascii_widen<char_type>('b')) {
648  pctx.advance();
649  }
650  if (!pctx.check_arg_end()) {
652  "Expected argument end");
653  }
654  pctx.arg_end();
655  return {};
656  }
657 
658  template <typename Context>
659  error scan(bool& val, Context& ctx)
660  {
661  using char_type = typename Context::char_type;
662 
663  if (allow_alpha) {
664  auto truename = locale_defaults<char_type>::truename();
665  auto falsename = locale_defaults<char_type>::falsename();
666  if (localized) {
667  truename = ctx.locale().truename();
668  falsename = ctx.locale().falsename();
669  }
670  const auto max_len =
671  detail::max(truename.size(), falsename.size());
672  std::basic_string<char_type> buf;
673  buf.reserve(max_len);
674 
675  auto tmp_it = std::back_inserter(buf);
676  auto e = read_until_space(
677  ctx.range(), tmp_it,
678  [&ctx](char_type ch) {
679  return ctx.locale().is_space(ch);
680  },
681  false);
682  if (!e) {
683  return e;
684  }
685 
686  bool found = false;
687  if (buf.size() >= falsename.size()) {
688  if (std::equal(falsename.begin(), falsename.end(),
689  buf.begin())) {
690  val = false;
691  found = true;
692  }
693  }
694  if (!found && buf.size() >= truename.size()) {
695  if (std::equal(truename.begin(), truename.end(),
696  buf.begin())) {
697  val = true;
698  found = true;
699  }
700  }
701  if (found) {
702  return {};
703  }
704  else {
705  auto pb =
706  putback_n(ctx.range(),
707  static_cast<std::ptrdiff_t>(buf.size()));
708  if (!pb) {
709  return pb;
710  }
711  }
712  }
713 
714  if (allow_num) {
715  auto tmp = read_char(ctx.range());
716  if (!tmp) {
717  return tmp.error();
718  }
719  if (tmp.value() == detail::ascii_widen<char_type>('0')) {
720  val = false;
721  return {};
722  }
723  if (tmp.value() == detail::ascii_widen<char_type>('1')) {
724  val = true;
725  return {};
726  }
727  auto pb = putback_n(ctx.range(), 1);
728  if (!pb) {
729  return pb;
730  }
731  }
732 
734  "Couldn't scan bool");
735  }
736 
737  bool localized{false};
738  bool allow_alpha{true};
739  bool allow_num{true};
740  };
741 
742  template <typename T>
744  static_assert(std::is_integral<T>::value, "");
745 
746  template <typename ParseCtx>
747  error parse(ParseCtx& pctx)
748  {
749  // {}: base detect, not localized
750  // n: localized decimal/thousand separator
751  // l: n + localized digits
752  // d: decimal, o: octal, x: hex, b[1-36]: base
753  using char_type = typename ParseCtx::char_type;
754  pctx.arg_begin();
755  if (SCN_UNLIKELY(!pctx)) {
757  "Unexpected format string end");
758  }
759 
760  bool base_set = false;
761  bool loc_set = false;
762  for (auto ch = pctx.next(); pctx && !pctx.check_arg_end();
763  pctx.advance(), ch = pctx.next()) {
764  if (!base_set) {
765  if (ch == detail::ascii_widen<char_type>('d')) {
766  base = 10;
767  base_set = true;
768  continue;
769  }
770  else if (ch == detail::ascii_widen<char_type>('x')) {
771  base = 16;
772  base_set = true;
773  continue;
774  }
775  else if (ch == detail::ascii_widen<char_type>('o')) {
776  base = 8;
777  base_set = true;
778  continue;
779  }
780  else if (ch == detail::ascii_widen<char_type>('i')) {
781  if (std::is_unsigned<T>::value) {
782  return error(
784  "'i' format specifier expects signed "
785  "integer argument");
786  }
787  base = 0;
788  base_set = true;
789  continue;
790  }
791  else if (ch == detail::ascii_widen<char_type>('u')) {
792  if (std::is_signed<T>::value) {
793  return error(
795  "'u' format specifier expects unsigned "
796  "integer argument");
797  }
798  base = 0;
799  base_set = true;
800  continue;
801  }
802  else if (ch == detail::ascii_widen<char_type>('b')) {
803  pctx.advance();
804  if (SCN_UNLIKELY(!pctx)) {
806  "Unexpected format string end");
807  }
808  if (SCN_UNLIKELY(pctx.check_arg_end())) {
810  "Unexpected argument end");
811  }
812  ch = pctx.next();
813 
814  const auto zero =
815  detail::ascii_widen<char_type>('0'),
816  nine =
817  detail::ascii_widen<char_type>('9');
818  int tmp = 0;
819  if (ch < zero || ch > nine) {
821  "Invalid character after 'b', "
822  "expected digit");
823  }
824  tmp = pctx.next() - zero;
825  if (tmp < 1) {
826  return error(
828  "Invalid base, must be between 1 and 36");
829  }
830 
831  pctx.advance();
832  ch = pctx.next();
833 
834  if (pctx.check_arg_end()) {
835  base = tmp;
836  break;
837  }
838  if (ch < zero || ch > nine) {
840  "Invalid character after 'b', "
841  "expected digit");
842  }
843  tmp *= 10;
844  tmp += ch - zero;
845  if (tmp < 1 || tmp > 36) {
846  return error(
848  "Invalid base, must be between 1 and 36");
849  }
850  base = tmp;
851  base_set = true;
852  continue;
853  }
854  }
855 
856  if (!loc_set) {
857  if (ch == detail::ascii_widen<char_type>('l')) {
859  loc_set = true;
860  continue;
861  }
862  else if (ch == detail::ascii_widen<char_type>('n')) {
864  loc_set = true;
865  continue;
866  }
867  }
868 
869  if (!have_thsep) {
870  if (ch == detail::ascii_widen<char_type>('\'')) {
871  have_thsep = true;
872  continue;
873  }
874  }
875 
877  "Unexpected character in format string");
878  }
879 
880  if (localized && (base != 0 && base != 10)) {
881  return error(
883  "Localized integers can only be scanned in base 10");
884  }
885  if (!pctx.check_arg_end()) {
887  "Expected argument end");
888  }
889  pctx.arg_end();
890  return {};
891  }
892 
893  template <typename Context>
894  error scan(T& val, Context& ctx)
895  {
896  using char_type = typename Context::char_type;
897  auto do_parse_int = [&](span<const char_type> s) -> error {
898  T tmp = 0;
900  if (SCN_UNLIKELY((localized & digits) != 0)) {
902  std::basic_string<char_type> str(s.data(), s.size());
903  ret = ctx.locale().read_num(tmp, str);
905  }
906  else {
907  ret = _parse_int(tmp, s,
908  ctx.locale().thousands_separator());
909  }
910 
911  if (!ret) {
912  return ret.error();
913  }
914  if (ret.value() != s.ssize()) {
915  auto pb =
916  putback_n(ctx.range(), s.ssize() - ret.value());
917  if (!pb) {
918  return pb;
919  }
920  }
921  val = tmp;
922  return {};
923  };
924 
925  auto is_space_pred = [&ctx](char_type ch) {
926  return ctx.locale().is_space(ch);
927  };
928 
929  if (Context::range_type::is_contiguous) {
930  auto s = read_all_zero_copy(ctx.range());
931  if (!s) {
932  return s.error();
933  }
934  return do_parse_int(s.value());
935  }
936 
938  auto outputit = std::back_inserter(buf);
939  auto e = read_until_space(ctx.range(), outputit, is_space_pred,
940  false);
941  if (!e && buf.empty()) {
942  return e;
943  }
944 
945  return do_parse_int(make_span(buf).as_const());
946  }
947 
948  enum localized_type : uint8_t {
950  digits = 2
951  };
952 
953  int base{0};
954  uint8_t localized{0};
955  bool have_thsep{false};
956 
957  template <typename CharT>
960  CharT thsep)
961  {
963  SCN_MSVC_IGNORE(4244)
964  SCN_MSVC_IGNORE(4127) // conditional expression is constant
965 
966  if (std::is_unsigned<T>::value) {
967  if (s[0] == detail::ascii_widen<CharT>('-')) {
969  "Unexpected sign '-' when scanning an "
970  "unsigned integer");
971  }
972  }
973 
975 
976  T tmp = 0;
977  bool minus_sign = false;
978  auto it = s.begin();
979 
980  if (s[0] == ascii_widen<CharT>('-') ||
981  s[0] == ascii_widen<CharT>('+')) {
983  SCN_GCC_IGNORE("-Wsign-conversion")
984  minus_sign = s[0] == ascii_widen<CharT>('-');
985  ++it;
987  }
988  if (SCN_UNLIKELY(it == s.end())) {
990  "Expected number after sign");
991  }
992 
993  if (*it == ascii_widen<CharT>('0')) {
994  ++it;
995  if (it == s.end()) {
996  val = 0;
997  return ranges::distance(s.begin(), it);
998  }
999  if (*it == ascii_widen<CharT>('x') ||
1000  *it == ascii_widen<CharT>('X')) {
1001  if (SCN_UNLIKELY(base != 0 && base != 16)) {
1002  val = 0;
1003  return ranges::distance(s.begin(), it);
1004  }
1005  ++it;
1006  if (SCN_UNLIKELY(it == s.end())) {
1007  --it;
1008  val = 0;
1009  return ranges::distance(s.begin(), it);
1010  }
1011  if (base == 0) {
1012  base = 16;
1013  }
1014  }
1015  else if (base == 0) {
1016  base = 8;
1017  }
1018  }
1019  if (base == 0) {
1020  base = 10;
1021  }
1022 
1023  SCN_GCC_PUSH
1024  SCN_GCC_IGNORE("-Wconversion")
1025  SCN_GCC_IGNORE("-Wsign-conversion")
1026  SCN_GCC_IGNORE("-Wsign-compare")
1027 
1029  SCN_CLANG_IGNORE("-Wconversion")
1030  SCN_CLANG_IGNORE("-Wsign-conversion")
1031  SCN_CLANG_IGNORE("-Wsign-compare")
1032 
1033  SCN_ASSUME(base > 0);
1034 
1035  auto r = _read_int(tmp, minus_sign,
1036  make_span(it, s.end()).as_const(), thsep);
1037  if (!r) {
1038  return r.error();
1039  }
1040  it = r.value();
1041  if (s.begin() == it) {
1043  "custom::read_int");
1044  }
1045  val = tmp;
1046  return ranges::distance(s.begin(), it);
1047 
1049  SCN_GCC_POP
1050  }
1051 
1052  template <typename CharT>
1054  T& val,
1055  bool minus_sign,
1056  span<const CharT> buf,
1057  CharT thsep) const
1058  {
1059  SCN_GCC_PUSH
1060  SCN_GCC_IGNORE("-Wconversion")
1061  SCN_GCC_IGNORE("-Wsign-conversion")
1062  SCN_GCC_IGNORE("-Wsign-compare")
1063 
1065  SCN_CLANG_IGNORE("-Wconversion")
1066  SCN_CLANG_IGNORE("-Wsign-conversion")
1067  SCN_CLANG_IGNORE("-Wsign-compare")
1068 
1070  SCN_MSVC_IGNORE(4018) // > signed/unsigned mismatch
1071  SCN_MSVC_IGNORE(4389) // == signed/unsigned mismatch
1072  SCN_MSVC_IGNORE(4244) // lossy conversion
1073 
1074  using utype = typename std::make_unsigned<T>::type;
1075 
1076  const auto ubase = static_cast<utype>(base);
1077  SCN_ASSUME(ubase > 0);
1078 
1079  constexpr auto uint_max = static_cast<utype>(-1);
1080  constexpr auto int_max = static_cast<utype>(uint_max >> 1);
1081  constexpr auto abs_int_min = static_cast<utype>(int_max + 1);
1082 
1083  const auto cut = div(
1084  [&]() -> utype {
1085  if (std::is_signed<T>::value) {
1086  if (minus_sign) {
1087  return abs_int_min;
1088  }
1089  return int_max;
1090  }
1091  return uint_max;
1092  }(),
1093  ubase);
1094  const auto cutoff = cut.first;
1095  const auto cutlim = cut.second;
1096 
1097  auto it = buf.begin();
1098  const auto end = buf.end();
1099  if (SCN_UNLIKELY(have_thsep)) {
1100  for (; it != end; ++it) {
1101  if (*it == thsep) {
1102  continue;
1103  }
1104 
1105  const auto digit = _char_to_int(*it);
1106  if (digit >= ubase) {
1107  break;
1108  }
1109  if (SCN_UNLIKELY(val > cutoff ||
1110  (val == cutoff && digit > cutlim))) {
1111  if (!minus_sign) {
1113  "Out of range: integer overflow");
1114  }
1116  "Out of range: integer underflow");
1117  }
1118  val = val * ubase + digit;
1119  }
1120  }
1121  else {
1122  for (; it != end; ++it) {
1123  const auto digit = _char_to_int(*it);
1124  if (digit >= ubase) {
1125  break;
1126  }
1127  if (SCN_UNLIKELY(val > cutoff ||
1128  (val == cutoff && digit > cutlim))) {
1129  if (!minus_sign) {
1131  "Out of range: integer overflow");
1132  }
1134  "Out of range: integer underflow");
1135  }
1136  val = val * ubase + digit;
1137  }
1138  }
1139  val = val * (minus_sign ? -1 : 1);
1140  return it;
1141 
1142  SCN_MSVC_POP
1144  SCN_GCC_POP
1145  }
1146 
1147  unsigned char _char_to_int(char ch) const
1148  {
1149  static constexpr unsigned char digits_arr[] = {
1150  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1151  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1152  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1153  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1154  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
1155  255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
1156  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
1157  29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
1158  255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
1159  21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
1160  33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1161  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1162  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1163  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1164  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1165  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1166  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1167  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1168  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1169  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1170  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1171  255, 255, 255, 255};
1172  return digits_arr[static_cast<unsigned char>(ch)];
1173  }
1174  unsigned char _char_to_int(wchar_t ch) const
1175  {
1176  SCN_GCC_PUSH
1177  SCN_GCC_IGNORE("-Wconversion")
1178  if (ch >= std::numeric_limits<char>::min() &&
1180  return _char_to_int(static_cast<char>(ch));
1181  }
1182  return 255;
1183  SCN_GCC_POP
1184  }
1185  };
1186 
1187  template <typename T>
1188  struct float_scanner {
1189  static_assert(std::is_floating_point<T>::value, "");
1190 
1191  template <typename ParseCtx>
1192  error parse(ParseCtx& pctx)
1193  {
1194  // {}: not localized
1195  // l: localized
1196  using char_type = typename ParseCtx::char_type;
1197  pctx.arg_begin();
1198  if (SCN_UNLIKELY(!pctx)) {
1200  "Unexpected format string end");
1201  }
1202 
1203  if (pctx.check_arg_end()) {
1204  pctx.arg_end();
1205  return {};
1206  }
1207 
1208  if (pctx.next() == detail::ascii_widen<char_type>('l')) {
1209  localized = true;
1210  pctx.advance();
1211  }
1212 
1213  if (pctx.check_arg_end()) {
1214  pctx.arg_end();
1215  return {};
1216  }
1217 
1218  if (pctx.next() == detail::ascii_widen<char_type>('a')) {
1219  pctx.advance();
1220  }
1221  else if (pctx.next() == detail::ascii_widen<char_type>('A')) {
1222  pctx.advance();
1223  }
1224  else if (pctx.next() == detail::ascii_widen<char_type>('e')) {
1225  pctx.advance();
1226  }
1227  else if (pctx.next() == detail::ascii_widen<char_type>('E')) {
1228  pctx.advance();
1229  }
1230  else if (pctx.next() == detail::ascii_widen<char_type>('f')) {
1231  pctx.advance();
1232  }
1233  else if (pctx.next() == detail::ascii_widen<char_type>('F')) {
1234  pctx.advance();
1235  }
1236  else if (pctx.next() == detail::ascii_widen<char_type>('g')) {
1237  pctx.advance();
1238  }
1239  else if (pctx.next() == detail::ascii_widen<char_type>('G')) {
1240  pctx.advance();
1241  }
1242 
1243  if (!pctx.check_arg_end()) {
1245  "Expected argument end");
1246  }
1247  pctx.arg_end();
1248  return {};
1249  }
1250 
1251  template <typename Context>
1252  error scan(T& val, Context& ctx)
1253  {
1254  using char_type = typename Context::char_type;
1255  auto do_parse_float = [&](span<const char_type> s) -> error {
1256  T tmp = 0;
1257  expected<std::ptrdiff_t> ret{0};
1258  if (SCN_UNLIKELY(localized)) {
1260  std::basic_string<char_type> str(s.data(), s.size());
1261  ret = ctx.locale().read_num(tmp, str);
1263  }
1264  else {
1265  ret = _read_float(tmp, s);
1266  }
1267 
1268  if (!ret) {
1269  return ret.error();
1270  }
1271  if (ret.value() != s.ssize()) {
1272  auto pb =
1273  putback_n(ctx.range(), s.ssize() - ret.value());
1274  if (!pb) {
1275  return pb;
1276  }
1277  }
1278  val = tmp;
1279  return {};
1280  };
1281 
1282  auto is_space_pred = [&ctx](char_type ch) {
1283  return ctx.locale().is_space(ch);
1284  };
1285 
1286  if (Context::range_type::is_contiguous) {
1287  auto s = read_until_space_zero_copy(ctx.range(),
1288  is_space_pred, false);
1289  if (!s) {
1290  return s.error();
1291  }
1292  return do_parse_float(s.value());
1293  }
1294 
1296  auto outputit = std::back_inserter(buf);
1297  auto e = read_until_space(ctx.range(), outputit, is_space_pred,
1298  false);
1299  if (!e && buf.empty()) {
1300  return e;
1301  }
1302 
1303  return do_parse_float(make_span(buf).as_const());
1304  }
1305 
1306  bool localized{false};
1307 
1308  template <typename CharT>
1310  {
1311  size_t chars{};
1312  std::basic_string<CharT> str(s.data(), s.size());
1314  auto ret = _read_float_impl(str.data(), chars);
1316  if (!ret) {
1317  return ret.error();
1318  }
1319  val = ret.value();
1320  return static_cast<std::ptrdiff_t>(chars);
1321  }
1322 
1323  template <typename CharT>
1324  expected<T> _read_float_impl(const CharT* str, size_t& chars);
1325  };
1326 
1328  template <typename ParseCtx>
1329  error parse(ParseCtx& pctx)
1330  {
1331  using char_type = typename ParseCtx::char_type;
1332  pctx.arg_begin();
1333  if (SCN_UNLIKELY(!pctx)) {
1335  "Unexpected format string end");
1336  }
1337  if (pctx.next() == detail::ascii_widen<char_type>('s')) {
1338  pctx.advance();
1339  }
1340  if (!pctx.check_arg_end()) {
1342  "Expected argument end");
1343  }
1344  pctx.arg_end();
1345  return {};
1346  }
1347 
1348  template <typename Context>
1349  error scan(std::basic_string<typename Context::char_type>& val,
1350  Context& ctx)
1351  {
1352  using char_type = typename Context::char_type;
1353 
1354  auto is_space_pred = [&ctx](char_type ch) {
1355  return ctx.locale().is_space(ch);
1356  };
1357 
1358  if (Context::range_type::is_contiguous) {
1359  auto s = read_until_space_zero_copy(ctx.range(),
1360  is_space_pred, false);
1361  if (!s) {
1362  return s.error();
1363  }
1364  val = std::basic_string<char_type>{s.value().data(),
1365  s.value().size()};
1366  return {};
1367  }
1368 
1369  std::basic_string<char_type> tmp;
1370  auto outputit = std::back_inserter(tmp);
1371  auto ret = read_until_space(ctx.range(), outputit,
1372  is_space_pred, false);
1373  if (SCN_UNLIKELY(!ret)) {
1374  return ret;
1375  }
1376  if (SCN_UNLIKELY(tmp.empty())) {
1378  "Empty string parsed");
1379  }
1380  val = std::move(tmp);
1381 
1382  return {};
1383  }
1384  };
1385 
1387  public:
1388  template <typename Context>
1390  Context& ctx)
1391  {
1392  using char_type = typename Context::char_type;
1393  auto is_space_pred = [&ctx](char_type ch) {
1394  return ctx.locale().is_space(ch);
1395  };
1396  if (!Context::range_type::is_contiguous) {
1398  "Cannot read a string_view from a "
1399  "non-contiguous_range");
1400  }
1401  auto s = read_until_space_zero_copy(ctx.range(), is_space_pred,
1402  false);
1403  if (!s) {
1404  return s.error();
1405  }
1406  val = basic_string_view<char_type>(s.value().data(),
1407  s.value().size());
1408  return {};
1409  }
1410  };
1411  } // namespace detail
1412 
1413  template <typename CharT>
1414  struct scanner<CharT, CharT> : public detail::char_scanner {
1415  };
1416  template <typename CharT>
1417  struct scanner<CharT, span<CharT>> : public detail::buffer_scanner {
1418  };
1419  template <typename CharT>
1420  struct scanner<CharT, bool> : public detail::bool_scanner {
1421  };
1422  template <typename CharT>
1423  struct scanner<CharT, short> : public detail::integer_scanner<short> {
1424  };
1425  template <typename CharT>
1426  struct scanner<CharT, int> : public detail::integer_scanner<int> {
1427  };
1428  template <typename CharT>
1429  struct scanner<CharT, long> : public detail::integer_scanner<long> {
1430  };
1431  template <typename CharT>
1432  struct scanner<CharT, long long>
1433  : public detail::integer_scanner<long long> {
1434  };
1435  template <typename CharT>
1436  struct scanner<CharT, unsigned short>
1437  : public detail::integer_scanner<unsigned short> {
1438  };
1439  template <typename CharT>
1440  struct scanner<CharT, unsigned int>
1441  : public detail::integer_scanner<unsigned int> {
1442  };
1443  template <typename CharT>
1444  struct scanner<CharT, unsigned long>
1445  : public detail::integer_scanner<unsigned long> {
1446  };
1447  template <typename CharT>
1448  struct scanner<CharT, unsigned long long>
1449  : public detail::integer_scanner<unsigned long long> {
1450  };
1451  template <typename CharT>
1452  struct scanner<CharT, float> : public detail::float_scanner<float> {
1453  };
1454  template <typename CharT>
1455  struct scanner<CharT, double> : public detail::float_scanner<double> {
1456  };
1457  template <typename CharT>
1458  struct scanner<CharT, long double>
1459  : public detail::float_scanner<long double> {
1460  };
1461  template <typename CharT>
1462  struct scanner<CharT, std::basic_string<CharT>>
1463  : public detail::string_scanner {
1464  };
1465  template <typename CharT>
1466  struct scanner<CharT, basic_string_view<CharT>>
1467  : public detail::string_view_scanner {
1468  };
1469  template <typename CharT>
1470  struct scanner<CharT, detail::monostate>;
1471 
1473 
1482  template <typename Context,
1483  typename std::enable_if<
1484  !Context::range_type::is_contiguous>::type* = nullptr>
1485  error skip_range_whitespace(Context& ctx) noexcept
1486  {
1487  while (true) {
1489 
1490  auto ch = read_char(ctx.range());
1491  if (SCN_UNLIKELY(!ch)) {
1492  return ch.error();
1493  }
1494  if (!ctx.locale().is_space(ch.value())) {
1495  auto pb = putback_n(ctx.range(), 1);
1496  if (SCN_UNLIKELY(!pb)) {
1497  return pb;
1498  }
1499  break;
1500  }
1501 
1503  }
1504  return {};
1505  }
1506  template <typename Context,
1507  typename std::enable_if<
1508  Context::range_type::is_contiguous>::type* = nullptr>
1509  error skip_range_whitespace(Context& ctx) noexcept
1510  {
1512 
1513  const auto end = ctx.range().end();
1514  for (auto it = ctx.range().begin(); it != end; ++it) {
1515  if (!ctx.locale().is_space(*it)) {
1516  ctx.range().advance_to(it);
1517  return {};
1518  }
1519  }
1520  ctx.range().advance_to(end);
1521  return {};
1522 
1524  }
1525 
1527 
1529 } // namespace scn
1530 
1531 #if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_READER_CPP)
1532 #include "reader.cpp"
1533 #endif
1534 
1535 #endif // SCN_DETAIL_READER_H
1536 
error read_until_space(WrappedRange &r, OutputIterator &out, Predicate is_space, bool keep_final_space)
Reads characters from r until a space is found (as determined by is_space) and writes them into out...
Definition: reader.h:321
unsigned char _char_to_int(char ch) const
Definition: reader.h:1147
error scan(T &val, Context &ctx)
Definition: reader.h:1252
Scanned value was invalid for given type.
Definition: result.h:44
SCN_CONSTEXPR14 iterator end() noexcept
Definition: span.h:258
#define SCN_END_NAMESPACE
Definition: config.h:401
expected< detail::ranges::range_value_t< WrappedRange > > read_char(WrappedRange &r)
Reads a single character from the range.
Definition: reader.h:53
#define SCN_UNLIKELY(x)
Definition: config.h:362
constexpr ssize_type ssize() const noexcept
Definition: span.h:319
error skip_range_whitespace(Context &ctx) noexcept
Reads from the range in ctx as if by repeatedly calling read_char(), until a non-space character is f...
Definition: reader.h:1485
unsigned char _char_to_int(wchar_t ch) const
Definition: reader.h:1174
expected< T > _read_float_impl(const CharT *str, size_t &chars)
expected< span< const typename detail::extract_char_type< typename WrappedRange::iterator >::type > > read_until_space_zero_copy(WrappedRange &r, Predicate is_space, bool keep_final_space)
Reads characters from r until a space is found (as determined by is_space), and returns a span into t...
Definition: reader.h:262
constexpr std::pair< T, T > div(T l, T r) noexcept
Definition: util.h:480
Scanned value was out of range for the desired type.
Definition: result.h:49
error scan(T &val, Context &ctx)
Definition: reader.h:894
#define SCN_MSVC_PUSH
Definition: config.h:155
#define SCN_CLANG_IGNORE(x)
Definition: config.h:144
expected< std::ptrdiff_t > _read_float(T &val, span< const CharT > s)
Definition: reader.h:1309
constexpr T max(T a, T b) noexcept
Definition: util.h:509
SCN_CONSTEXPR14 iterator begin() noexcept
Definition: small_vector.h:611
#define SCN_GCC_PUSH
Definition: config.h:119
Format string was invalid.
Definition: result.h:41
error parse(ParseCtx &pctx)
Definition: reader.h:607
expected-like type.
Definition: result.h:180
#define SCN_CLANG_POP
Definition: config.h:143
#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:146
constexpr T min(T a, T b) noexcept
Definition: util.h:532
error scan(bool &val, Context &ctx)
Definition: reader.h:659
error scan(typename Context::char_type &val, Context &ctx)
Definition: reader.h:558
error parse(ParseCtx &pctx)
Definition: reader.h:747
error scan(std::basic_string< typename Context::char_type > &val, Context &ctx)
Definition: reader.h:1349
Stream does not support the performed operation.
Definition: result.h:46
typename std::enable_if< range< R >::value, iter_difference_t< iterator_t< R >>>::type range_difference_t
Definition: ranges.h:500
bool empty() const noexcept
Definition: small_vector.h:562
#define SCN_MSVC_IGNORE(x)
Definition: config.h:157
Error class.
Definition: result.h:32
#define SCN_BEGIN_NAMESPACE
Definition: config.h:400
typename std::enable_if< range< R >::value, iter_value_t< iterator_t< R >>>::type range_value_t
Definition: ranges.h:504
#define SCN_GCC_POP
Definition: config.h:120
constexpr pointer data() const noexcept
Definition: span.h:311
error parse(ParseCtx &pctx)
Definition: reader.h:1192
The source range emitted an error that cannot be recovered from.
Definition: result.h:58
error read_into(WrappedRange &r, OutputIterator &it, detail::ranges::range_difference_t< WrappedRange > n)
Reads n characters from r into it.
Definition: reader.h:176
expected< span< const typename detail::extract_char_type< typename WrappedRange::iterator >::type > > read_zero_copy(WrappedRange &r, detail::ranges::range_difference_t< WrappedRange > n)
Reads up to n characters from r, and returns a span into the range.
Definition: reader.h:97
#define SCN_ASSUME(x)
Definition: config.h:341
#define SCN_MSVC_POP
Definition: config.h:156
error parse(ParseCtx &pctx)
Definition: reader.h:518
bool is_space(char ch) noexcept
Definition: locale.h:70
error parse(ParseCtx &pctx)
Definition: reader.h:1329
error scan(span< typename Context::char_type > &val, Context &ctx)
Definition: reader.h:571
#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:145
void end(T &&)=delete
error parse(ParseCtx &pctx)
Definition: reader.h:537
error read_until_space_ranged(WrappedRange &r, OutputIterator &out, Sentinel end, Predicate is_space, bool keep_final_space)
Reads characters from r until a space is found (as determined by is_space), or out reaches end...
Definition: reader.h:414
SCN_CONSTEXPR14 iterator begin() noexcept
Definition: span.h:254
constexpr span< T > make_span(T *ptr, std::size_t count) noexcept
Definition: span.h:383
constexpr index_type size() const noexcept
Definition: span.h:315
expected< typename span< const CharT >::iterator > _read_int(T &val, bool minus_sign, span< const CharT > buf, CharT thsep) const
Definition: reader.h:1053
A view over a (sub)string.
Definition: string_view.h:60
expected< span< const typename detail::extract_char_type< typename WrappedRange::iterator >::type > > read_all_zero_copy(WrappedRange &r)
Reads every character from r, and returns a span into the range.
Definition: reader.h:138
error scan(basic_string_view< typename Context::char_type > &val, Context &ctx)
Definition: reader.h:1389
#define SCN_GCC_IGNORE(x)
Definition: config.h:121
#define SCN_EXPECT(cond)
Definition: config.h:396
#define SCN_CLANG_PUSH
Definition: config.h:142
A view over a contiguous range.
Definition: span.h:29
error putback_n(WrappedRange &r, detail::ranges::range_difference_t< WrappedRange > n)
Puts back n characters into r as if by repeatedly calling r.advance(-1) .
Definition: reader.h:486
expected< std::ptrdiff_t > _parse_int(T &val, span< const CharT > s, CharT thsep)
Definition: reader.h:958