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 input.advance(); // get past the opening quote.
326 std::string output;
327
328 while (1) {
329 char next = input.get();
330 switch (next) {
331 case '"':
332 input.advance(); // get past the closing quote.
333 return output;
334
335 case '\\':
336 if (!input.advance()) {
337 throw std::runtime_error("unterminated string at position " + std::to_string(input.position() + 1));
338 } else {
339 char next2 = input.get();
340 switch (next2) {
341 case '"':
342 output += '"';
343 break;
344 case 'n':
345 output += '\n';
346 break;
347 case 'r':
348 output += '\r';
349 break;
350 case '\\':
351 output += '\\';
352 break;
353 case '/':
354 output += '/';
355 break;
356 case 'b':
357 output += '\b';
358 break;
359 case 'f':
360 output += '\f';
361 break;
362 case 't':
363 output += '\t';
364 break;
365 case 'u':
366 {
367 unsigned short mb = 0;
368 for (int i = 0; i < 4; ++i) {
369 if (!input.advance()){
370 throw std::runtime_error("unterminated string at position " + std::to_string(input.position() + 1));
371 }
372 mb *= 16;
373 char val = input.get();
374 switch (val) {
375 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
376 mb += val - '0';
377 break;
378 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
379 mb += (val - 'a') + 10;
380 break;
381 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
382 mb += (val - 'A') + 10;
383 break;
384 default:
385 throw std::runtime_error("invalid unicode escape detected at position " + std::to_string(input.position() + 1));
386 }
387 }
388
389 // Manually convert Unicode code points to UTF-8. We only allow
390 // 3 bytes at most because there's only 4 hex digits in JSON.
391 if (mb <= 127) {
392 output += static_cast<char>(mb);
393 } else if (mb <= 2047) {
394 unsigned char left = (mb >> 6) | 0b11000000;
395 output += *(reinterpret_cast<char*>(&left));
396 unsigned char right = (mb & 0b00111111) | 0b10000000;
397 output += *(reinterpret_cast<char*>(&right));
398 } else {
399 unsigned char left = (mb >> 12) | 0b11100000;
400 output += *(reinterpret_cast<char*>(&left));
401 unsigned char middle = ((mb >> 6) & 0b00111111) | 0b10000000;
402 output += *(reinterpret_cast<char*>(&middle));
403 unsigned char right = (mb & 0b00111111) | 0b10000000;
404 output += *(reinterpret_cast<char*>(&right));
405 }
406 }
407 break;
408 default:
409 throw std::runtime_error("unrecognized escape '\\" + std::string(1, next2) + "'");
410 }
411 }
412 break;
413
414 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:
415 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:
416 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:
417 case (char)30: case (char)31:
418 case (char)127:
419 throw std::runtime_error("string contains ASCII control character at position " + std::to_string(input.position() + 1));
420
421 default:
422 output += next;
423 break;
424 }
425
426 if (!input.advance()) {
427 throw std::runtime_error("unterminated string at position " + std::to_string(input.position() + 1));
428 }
429 }
430
431 return output; // Technically unreachable, but whatever.
432}
433
434template<bool as_string_, class Input_>
435typename std::conditional<as_string_, std::string, double>::type extract_number(Input_& input) {
436 auto value = []{
437 if constexpr(as_string_) {
438 return std::string("");
439 } else {
440 return static_cast<double>(0);
441 }
442 }();
443 bool in_fraction = false;
444 bool in_exponent = false;
445
446 auto add_string_value = [&](char x) -> void {
447 if constexpr(as_string_) {
448 value += x;
449 }
450 };
451
452 // We assume we're starting from the absolute value, after removing any preceding negative sign.
453 char lead = input.get();
454 add_string_value(lead);
455 if (lead == '0') {
456 if (!input.advance()) {
457 return value;
458 }
459
460 auto after_zero = input.get();
461 switch (after_zero) {
462 case '.':
463 add_string_value(after_zero);
464 in_fraction = true;
465 break;
466 case 'e': case 'E':
467 add_string_value(after_zero);
468 in_exponent = true;
469 break;
470 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
471 return value;
472 default:
473 throw std::runtime_error("invalid number starting with 0 at position " + std::to_string(input.position() + 1));
474 }
475
476 } else { // 'lead' must be a digit, as extract_number is only called when the current character is a digit.
477 if constexpr(!as_string_) {
478 value += lead - '0';
479 }
480
481 while (input.advance()) {
482 char val = input.get();
483 switch (val) {
484 case '.':
485 add_string_value(val);
486 in_fraction = true;
487 goto integral_end;
488 case 'e': case 'E':
489 add_string_value(val);
490 in_exponent = true;
491 goto integral_end;
492 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
493 goto total_end;
494 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
495 if constexpr(as_string_) {
496 value += val;
497 } else {
498 value *= 10;
499 value += val - '0';
500 }
501 break;
502 default:
503 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(input.position() + 1));
504 }
505 }
506
507integral_end:;
508 }
509
510 if (in_fraction) {
511 if (!input.advance()) {
512 throw std::runtime_error("invalid number with trailing '.' at position " + std::to_string(input.position() + 1));
513 }
514
515 char val = input.get();
516 if (!is_digit(val)) {
517 throw std::runtime_error("'.' must be followed by at least one digit at position " + std::to_string(input.position() + 1));
518 }
519
520 double fractional = 10;
521 if constexpr(as_string_) {
522 value += val;
523 } else {
524 value += (val - '0') / fractional;
525 }
526
527 while (input.advance()) {
528 char val = input.get();
529 switch (val) {
530 case 'e': case 'E':
531 in_exponent = true;
532 add_string_value(val);
533 goto fraction_end;
534 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
535 goto total_end;
536 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
537 if constexpr(as_string_) {
538 value += val;
539 } else {
540 fractional *= 10;
541 value += (val - '0') / fractional;
542 }
543 break;
544 default:
545 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(input.position() + 1));
546 }
547 }
548
549fraction_end:;
550 }
551
552 if (in_exponent) {
553 double exponent = 0;
554 bool negative_exponent = false;
555
556 if (!input.advance()) {
557 throw std::runtime_error("invalid number with trailing 'e/E' at position " + std::to_string(input.position() + 1));
558 }
559
560 char val = input.get();
561 if (!is_digit(val)) {
562 if (val == '-') {
563 negative_exponent = true;
564 add_string_value(val);
565 } else if (val != '+') {
566 throw std::runtime_error("'e/E' should be followed by a sign or digit in number at position " + std::to_string(input.position() + 1));
567 }
568
569 if (!input.advance()) {
570 throw std::runtime_error("invalid number with trailing exponent sign at position " + std::to_string(input.position() + 1));
571 }
572 val = input.get();
573 if (!is_digit(val)) {
574 throw std::runtime_error("exponent sign must be followed by at least one digit in number at position " + std::to_string(input.position() + 1));
575 }
576 }
577
578 if constexpr(as_string_) {
579 value += val;
580 } else {
581 exponent += (val - '0');
582 }
583
584 while (input.advance()) {
585 char val = input.get();
586 switch (val) {
587 case ',': case ']': case '}': case ' ': case '\r': case '\n': case '\t':
588 goto exponent_end;
589 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
590 if constexpr(as_string_) {
591 value += val;
592 } else {
593 exponent *= 10;
594 exponent += (val - '0');
595 }
596 break;
597 default:
598 throw std::runtime_error("invalid number containing '" + std::string(1, val) + "' at position " + std::to_string(input.position() + 1));
599 }
600 }
601
602exponent_end:
603 if constexpr(!as_string_) {
604 if (exponent) {
605 if (negative_exponent) {
606 exponent *= -1;
607 }
608 value *= std::pow(10.0, exponent);
609 }
610 }
611 }
612
613total_end:
614 return value;
615}
616
617struct FakeProvisioner {
618 class FakeBase {
619 public:
620 virtual Type type() const = 0;
621 virtual ~FakeBase() {}
622 };
623 typedef FakeBase Base;
624
625 class FakeBoolean final : public FakeBase {
626 public:
627 Type type() const { return BOOLEAN; }
628 };
629 static FakeBoolean* new_boolean(bool) {
630 return new FakeBoolean;
631 }
632
633 class FakeNumber final : public FakeBase {
634 public:
635 Type type() const { return NUMBER; }
636 };
637 static FakeNumber* new_number(double) {
638 return new FakeNumber;
639 }
640
641 class FakeNumberAsString final : public FakeBase {
642 public:
643 Type type() const { return NUMBER_AS_STRING; }
644 };
645 static FakeNumberAsString* new_number_as_string(std::string) {
646 return new FakeNumberAsString;
647 }
648
649 class FakeString final : public FakeBase {
650 public:
651 Type type() const { return STRING; }
652 };
653 static FakeString* new_string(std::string) {
654 return new FakeString;
655 }
656
657 class FakeNothing final : public FakeBase {
658 public:
659 Type type() const { return NOTHING; }
660 };
661 static FakeNothing* new_nothing() {
662 return new FakeNothing;
663 }
664
665 class FakeArray final : public FakeBase {
666 public:
667 Type type() const { return ARRAY; }
668 };
669 static FakeArray* new_array(std::vector<std::shared_ptr<FakeBase> >) {
670 return new FakeArray;
671 }
672
673 class FakeObject final : public FakeBase {
674 public:
675 Type type() const { return OBJECT; }
676 };
677 static FakeObject* new_object(std::unordered_map<std::string, std::shared_ptr<FakeBase> >) {
678 return new FakeObject;
679 }
680};
681
682template<class Provisioner_, class Input_>
683std::shared_ptr<typename Provisioner_::Base> parse_internal(Input_& input, const ParseOptions& options) {
684 if (!check_and_chomp(input)) {
685 throw std::runtime_error("invalid JSON with no contents");
686 }
687
688 // The most natural algorithm for parsing nested JSON arrays/objects would involve recursion,
689 // but we avoid this to eliminate the associated risk of stack overflows (and maybe improve perf?).
690 // Instead, we use an iterative algorithm with a manual stack for the two nestable JSON types.
691 // We only have to worry about OBJECTs and ARRAYs so there's only two sets of states to manage.
692 std::vector<Type> stack;
693 typedef std::vector<std::shared_ptr<typename Provisioner_::Base> > ArrayContents;
694 std::vector<ArrayContents> array_stack;
695 struct ObjectContents {
696 ObjectContents() = default;
697 ObjectContents(std::string key) : key(std::move(key)) {}
698 std::unordered_map<std::string, std::shared_ptr<typename Provisioner_::Base> > mapping;
699 std::string key;
700 };
701 std::vector<ObjectContents> object_stack;
702
703 auto extract_object_key = [&]() -> std::string {
704 char next = input.get();
705 if (next != '"') {
706 throw std::runtime_error("expected a string as the object key at position " + std::to_string(input.position() + 1));
707 }
708 auto key = extract_string(input);
709 if (!check_and_chomp(input)) {
710 throw std::runtime_error("unterminated object at position " + std::to_string(input.position() + 1));
711 }
712 if (input.get() != ':') {
713 throw std::runtime_error("expected ':' to separate keys and values at position " + std::to_string(input.position() + 1));
714 }
715 if (!advance_and_chomp(input)) {
716 throw std::runtime_error("unterminated object at position " + std::to_string(input.position() + 1));
717 }
718 return key;
719 };
720
721 std::shared_ptr<typename Provisioner_::Base> output;
722 while (1) {
723 const char current = input.get();
724 switch(current) {
725 case 't':
726 if (!is_expected_string(input, "true", 4)) {
727 throw std::runtime_error("expected a 'true' string at position " + std::to_string(input.position() + 1));
728 }
729 output.reset(Provisioner_::new_boolean(true));
730 break;
731
732 case 'f':
733 if (!is_expected_string(input, "false", 5)) {
734 throw std::runtime_error("expected a 'false' string at position " + std::to_string(input.position() + 1));
735 }
736 output.reset(Provisioner_::new_boolean(false));
737 break;
738
739 case 'n':
740 if (!is_expected_string(input, "null", 4)) {
741 throw std::runtime_error("expected a 'null' string at position " + std::to_string(input.position() + 1));
742 }
743 output.reset(Provisioner_::new_nothing());
744 break;
745
746 case '"':
747 output.reset(Provisioner_::new_string(extract_string(input)));
748 break;
749
750 case '[':
751 if (!advance_and_chomp(input)) {
752 throw std::runtime_error("unterminated array at position " + std::to_string(input.position() + 1));
753 }
754 if (input.get() != ']') {
755 stack.push_back(ARRAY);
756 array_stack.emplace_back();
757 continue; // prepare to parse the first element of the array.
758 }
759 input.advance(); // move past the closing bracket.
760 output.reset(Provisioner_::new_array(std::vector<std::shared_ptr<typename Provisioner_::Base> >{}));
761 break;
762
763 case '{':
764 if (!advance_and_chomp(input)) {
765 throw std::runtime_error("unterminated object at position " + std::to_string(input.position() + 1));
766 }
767 if (input.get() != '}') {
768 stack.push_back(OBJECT);
769 object_stack.emplace_back(extract_object_key());
770 continue; // prepare to parse the first value of the object.
771 }
772 input.advance(); // move past the closing brace.
773 output.reset(Provisioner_::new_object(std::unordered_map<std::string, std::shared_ptr<typename Provisioner_::Base> >{}));
774 break;
775
776 case '-':
777 if (!input.advance()) {
778 throw std::runtime_error("incomplete number at position " + std::to_string(input.position() + 1));
779 }
780 if (!is_digit(input.get())) {
781 throw std::runtime_error("invalid number at position " + std::to_string(input.position() + 1));
782 }
783 if (options.number_as_string) {
784 output.reset(Provisioner_::new_number_as_string("-" + extract_number<true>(input)));
785 } else {
786 output.reset(Provisioner_::new_number(-extract_number<false>(input)));
787 }
788 break;
789
790 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
791 if (options.number_as_string) {
792 output.reset(Provisioner_::new_number_as_string(extract_number<true>(input)));
793 } else {
794 output.reset(Provisioner_::new_number(extract_number<false>(input)));
795 }
796 break;
797
798 default:
799 throw std::runtime_error(std::string("unknown type starting with '") + std::string(1, current) + "' at position " + std::to_string(input.position() + 1));
800 }
801
802 while (1) {
803 if (stack.empty()) {
804 goto parse_finish; // double-break to save ourselves a conditional.
805 }
806
807 if (stack.back() == ARRAY) {
808 auto& contents = array_stack.back();
809 contents.emplace_back(std::move(output));
810
811 if (!check_and_chomp(input)) {
812 throw std::runtime_error("unterminated array at position " + std::to_string(input.position() + 1));
813 }
814
815 char next = input.get();
816 if (next == ',') {
817 if (!advance_and_chomp(input)) {
818 throw std::runtime_error("unterminated array at position " + std::to_string(input.position() + 1));
819 }
820 break; // prepare to parse the next entry of the array.
821 }
822 if (next != ']') {
823 throw std::runtime_error("unknown character '" + std::string(1, next) + "' in array at position " + std::to_string(input.position() + 1));
824 }
825 input.advance(); // skip the closing bracket.
826
827 output.reset(Provisioner_::new_array(std::move(contents)));
828 stack.pop_back();
829 array_stack.pop_back();
830
831 } else {
832 auto& mapping = object_stack.back().mapping;
833 auto& key = object_stack.back().key;
834 if (mapping.find(key) != mapping.end()) {
835 throw std::runtime_error("detected duplicate keys in the object at position " + std::to_string(input.position() + 1));
836 }
837 mapping[std::move(key)] = std::move(output); // consuming the key here.
838
839 if (!check_and_chomp(input)) {
840 throw std::runtime_error("unterminated object at position " + std::to_string(input.position() + 1));
841 }
842
843 char next = input.get();
844 if (next == ',') {
845 if (!advance_and_chomp(input)) {
846 throw std::runtime_error("unterminated object at position " + std::to_string(input.position() + 1));
847 }
848 key = extract_object_key();
849 break; // prepare to parse the next value of the object.
850 }
851 if (next != '}') {
852 throw std::runtime_error("unknown character '" + std::string(1, next) + "' in array at position " + std::to_string(input.position() + 1));
853 }
854 input.advance(); // skip the closing brace.
855
856 output.reset(Provisioner_::new_object(std::move(mapping)));
857 stack.pop_back();
858 object_stack.pop_back();
859 }
860 }
861 }
862
863parse_finish:;
864 if (check_and_chomp(input)) {
865 throw std::runtime_error("invalid JSON with trailing non-space characters at position " + std::to_string(input.position() + 1));
866 }
867 return output;
868}
882
887 static Boolean* new_boolean(bool x) {
888 return new Boolean(x);
889 }
890
895 static Number* new_number(double x) {
896 return new Number(x);
897 }
898
903 static NumberAsString* new_number_as_string(std::string x) {
904 return new NumberAsString(std::move(x));
905 }
906
911 static String* new_string(std::string x) {
912 return new String(std::move(x));
913 }
914
919 return new Nothing;
920 }
921
926 static Array* new_array(std::vector<std::shared_ptr<Base> > x) {
927 return new Array(std::move(x));
928 }
929
934 static Object* new_object(std::unordered_map<std::string, std::shared_ptr<Base> > x) {
935 return new Object(std::move(x));
936 }
937};
938
942template<typename Input_>
943auto setup_buffered_reader(Input_& input, const ParseOptions& options) {
944 std::unique_ptr<byteme::BufferedReader<char> > ptr;
945 if (options.parallel) {
946 ptr.reset(new byteme::ParallelBufferedReader<char, Input_*>(&input, options.buffer_size));
947 } else {
948 ptr.reset(new byteme::SerialBufferedReader<char, Input_*>(&input, options.buffer_size));
949 }
950 return ptr;
951}
973template<class Provisioner_ = DefaultProvisioner, class Input_ = byteme::Reader>
974std::shared_ptr<typename DefaultProvisioner::Base> parse(Input_& input, const ParseOptions& options) {
975 auto iptr = setup_buffered_reader(input, options);
976 return parse_internal<Provisioner_>(*iptr, options);
977}
978
991template<class Input_ = byteme::Reader>
992Type validate(Input_& input, const ParseOptions& options) {
993 auto iptr = setup_buffered_reader(input, options);
994 auto ptr = parse_internal<FakeProvisioner>(*iptr, options);
995 return ptr->type();
996}
997
1008template<class Provisioner_ = DefaultProvisioner>
1009inline std::shared_ptr<typename Provisioner_::Base> parse_string(const char* ptr, std::size_t len, const ParseOptions& options) {
1010 byteme::RawBufferReader input(reinterpret_cast<const unsigned char*>(ptr), len);
1011 return parse<Provisioner_>(input, options);
1012}
1013
1024inline Type validate_string(const char* ptr, std::size_t len, const ParseOptions& options) {
1025 byteme::RawBufferReader input(reinterpret_cast<const unsigned char*>(ptr), len);
1026 return validate(input, options);
1027}
1028
1037template<class Provisioner_ = DefaultProvisioner>
1038std::shared_ptr<Base> parse_file(const char* path, const ParseOptions& options) {
1039 byteme::RawFileReader input(path, {});
1040 return parse(input, options);
1041}
1042
1052inline Type validate_file(const char* path, const ParseOptions& options) {
1053 byteme::RawFileReader input(path, {});
1054 return validate(input, options);
1055}
1056
1060// Back-compatibility only.
1061typedef ParseOptions FileReadOptions;
1062
1063template<class Provisioner_ = DefaultProvisioner, class Input_>
1064std::shared_ptr<typename DefaultProvisioner::Base> parse(Input_& input) {
1065 return parse<Provisioner_>(input, {});
1066}
1067
1068template<class Input_>
1069Type validate(Input_& input) {
1070 return validate(input, {});
1071}
1072
1073template<class Provisioner_ = DefaultProvisioner>
1074inline std::shared_ptr<typename Provisioner_::Base> parse_string(const char* ptr, std::size_t len) {
1075 return parse_string<Provisioner_>(ptr, len, {});
1076}
1077
1078inline Type validate_string(const char* ptr, std::size_t len) {
1079 return validate_string(ptr, len, {});
1080}
1085}
1086
1087#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:1024
std::shared_ptr< typename DefaultProvisioner::Base > parse(Input_ &input, const ParseOptions &options)
Definition millijson.hpp:974
std::shared_ptr< Base > parse_file(const char *path, const ParseOptions &options)
Definition millijson.hpp:1038
Type validate(Input_ &input, const ParseOptions &options)
Definition millijson.hpp:992
std::shared_ptr< typename Provisioner_::Base > parse_string(const char *ptr, std::size_t len, const ParseOptions &options)
Definition millijson.hpp:1009
Type validate_file(const char *path, const ParseOptions &options)
Definition millijson.hpp:1052
Type
Definition millijson.hpp:33
constexpr Dest_ cap(Value_ x)
Default methods to provision representations of JSON types.
Definition millijson.hpp:876
static Array * new_array(std::vector< std::shared_ptr< Base > > x)
Definition millijson.hpp:926
static NumberAsString * new_number_as_string(std::string x)
Definition millijson.hpp:903
static Number * new_number(double x)
Definition millijson.hpp:895
static Object * new_object(std::unordered_map< std::string, std::shared_ptr< Base > > x)
Definition millijson.hpp:934
static Nothing * new_nothing()
Definition millijson.hpp:918
static Boolean * new_boolean(bool x)
Definition millijson.hpp:887
::millijson::Base Base
Definition millijson.hpp:881
static String * new_string(std::string x)
Definition millijson.hpp:911
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