#ifndef SIMDJSON_PARSEDJSON_H #define SIMDJSON_PARSEDJSON_H #include #include #include #include #include "simdjson/portability.h" #include "simdjson/jsonformatutils.h" #include "simdjson/common_defs.h" #define JSONVALUEMASK 0xFFFFFFFFFFFFFF #define DEFAULTMAXDEPTH 1024// a JSON document with a depth exceeding 1024 is probably de facto invalid /************ * The JSON is parsed to a tape, see the accompanying tape.md file * for documentation. ***********/ struct ParsedJson { public: // create a ParsedJson container with zero capacity, call allocateCapacity to // allocate memory ParsedJson(); ~ParsedJson(); ParsedJson(ParsedJson && p); // if needed, allocate memory so that the object is able to process JSON // documents having up to len butes and maxdepth "depth" WARN_UNUSED bool allocateCapacity(size_t len, size_t maxdepth = DEFAULTMAXDEPTH); bool isValid() const; // deallocate memory and set capacity to zero, called automatically by the // destructor void deallocate(); // this should be called when parsing (right before writing the tapes) void init(); // print the json to stdout (should be valid) // return false if the tape is likely wrong (e.g., you did not parse a valid // JSON). WARN_UNUSED bool printjson(std::ostream &os); WARN_UNUSED bool dump_raw_tape(std::ostream &os); // all nodes are stored on the tape using a 64-bit word. // // strings, double and ints are stored as // a 64-bit word with a pointer to the actual value // // // // for objects or arrays, store [ or { at the beginning and } and ] at the // end. For the openings ([ or {), we annotate them with a reference to the // location on the tape of the end, and for then closings (} and ]), we // annotate them with a reference to the location of the opening // // // this should be considered a private function really_inline void write_tape(uint64_t val, uint8_t c) { tape[current_loc++] = val | (((uint64_t)c) << 56); } really_inline void write_tape_s64(int64_t i) { write_tape(0, 'l'); tape[current_loc++] = *((uint64_t *)&i); } really_inline void write_tape_double(double d) { write_tape(0, 'd'); static_assert(sizeof(d) == sizeof(tape[current_loc]), "mismatch size"); memcpy(& tape[current_loc++], &d, sizeof(double)); //tape[current_loc++] = *((uint64_t *)&d); } really_inline uint32_t get_current_loc() { return current_loc; } really_inline void annotate_previousloc(uint32_t saved_loc, uint64_t val) { tape[saved_loc] |= val; } struct iterator { explicit iterator(ParsedJson &pj_); ~iterator(); iterator(const iterator &o); iterator(iterator &&o); bool isOk() const; // useful for debuging purposes size_t get_tape_location() const; // useful for debuging purposes size_t get_tape_length() const; // returns the current depth (start at 1 with 0 reserved for the fictitious root node) size_t get_depth() const; // A scope is a series of nodes at the same depth, typically it is either an object ({) or an array ([). // The root node has type 'r'. uint8_t get_scope_type() const; // move forward in document order bool move_forward(); // retrieve the character code of what we're looking at: // [{"sltfn are the possibilities uint8_t get_type() const; // get the int64_t value at this node; valid only if we're at "l" int64_t get_integer() const; // get the string value at this node (NULL ended); valid only if we're at " // note that tabs, and line endings are escaped in the returned value (see print_with_escapes) // return value is valid UTF-8 const char * get_string() const; // get the double value at this node; valid only if // we're at "d" double get_double() const; bool is_object_or_array() const; bool is_object() const; bool is_array() const; bool is_string() const; bool is_integer() const; bool is_double() const; static bool is_object_or_array(uint8_t type); // when at {, go one level deep, looking for a given key // if successful, we are left pointing at the value, // if not, we are still pointing at the object ({) // (in case of repeated keys, this only finds the first one) bool move_to_key(const char * key); // throughout return true if we can do the navigation, false // otherwise // Withing a given scope (series of nodes at the same depth within either an // array or an object), we move forward. // Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { and [. // At the object ({) or at the array ([), you can issue a "down" to visit their content. // valid if we're not at the end of a scope (returns true). bool next(); // Withing a given scope (series of nodes at the same depth within either an // array or an object), we move backward. // Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true when starting at the end // of the scope. // At the object ({) or at the array ([), you can issue a "down" to visit their content. bool prev(); // Moves back to either the containing array or object (type { or [) from // within a contained scope. // Valid unless we are at the first level of the document bool up(); // Valid if we're at a [ or { and it starts a non-empty scope; moves us to start of // that deeper scope if it not empty. // Thus, given [true, null, {"a":1}, [1,2]], if we are at the { node, we would move to the // "a" node. bool down(); // move us to the start of our current scope, // a scope is a series of nodes at the same level void to_start_scope(); // void to_end_scope(); // move us to // the start of our current scope; always succeeds // print the thing we're currently pointing at bool print(std::ostream &os, bool escape_strings = true) const; typedef struct {size_t start_of_scope; uint8_t scope_type;} scopeindex_t; private: iterator& operator=(const iterator& other) ; ParsedJson &pj; size_t depth; size_t location; // our current location on a tape size_t tape_length; uint8_t current_type; uint64_t current_val; scopeindex_t *depthindex; }; size_t bytecapacity; // indicates how many bits are meant to be supported size_t depthcapacity; // how deep we can go size_t tapecapacity; size_t stringcapacity; uint32_t current_loc; uint32_t n_structural_indexes; uint32_t *structural_indexes; uint64_t *tape; uint32_t *containing_scope_offset; #ifdef SIMDJSON_USE_COMPUTED_GOTO void **ret_address; #else char *ret_address; #endif uint8_t *string_buf; // should be at least bytecapacity uint8_t *current_string_buf_loc; bool isvalid; private : ParsedJson(const ParsedJson & p) = delete; ParsedJson & operator=(const ParsedJson&o) = delete; }; // dump bits low to high inline void dumpbits_always(uint64_t v, const std::string &msg) { for (uint32_t i = 0; i < 64; i++) { std::cout << (((v >> (uint64_t)i) & 0x1ULL) ? "1" : "_"); } std::cout << " " << msg.c_str() << "\n"; } inline void dumpbits32_always(uint32_t v, const std::string &msg) { for (uint32_t i = 0; i < 32; i++) { std::cout << (((v >> (uint32_t)i) & 0x1ULL) ? "1" : "_"); } std::cout << " " << msg.c_str() << "\n"; } #endif