millijson
Lightweight JSON parser for C++
Loading...
Searching...
No Matches
millijson.hpp
Go to the documentation of this file.
1#ifndef MILLIJSON_MILLIJSON_HPP
2#define MILLIJSON_MILLIJSON_HPP
3
4#include <memory>
5#include <vector>
6#include <cstddef>
7#include <cstdlib>
8#include <string>
9#include <stdexcept>
10#include <cmath>
11#include <unordered_map>
12#include <unordered_set>
13#include <cstdio>
14
15#include "byteme/byteme.hpp"
17
27namespace millijson {
28
33enum Type {
34 NUMBER,
35 NUMBER_AS_STRING,
36 STRING,
37 BOOLEAN,
38 NOTHING,
39 ARRAY,
40 OBJECT
41};
42
46class Base {
47public:
51 virtual Type type() const = 0;
52
56 Base() = default;
57 Base(Base&&) = default;
58 Base(const Base&) = default;
59 Base& operator=(Base&&) = default;
60 Base& operator=(const Base&) = default;
61 virtual ~Base() {}
65};
66
70class Number final : public Base {
71public:
75 Number(double x) : my_value(x) {}
76
77 Type type() const { return NUMBER; }
78
79public:
83 const double& value() const { return my_value; }
84
88 double& value() { return my_value; }
89
90private:
91 double my_value;
92};
93
97class NumberAsString final : public Base {
98public:
102 NumberAsString(std::string x) : my_value(x) {}
103
104 Type type() const { return NUMBER_AS_STRING; }
105
106public:
110 const std::string& value() const { return my_value; }
111
115 std::string& value() { return my_value; }
116
117private:
118 std::string my_value;
119};
120
124class String final : public Base {
125public:
129 String(std::string x) : my_value(std::move(x)) {}
130
131 Type type() const { return STRING; }
132
133public:
137 const std::string& value() const { return my_value; }
138
142 std::string& value() { return my_value; }
143
144private:
145 std::string my_value;
146};
147
151class Boolean final : public Base {
152public:
156 Boolean(bool x) : my_value(x) {}
157
158 Type type() const { return BOOLEAN; }
159
160public:
164 const bool& value() const { return my_value; }
165
169 bool& value() { return my_value; }
170
171private:
172 bool my_value;
173};
174
178class Nothing final : public Base {
179public:
180 Type type() const { return NOTHING; }
181};
182
186class Array final : public Base {
187public:
191 Array(std::vector<std::shared_ptr<Base> > x) : my_value(std::move(x)) {}
192
193 Type type() const { return ARRAY; }
194
195public:
199 const std::vector<std::shared_ptr<Base> >& value() const {
200 return my_value;
201 }
202
206 std::vector<std::shared_ptr<Base> >& value() {
207 return my_value;
208 }
209
210private:
211 std::vector<std::shared_ptr<Base> > my_value;
212};
213
217class Object final : public Base {
218public:
222 Object(std::unordered_map<std::string, std::shared_ptr<Base> > x) : my_value(std::move(x)) {}
223
224 Type type() const { return OBJECT; }
225
226public:
230 const std::unordered_map<std::string, std::shared_ptr<Base> >& value() const {
231 return my_value;
232 }
233
237 std::unordered_map<std::string, std::shared_ptr<Base> >& value() {
238 return my_value;
239 }
240
241private:
242 std::unordered_map<std::string, std::shared_ptr<Base> > my_value;
243};
244
254 bool number_as_string = false;
255
261
266 bool parallel = false;
267};
268
272// Return value of the various chomp functions indicates whether there are any
273// characters left in 'input', allowing us to avoid an extra call to valid().
274template<class Input_>
275bool raw_chomp(Input_& input, bool ok) {
276 while (ok) {
277 switch(input.get()) {
278 // Allowable whitespaces as of https://www.rfc-editor.org/rfc/rfc7159#section-2.
279 case ' ': case '\n': case '\r': case '\t':
280 break;
281 default:
282 return true;
283 }
284 ok = input.advance();
285 }
286 return false;
287}
288
289template<class Input_>
290bool check_and_chomp(Input_& input) {
291 bool ok = input.valid();
292 return raw_chomp(input, ok);
293}
294
295template<class Input_>
296bool advance_and_chomp(Input_& input) {
297 bool ok = input.advance();
298 return raw_chomp(input, ok);
299}
300
301inline bool is_digit(char val) {
302 return val >= '0' && val <= '9';
303}
304
305template<class Input_>
306bool is_expected_string(Input_& input, const char* ptr, std::size_t len) {
307 // We use a hard-coded 'len' instead of scanning for '\0' to enable loop unrolling.
308 for (std::size_t i = 1; i < len; ++i) {
309 // The current character was already used to determine what string to
310 // expect, so we can skip past it in order to match the rest of the
311 // string. This is also why we start from i = 1 instead of i = 0.
312 if (!input.advance()) {
313 return false;
314 }
315 if (input.get() != ptr[i]) {
316 return false;
317 }
318 }
319 input.advance(); // move off the last character.
320 return true;
321}
322
323template<class Input_>
324std::string extract_string(Input_& input) {
325 unsigned long long start = input.position() + 1;
326 input.advance(); // get past the opening quote.
327 std::string output;
328
329 while (1) {
330 char next = input.get();
331 switch (next) {
332 case '"':
333 input.advance(); // get past the closing quote.
334 return output;
335
336 case '\\':
337 if (!input.advance()) {
338 throw std::runtime_error("unterminated string at position " + std::to_string(start));
339 } else {
340 char next2 = input.get();
341 switch (next2) {
342 case '"':
343 output += '"';
344 break;
345 case 'n':
346 output += '\n';
347 break;
348 case 'r':
349 output += '\r';
350 break;
351 case '\\':
352 output += '\\';
353 break;
354 case '/':
355 output += '/';
356 break;
357 case 'b':
358 output += '\b';
359 break;
360 case 'f':
361 output += '\f';
362 break;
363 case 't':
364 output += '\t';
365 break;
366 case 'u':
367 {
368 unsigned short mb = 0;
369 for (int i = 0; i < 4; ++i) {
370 if (!input.advance()){
371 throw std::runtime_error("unterminated string at position " + std::to_string(start));
372 }
373 mb *= 16;
374 char val = input.get();
375 switch (val) {
376 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
377 mb += val - '0';
378 break;
379 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
380 mb += (val - 'a') + 10;
381 break;
382 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
383 mb += (val - 'A') + 10;
384 break;
385 default:
386 throw std::runtime_error("invalid unicode escape detected at position " + std::to_string(input.position() + 1));
387 }
388 }
389
390 // Manually convert Unicode code points to UTF-8. We only allow
391 // 3 bytes at most because there's only 4 hex digits in JSON.
392 if (mb <= 127) {
393 output += static_cast<char>(mb);
394 } else if (mb <= 2047) {
395 unsigned char left = (mb >> 6) | 0b11000000;
396 output += *(reinterpret_cast<char*>(&left));
397 unsigned char right = (mb & 0b00111111) | 0b10000000;
398 output += *(reinterpret_cast<char*>(&right));
399 } else {
400 unsigned char left = (mb >> 12) | 0b11100000;
401 output += *(reinterpret_cast<char*>(&left));
402 unsigned char middle = ((mb >> 6) & 0b00111111) | 0b10000000;
403 output += *(reinterpret_cast<char*>(&middle));
404 unsigned char right = (mb & 0b00111111) | 0b10000000;
405 output += *(reinterpret_cast<char*>(&right));
406 }
407 }
408 break;
409 default:
410 throw std::runtime_error("unrecognized escape '\\" + std::string(1, next2) + "'");
411 }
412 }
413 break;
414
415 case (char) 0: case (char) 1: case (char) 2: case (char) 3: case (char) 4: case (char) 5: case (char) 6: case (char) 7: case (char) 8: case (char) 9:
416 case (char)10: case (char)11: case (char)12: case (char)13: case (char)14: case (char)15: case (char)16: case (char)17: case (char)18: case (char)19:
417 case (char)20: case (char)21: case (char)22: case (char)23: case (char)24: case (char)25: case (char)26: case (char)27: case (char)28: case (char)29:
418 case (char)30: case (char)31:
419 case (char)127:
420 throw std::runtime_error("string contains ASCII control character at position " + std::to_string(input.position() + 1));
421
422 default:
423 output += next;
424 break;
425 }
426
427 if (!input.advance()) {
428 throw std::runtime_error("unterminated string at position " + std::to_string(start));
429 }
430 }
431
432 return output; // Technically unreachable, but whatever.
433}
434
435template<bool as_string_, class Input_>
436typename std::conditional<as_string_, std::string, double>::type extract_number(Input_& input) {
437 unsigned long long start = input.position() + 1;
438 auto value = []{
439 if constexpr(as_string_) {
440 return std::string("");
441 } else {
442 return static_cast<double>(0);
443 }
444 }();
445 bool in_fraction = false;
446 bool in_exponent = false;
447
448 auto add_string_value = [&](char x) -> void {
449 if constexpr(as_string_) {
450 value += x;
451 }
452 };
453
454 // We assume we're starting from the absolute value, after removing any preceding negative sign.
455 char lead = input.get();
456 add_string_value(lead);
457 if (lead == '0') {
458 if (!input.advance()) {
459 return value;
460 }
461
462 auto after_zero = input.get();
463 switch (after_zero) {
464 case '.':
465 add_string_value(after_zero);
466 in_fraction = true;
467 break;
468 case 'e': case 'E':
469 add_string_value(after_zero);
470 in_exponent = true;
471 break;
472 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
473 return value;
474 default:
475 throw std::runtime_error("invalid number starting with 0 at position " + std::to_string(start));
476 }
477
478 } else { // 'lead' must be a digit, as extract_number is only called when the current character is a digit.
479 if constexpr(!as_string_) {
480 value += lead - '0';
481 }
482
483 while (input.advance()) {
484 char val = input.get();
485 switch (val) {
486 case '.':
487 add_string_value(val);
488 in_fraction = true;
489 goto integral_end;
490 case 'e': case 'E':
491 add_string_value(val);
492 in_exponent = true;
493 goto integral_end;
494 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
495 goto total_end;
496 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
497 if constexpr(as_string_) {
498 value += val;
499 } else {
500 value *= 10;
501 value += val - '0';
502 }
503 break;
504 default:
505 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(start));
506 }
507 }
508
509integral_end:;
510 }
511
512 if (in_fraction) {
513 if (!input.advance()) {
514 throw std::runtime_error("invalid number with trailing '.' at position " + std::to_string(start));
515 }
516
517 char val = input.get();
518 if (!is_digit(val)) {
519 throw std::runtime_error("'.' must be followed by at least one digit at position " + std::to_string(start));
520 }
521
522 double fractional = 10;
523 if constexpr(as_string_) {
524 value += val;
525 } else {
526 value += (val - '0') / fractional;
527 }
528
529 while (input.advance()) {
530 char val = input.get();
531 switch (val) {
532 case 'e': case 'E':
533 in_exponent = true;
534 add_string_value(val);
535 goto fraction_end;
536 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
537 goto total_end;
538 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
539 if constexpr(as_string_) {
540 value += val;
541 } else {
542 fractional *= 10;
543 value += (val - '0') / fractional;
544 }
545 break;
546 default:
547 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(start));
548 }
549 }
550
551fraction_end:;
552 }
553
554 if (in_exponent) {
555 double exponent = 0;
556 bool negative_exponent = false;
557
558 if (!input.advance()) {
559 throw std::runtime_error("invalid number with trailing 'e/E' at position " + std::to_string(start));
560 }
561
562 char val = input.get();
563 if (!is_digit(val)) {
564 if (val == '-') {
565 negative_exponent = true;
566 add_string_value(val);
567 } else if (val != '+') {
568 throw std::runtime_error("'e/E' should be followed by a sign or digit in number at position " + std::to_string(start));
569 }
570
571 if (!input.advance()) {
572 throw std::runtime_error("invalid number with trailing exponent sign at position " + std::to_string(start));
573 }
574 val = input.get();
575 if (!is_digit(val)) {
576 throw std::runtime_error("exponent sign must be followed by at least one digit in number at position " + std::to_string(start));
577 }
578 }
579
580 if constexpr(as_string_) {
581 value += val;
582 } else {
583 exponent += (val - '0');
584 }
585
586 while (input.advance()) {
587 char val = input.get();
588 switch (val) {
589 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
590 goto exponent_end;
591 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
592 if constexpr(as_string_) {
593 value += val;
594 } else {
595 exponent *= 10;
596 exponent += (val - '0');
597 }
598 break;
599 default:
600 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(start));
601 }
602 }
603
604exponent_end:
605 if constexpr(!as_string_) {
606 if (exponent) {
607 if (negative_exponent) {
608 exponent *= -1;
609 }
610 value *= std::pow(10.0, exponent);
611 }
612 }
613 }
614
615total_end:
616 return value;
617}
618
619struct FakeProvisioner {
620 class FakeBase {
621 public:
622 virtual Type type() const = 0;
623 virtual ~FakeBase() {}
624 };
625 typedef FakeBase Base;
626
627 class FakeBoolean final : public FakeBase {
628 public:
629 Type type() const { return BOOLEAN; }
630 };
631 static FakeBoolean* new_boolean(bool) {
632 return new FakeBoolean;
633 }
634
635 class FakeNumber final : public FakeBase {
636 public:
637 Type type() const { return NUMBER; }
638 };
639 static FakeNumber* new_number(double) {
640 return new FakeNumber;
641 }
642
643 class FakeNumberAsString final : public FakeBase {
644 public:
645 Type type() const { return NUMBER_AS_STRING; }
646 };
647 static FakeNumberAsString* new_number_as_string(std::string) {
648 return new FakeNumberAsString;
649 }
650
651 class FakeString final : public FakeBase {
652 public:
653 Type type() const { return STRING; }
654 };
655 static FakeString* new_string(std::string) {
656 return new FakeString;
657 }
658
659 class FakeNothing final : public FakeBase {
660 public:
661 Type type() const { return NOTHING; }
662 };
663 static FakeNothing* new_nothing() {
664 return new FakeNothing;
665 }
666
667 class FakeArray final : public FakeBase {
668 public:
669 Type type() const { return ARRAY; }
670 };
671 static FakeArray* new_array(std::vector<std::shared_ptr<FakeBase> >) {
672 return new FakeArray;
673 }
674
675 class FakeObject final : public FakeBase {
676 public:
677 Type type() const { return OBJECT; }
678 };
679 static FakeObject* new_object(std::unordered_map<std::string, std::shared_ptr<FakeBase> >) {
680 return new FakeObject;
681 }
682};
683
684template<class Provisioner_, class Input_>
685std::shared_ptr<typename Provisioner_::Base> parse_internal(Input_& input, const ParseOptions& options) {
686 if (!check_and_chomp(input)) {
687 throw std::runtime_error("invalid JSON with no contents");
688 }
689
690 // The most natural algorithm for parsing nested JSON arrays/objects would involve recursion,
691 // but we avoid this to eliminate the associated risk of stack overflows (and maybe improve perf?).
692 // Instead, we use an iterative algorithm with a manual stack for the two nestable JSON types.
693 // We only have to worry about OBJECTs and ARRAYs so there's only two sets of states to manage.
694 std::vector<Type> stack;
695 typedef std::vector<std::shared_ptr<typename Provisioner_::Base> > ArrayContents;
696 std::vector<ArrayContents> array_stack;
697 struct ObjectContents {
698 ObjectContents() = default;
699 ObjectContents(std::string key) : key(std::move(key)) {}
700 std::unordered_map<std::string, std::shared_ptr<typename Provisioner_::Base> > mapping;
701 std::string key;
702 };
703 std::vector<ObjectContents> object_stack;
704
705 unsigned long long start = input.position() + 1;
706 auto extract_object_key = [&]() -> std::string {
707 char next = input.get();
708 if (next != '"') {
709 throw std::runtime_error("expected a string as the object key at position " + std::to_string(input.position() + 1));
710 }
711 auto key = extract_string(input);
712 if (!check_and_chomp(input)) {
713 throw std::runtime_error("unterminated object starting at position " + std::to_string(start));
714 }
715 if (input.get() != ':') {
716 throw std::runtime_error("expected ':' to separate keys and values at position " + std::to_string(input.position() + 1));
717 }
718 if (!advance_and_chomp(input)) {
719 throw std::runtime_error("unterminated object starting at position " + std::to_string(start));
720 }
721 return key;
722 };
723
724 std::shared_ptr<typename Provisioner_::Base> output;
725 while (1) {
726 const char current = input.get();
727 switch(current) {
728 case 't':
729 if (!is_expected_string(input, "true", 4)) {
730 throw std::runtime_error("expected a 'true' string at position " + std::to_string(start));
731 }
732 output.reset(Provisioner_::new_boolean(true));
733 break;
734
735 case 'f':
736 if (!is_expected_string(input, "false", 5)) {
737 throw std::runtime_error("expected a 'false' string at position " + std::to_string(start));
738 }
739 output.reset(Provisioner_::new_boolean(false));
740 break;
741
742 case 'n':
743 if (!is_expected_string(input, "null", 4)) {
744 throw std::runtime_error("expected a 'null' string at position " + std::to_string(start));
745 }
746 output.reset(Provisioner_::new_nothing());
747 break;
748
749 case '"':
750 output.reset(Provisioner_::new_string(extract_string(input)));
751 break;
752
753 case '[':
754 if (!advance_and_chomp(input)) {
755 throw std::runtime_error("unterminated array starting at position " + std::to_string(start));
756 }
757 if (input.get() != ']') {
758 stack.push_back(ARRAY);
759 array_stack.emplace_back();
760 continue; // prepare to parse the first element of the array.
761 }
762 input.advance(); // move past the closing bracket.
763 output.reset(Provisioner_::new_array(std::vector<std::shared_ptr<typename Provisioner_::Base> >{}));
764 break;
765
766 case '{':
767 if (!advance_and_chomp(input)) {
768 throw std::runtime_error("unterminated object starting at position " + std::to_string(start));
769 }
770 if (input.get() != '}') {
771 stack.push_back(OBJECT);
772 object_stack.emplace_back(extract_object_key());
773 continue; // prepare to parse the first value of the object.
774 }
775 input.advance(); // move past the closing brace.
776 output.reset(Provisioner_::new_object(std::unordered_map<std::string, std::shared_ptr<typename Provisioner_::Base> >{}));
777 break;
778
779 case '-':
780 if (!input.advance()) {
781 throw std::runtime_error("incomplete number starting at position " + std::to_string(start));
782 }
783 if (!is_digit(input.get())) {
784 throw std::runtime_error("invalid number starting at position " + std::to_string(start));
785 }
786 if (options.number_as_string) {
787 output.reset(Provisioner_::new_number_as_string("-" + extract_number<true>(input)));
788 } else {
789 output.reset(Provisioner_::new_number(-extract_number<false>(input)));
790 }
791 break;
792
793 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
794 if (options.number_as_string) {
795 output.reset(Provisioner_::new_number_as_string(extract_number<true>(input)));
796 } else {
797 output.reset(Provisioner_::new_number(extract_number<false>(input)));
798 }
799 break;
800
801 default:
802 throw std::runtime_error(std::string("unknown type starting with '") + std::string(1, current) + "' at position " + std::to_string(start));
803 }
804
805 while (1) {
806 if (stack.empty()) {
807 goto parse_finish; // double-break to save ourselves a conditional.
808 }
809
810 if (stack.back() == ARRAY) {
811 auto& contents = array_stack.back();
812 contents.emplace_back(std::move(output));
813
814 if (!check_and_chomp(input)) {
815 throw std::runtime_error("unterminated array starting at position " + std::to_string(start));
816 }
817
818 char next = input.get();
819 if (next == ',') {
820 if (!advance_and_chomp(input)) {
821 throw std::runtime_error("unterminated array starting at position " + std::to_string(start));
822 }
823 break; // prepare to parse the next entry of the array.
824 }
825 if (next != ']') {
826 throw std::runtime_error("unknown character '" + std::string(1, next) + "' in array at position " + std::to_string(input.position() + 1));
827 }
828 input.advance(); // skip the closing bracket.
829
830 output.reset(Provisioner_::new_array(std::move(contents)));
831 stack.pop_back();
832 array_stack.pop_back();
833
834 } else {
835 auto& mapping = object_stack.back().mapping;
836 auto& key = object_stack.back().key;
837 if (mapping.find(key) != mapping.end()) {
838 throw std::runtime_error("detected duplicate keys in the object at position " + std::to_string(input.position() + 1));
839 }
840 mapping[std::move(key)] = std::move(output); // consuming the key here.
841
842 if (!check_and_chomp(input)) {
843 throw std::runtime_error("unterminated object starting at position " + std::to_string(start));
844 }
845
846 char next = input.get();
847 if (next == ',') {
848 if (!advance_and_chomp(input)) {
849 throw std::runtime_error("unterminated object starting at position " + std::to_string(start));
850 }
851 key = extract_object_key();
852 break; // prepare to parse the next value of the object.
853 }
854 if (next != '}') {
855 throw std::runtime_error("unknown character '" + std::string(1, next) + "' in array at position " + std::to_string(input.position() + 1));
856 }
857 input.advance(); // skip the closing brace.
858
859 output.reset(Provisioner_::new_object(std::move(mapping)));
860 stack.pop_back();
861 object_stack.pop_back();
862 }
863 }
864 }
865
866parse_finish:;
867 if (check_and_chomp(input)) {
868 throw std::runtime_error("invalid JSON with trailing non-space characters at position " + std::to_string(input.position() + 1));
869 }
870 return output;
871}
885
890 static Boolean* new_boolean(bool x) {
891 return new Boolean(x);
892 }
893
898 static Number* new_number(double x) {
899 return new Number(x);
900 }
901
906 static NumberAsString* new_number_as_string(std::string x) {
907 return new NumberAsString(std::move(x));
908 }
909
914 static String* new_string(std::string x) {
915 return new String(std::move(x));
916 }
917
922 return new Nothing;
923 }
924
929 static Array* new_array(std::vector<std::shared_ptr<Base> > x) {
930 return new Array(std::move(x));
931 }
932
937 static Object* new_object(std::unordered_map<std::string, std::shared_ptr<Base> > x) {
938 return new Object(std::move(x));
939 }
940};
941
945template<typename Input_>
946auto setup_buffered_reader(Input_& input, const ParseOptions& options) {
947 std::unique_ptr<byteme::BufferedReader<char> > ptr;
948 if (options.parallel) {
949 ptr.reset(new byteme::ParallelBufferedReader<char, Input_*>(&input, options.buffer_size));
950 } else {
951 ptr.reset(new byteme::SerialBufferedReader<char, Input_*>(&input, options.buffer_size));
952 }
953 return ptr;
954}
976template<class Provisioner_ = DefaultProvisioner, class Input_ = byteme::Reader>
977std::shared_ptr<typename DefaultProvisioner::Base> parse(Input_& input, const ParseOptions& options) {
978 auto iptr = setup_buffered_reader(input, options);
979 return parse_internal<Provisioner_>(*iptr, options);
980}
981
994template<class Input_ = byteme::Reader>
995Type validate(Input_& input, const ParseOptions& options) {
996 auto iptr = setup_buffered_reader(input, options);
997 auto ptr = parse_internal<FakeProvisioner>(*iptr, options);
998 return ptr->type();
999}
1000
1011template<class Provisioner_ = DefaultProvisioner>
1012inline std::shared_ptr<typename Provisioner_::Base> parse_string(const char* ptr, std::size_t len, const ParseOptions& options) {
1013 byteme::RawBufferReader input(reinterpret_cast<const unsigned char*>(ptr), len);
1014 return parse<Provisioner_>(input, options);
1015}
1016
1027inline Type validate_string(const char* ptr, std::size_t len, const ParseOptions& options) {
1028 byteme::RawBufferReader input(reinterpret_cast<const unsigned char*>(ptr), len);
1029 return validate(input, options);
1030}
1031
1040template<class Provisioner_ = DefaultProvisioner>
1041std::shared_ptr<Base> parse_file(const char* path, const ParseOptions& options) {
1042 byteme::RawFileReader input(path, {});
1043 return parse(input, options);
1044}
1045
1055inline Type validate_file(const char* path, const ParseOptions& options) {
1056 byteme::RawFileReader input(path, {});
1057 return validate(input, options);
1058}
1059
1063// Back-compatibility only.
1064typedef ParseOptions FileReadOptions;
1065
1066template<class Provisioner_ = DefaultProvisioner, class Input_>
1067std::shared_ptr<typename DefaultProvisioner::Base> parse(Input_& input) {
1068 return parse<Provisioner_>(input, {});
1069}
1070
1071template<class Input_>
1072Type validate(Input_& input) {
1073 return validate(input, {});
1074}
1075
1076template<class Provisioner_ = DefaultProvisioner>
1077inline std::shared_ptr<typename Provisioner_::Base> parse_string(const char* ptr, std::size_t len) {
1078 return parse_string<Provisioner_>(ptr, len, {});
1079}
1080
1081inline Type validate_string(const char* ptr, std::size_t len) {
1082 return validate_string(ptr, len, {});
1083}
1088}
1089
1090#endif
JSON array.
Definition millijson.hpp:186
Type type() const
Definition millijson.hpp:193
std::vector< std::shared_ptr< Base > > & value()
Definition millijson.hpp:206
const std::vector< std::shared_ptr< Base > > & value() const
Definition millijson.hpp:199
Array(std::vector< std::shared_ptr< Base > > x)
Definition millijson.hpp:191
Virtual base class for all JSON types.
Definition millijson.hpp:46
virtual Type type() const =0
JSON boolean.
Definition millijson.hpp:151
Boolean(bool x)
Definition millijson.hpp:156
Type type() const
Definition millijson.hpp:158
const bool & value() const
Definition millijson.hpp:164
bool & value()
Definition millijson.hpp:169
JSON null.
Definition millijson.hpp:178
Type type() const
Definition millijson.hpp:180
JSON number as a string.
Definition millijson.hpp:97
const std::string & value() const
Definition millijson.hpp:110
Type type() const
Definition millijson.hpp:104
NumberAsString(std::string x)
Definition millijson.hpp:102
std::string & value()
Definition millijson.hpp:115
JSON number.
Definition millijson.hpp:70
double & value()
Definition millijson.hpp:88
Type type() const
Definition millijson.hpp:77
const double & value() const
Definition millijson.hpp:83
Number(double x)
Definition millijson.hpp:75
JSON object.
Definition millijson.hpp:217
const std::unordered_map< std::string, std::shared_ptr< Base > > & value() const
Definition millijson.hpp:230
std::unordered_map< std::string, std::shared_ptr< Base > > & value()
Definition millijson.hpp:237
Object(std::unordered_map< std::string, std::shared_ptr< Base > > x)
Definition millijson.hpp:222
Type type() const
Definition millijson.hpp:224
JSON string.
Definition millijson.hpp:124
const std::string & value() const
Definition millijson.hpp:137
std::string & value()
Definition millijson.hpp:142
Type type() const
Definition millijson.hpp:131
String(std::string x)
Definition millijson.hpp:129
A lightweight header-only JSON parser.
Type validate_string(const char *ptr, std::size_t len, const ParseOptions &options)
Definition millijson.hpp:1027
std::shared_ptr< typename DefaultProvisioner::Base > parse(Input_ &input, const ParseOptions &options)
Definition millijson.hpp:977
std::shared_ptr< Base > parse_file(const char *path, const ParseOptions &options)
Definition millijson.hpp:1041
Type validate(Input_ &input, const ParseOptions &options)
Definition millijson.hpp:995
std::shared_ptr< typename Provisioner_::Base > parse_string(const char *ptr, std::size_t len, const ParseOptions &options)
Definition millijson.hpp:1012
Type validate_file(const char *path, const ParseOptions &options)
Definition millijson.hpp:1055
Type
Definition millijson.hpp:33
constexpr Dest_ cap(Value_ x)
Default methods to provision representations of JSON types.
Definition millijson.hpp:879
static Array * new_array(std::vector< std::shared_ptr< Base > > x)
Definition millijson.hpp:929
static NumberAsString * new_number_as_string(std::string x)
Definition millijson.hpp:906
static Number * new_number(double x)
Definition millijson.hpp:898
static Object * new_object(std::unordered_map< std::string, std::shared_ptr< Base > > x)
Definition millijson.hpp:937
static Nothing * new_nothing()
Definition millijson.hpp:921
static Boolean * new_boolean(bool x)
Definition millijson.hpp:890
::millijson::Base Base
Definition millijson.hpp:884
static String * new_string(std::string x)
Definition millijson.hpp:914
Options for parse().
Definition millijson.hpp:248
bool number_as_string
Definition millijson.hpp:254
bool parallel
Definition millijson.hpp:266
std::size_t buffer_size
Definition millijson.hpp:260