#ifndef SIMDJSON_INLINE_DOCUMENT_H #define SIMDJSON_INLINE_DOCUMENT_H #include "simdjson/document.h" // Inline implementations go in here if they aren't small enough to go in the class itself or if // there are complex header file dependencies that need to be broken by externalizing the // implementation. #include "simdjson/implementation.h" #include namespace simdjson { // // document::element_result inline implementation // template inline document::element_result::element_result(T _value) noexcept : value(_value), error{SUCCESS} {} template inline document::element_result::element_result(error_code _error) noexcept : value(), error{_error} {} template<> inline document::element_result::operator std::string_view() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } template<> inline document::element_result::operator const char *() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } template<> inline document::element_result::operator bool() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } template<> inline document::element_result::operator uint64_t() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } template<> inline document::element_result::operator int64_t() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } template<> inline document::element_result::operator double() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } // // document::element_result inline implementation // inline document::element_result::element_result(document::array _value) noexcept : value(_value), error{SUCCESS} {} inline document::element_result::element_result(error_code _error) noexcept : value(), error{_error} {} inline document::element_result::operator document::array() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } inline document::array::iterator document::element_result::begin() const noexcept(false) { if (error) { throw invalid_json(error); } return value.begin(); } inline document::array::iterator document::element_result::end() const noexcept(false) { if (error) { throw invalid_json(error); } return value.end(); } // // document::element_result inline implementation // inline document::element_result::element_result(document::object _value) noexcept : value(_value), error{SUCCESS} {} inline document::element_result::element_result(error_code _error) noexcept : value(), error{_error} {} inline document::element_result::operator document::object() const noexcept(false) { if (error) { throw invalid_json(error); } return value; } inline document::element_result document::element_result::operator[](const std::string_view &key) const noexcept { if (error) { return error; } return value[key]; } inline document::element_result document::element_result::operator[](const char *key) const noexcept { if (error) { return error; } return value[key]; } inline document::object::iterator document::element_result::begin() const noexcept(false) { if (error) { throw invalid_json(error); } return value.begin(); } inline document::object::iterator document::element_result::end() const noexcept(false) { if (error) { throw invalid_json(error); } return value.end(); } // // document::element_result inline implementation // inline document::element_result::element_result(document::element _value) noexcept : value(_value), error{SUCCESS} {} inline document::element_result::element_result(error_code _error) noexcept : value(), error{_error} {} inline document::element_result document::element_result::is_null() const noexcept { if (error) { return error; } return value.is_null(); } inline document::element_result document::element_result::as_bool() const noexcept { if (error) { return error; } return value.as_bool(); } inline document::element_result document::element_result::as_c_str() const noexcept { if (error) { return error; } return value.as_c_str(); } inline document::element_result document::element_result::as_string() const noexcept { if (error) { return error; } return value.as_string(); } inline document::element_result document::element_result::as_uint64_t() const noexcept { if (error) { return error; } return value.as_uint64_t(); } inline document::element_result document::element_result::as_int64_t() const noexcept { if (error) { return error; } return value.as_int64_t(); } inline document::element_result document::element_result::as_double() const noexcept { if (error) { return error; } return value.as_double(); } inline document::element_result document::element_result::as_array() const noexcept { if (error) { return error; } return value.as_array(); } inline document::element_result document::element_result::as_object() const noexcept { if (error) { return error; } return value.as_object(); } inline document::element_result::operator bool() const noexcept(false) { return as_bool(); } inline document::element_result::operator const char *() const noexcept(false) { return as_c_str(); } inline document::element_result::operator std::string_view() const noexcept(false) { return as_string(); } inline document::element_result::operator uint64_t() const noexcept(false) { return as_uint64_t(); } inline document::element_result::operator int64_t() const noexcept(false) { return as_int64_t(); } inline document::element_result::operator double() const noexcept(false) { return as_double(); } inline document::element_result::operator document::array() const noexcept(false) { return as_array(); } inline document::element_result::operator document::object() const noexcept(false) { return as_object(); } inline document::element_result document::element_result::operator[](const std::string_view &key) const noexcept { if (error) { return *this; } return value[key]; } inline document::element_result document::element_result::operator[](const char *key) const noexcept { if (error) { return *this; } return value[key]; } // // document inline implementation // inline document::element document::root() const noexcept { return document::element(this, 1); } inline document::element_result document::as_array() const noexcept { return root().as_array(); } inline document::element_result document::as_object() const noexcept { return root().as_object(); } inline document::operator document::element() const noexcept { return root(); } inline document::operator document::array() const noexcept(false) { return root(); } inline document::operator document::object() const noexcept(false) { return root(); } inline document::element_result document::operator[](const std::string_view &key) const noexcept { return root()[key]; } inline document::element_result document::operator[](const char *key) const noexcept { return root()[key]; } // // document::doc_ref_result inline implementation // inline document::doc_ref_result::doc_ref_result(document &_doc, error_code _error) noexcept : doc(_doc), error(_error) { } inline document::doc_ref_result::operator document&() noexcept(false) { if (error) { throw invalid_json(error); } return doc; } inline const std::string &document::doc_ref_result::get_error_message() const noexcept { return error_message(error); } // // document::doc_result inline implementation // inline document::doc_result::doc_result(document &&_doc, error_code _error) noexcept : doc(std::move(_doc)), error(_error) { } inline document::doc_result::doc_result(document &&_doc) noexcept : doc(std::move(_doc)), error(SUCCESS) { } inline document::doc_result::doc_result(error_code _error) noexcept : doc(), error(_error) { } inline document::doc_result::operator document() noexcept(false) { if (error) { throw invalid_json(error); } return std::move(doc); } inline const std::string &document::doc_result::get_error_message() const noexcept { return error_message(error); } // // document::parser inline implementation // inline bool document::parser::is_valid() const noexcept { return valid; } inline int document::parser::get_error_code() const noexcept { return error; } inline std::string document::parser::get_error_message() const noexcept { return error_message(error); } inline bool document::parser::print_json(std::ostream &os) const noexcept { return is_valid() ? doc.print_json(os) : false; } inline bool document::parser::dump_raw_tape(std::ostream &os) const noexcept { return is_valid() ? doc.dump_raw_tape(os) : false; } inline const document &document::parser::get_document() const noexcept(false) { if (!is_valid()) { throw invalid_json(error); } return doc; } inline document::doc_ref_result document::parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept { error_code code = init_parse(len); if (code) { return document::doc_ref_result(doc, code); } if (realloc_if_needed) { const uint8_t *tmp_buf = buf; buf = (uint8_t *)allocate_padded_buffer(len); if (buf == nullptr) return document::doc_ref_result(doc, MEMALLOC); memcpy((void *)buf, tmp_buf, len); } code = simdjson::active_implementation->parse(buf, len, *this); // We're indicating validity via the doc_ref_result, so set the parse state back to invalid valid = false; error = UNINITIALIZED; if (realloc_if_needed) { aligned_free((void *)buf); // must free before we exit } return document::doc_ref_result(doc, code); } really_inline document::doc_ref_result document::parser::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept { return parse((const uint8_t *)buf, len, realloc_if_needed); } really_inline document::doc_ref_result document::parser::parse(const std::string &s) noexcept { return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); } really_inline document::doc_ref_result document::parser::parse(const padded_string &s) noexcept { return parse(s.data(), s.length(), false); } inline document::doc_result document::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept { document::parser parser; if (!parser.allocate_capacity(len)) { return MEMALLOC; } auto [doc, error] = parser.parse(buf, len, realloc_if_needed); return document::doc_result((document &&)doc, error); } really_inline document::doc_result document::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept { return parse((const uint8_t *)buf, len, realloc_if_needed); } really_inline document::doc_result document::parse(const std::string &s) noexcept { return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); } really_inline document::doc_result document::parse(const padded_string &s) noexcept { return parse(s.data(), s.length(), false); } // // Parser callbacks // WARN_UNUSED inline error_code document::parser::init_parse(size_t len) noexcept { if (len > capacity()) { return error = CAPACITY; } // If the last doc was taken, we need to allocate a new one if (!doc.tape) { if (!doc.set_capacity(len)) { return error = MEMALLOC; } } return SUCCESS; } inline void document::parser::init_stage2() noexcept { current_string_buf_loc = doc.string_buf.get(); current_loc = 0; valid = false; error = UNINITIALIZED; } really_inline error_code document::parser::on_error(error_code new_error_code) noexcept { error = new_error_code; return new_error_code; } really_inline error_code document::parser::on_success(error_code success_code) noexcept { error = success_code; valid = true; return success_code; } really_inline bool document::parser::on_start_document(uint32_t depth) noexcept { containing_scope_offset[depth] = current_loc; write_tape(0, tape_type::ROOT); return true; } really_inline bool document::parser::on_start_object(uint32_t depth) noexcept { containing_scope_offset[depth] = current_loc; write_tape(0, tape_type::START_OBJECT); return true; } really_inline bool document::parser::on_start_array(uint32_t depth) noexcept { containing_scope_offset[depth] = current_loc; write_tape(0, tape_type::START_ARRAY); return true; } // TODO we're not checking this bool really_inline bool document::parser::on_end_document(uint32_t depth) noexcept { // write our doc.tape location to the header scope // The root scope gets written *at* the previous location. annotate_previous_loc(containing_scope_offset[depth], current_loc); write_tape(containing_scope_offset[depth], tape_type::ROOT); return true; } really_inline bool document::parser::on_end_object(uint32_t depth) noexcept { // write our doc.tape location to the header scope write_tape(containing_scope_offset[depth], tape_type::END_OBJECT); annotate_previous_loc(containing_scope_offset[depth], current_loc); return true; } really_inline bool document::parser::on_end_array(uint32_t depth) noexcept { // write our doc.tape location to the header scope write_tape(containing_scope_offset[depth], tape_type::END_ARRAY); annotate_previous_loc(containing_scope_offset[depth], current_loc); return true; } really_inline bool document::parser::on_true_atom() noexcept { write_tape(0, tape_type::TRUE_VALUE); return true; } really_inline bool document::parser::on_false_atom() noexcept { write_tape(0, tape_type::FALSE_VALUE); return true; } really_inline bool document::parser::on_null_atom() noexcept { write_tape(0, tape_type::NULL_VALUE); return true; } really_inline uint8_t *document::parser::on_start_string() noexcept { /* we advance the point, accounting for the fact that we have a NULL * termination */ write_tape(current_string_buf_loc - doc.string_buf.get(), tape_type::STRING); return current_string_buf_loc + sizeof(uint32_t); } really_inline bool document::parser::on_end_string(uint8_t *dst) noexcept { uint32_t str_length = dst - (current_string_buf_loc + sizeof(uint32_t)); // TODO check for overflow in case someone has a crazy string (>=4GB?) // But only add the overflow check when the document itself exceeds 4GB // Currently unneeded because we refuse to parse docs larger or equal to 4GB. memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); // NULL termination is still handy if you expect all your strings to // be NULL terminated? It comes at a small cost *dst = 0; current_string_buf_loc = dst + 1; return true; } really_inline bool document::parser::on_number_s64(int64_t value) noexcept { write_tape(0, tape_type::INT64); std::memcpy(&doc.tape[current_loc], &value, sizeof(value)); ++current_loc; return true; } really_inline bool document::parser::on_number_u64(uint64_t value) noexcept { write_tape(0, tape_type::UINT64); doc.tape[current_loc++] = value; return true; } really_inline bool document::parser::on_number_double(double value) noexcept { write_tape(0, tape_type::DOUBLE); static_assert(sizeof(value) == sizeof(doc.tape[current_loc]), "mismatch size"); memcpy(&doc.tape[current_loc++], &value, sizeof(double)); // doc.tape[doc.current_loc++] = *((uint64_t *)&d); return true; } really_inline void document::parser::write_tape(uint64_t val, document::tape_type t) noexcept { doc.tape[current_loc++] = val | ((static_cast(static_cast(t))) << 56); } really_inline void document::parser::annotate_previous_loc(uint32_t saved_loc, uint64_t val) noexcept { doc.tape[saved_loc] |= val; } // // document::tape_ref inline implementation // really_inline document::tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {} really_inline document::tape_ref::tape_ref(const document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {} inline size_t document::tape_ref::after_element() const noexcept { switch (type()) { case tape_type::START_ARRAY: case tape_type::START_OBJECT: return tape_value(); case tape_type::UINT64: case tape_type::INT64: case tape_type::DOUBLE: return json_index + 2; default: return json_index + 1; } } really_inline document::tape_type document::tape_ref::type() const noexcept { return static_cast(doc->tape[json_index] >> 56); } really_inline uint64_t document::tape_ref::tape_value() const noexcept { return doc->tape[json_index] & JSON_VALUE_MASK; } template really_inline T document::tape_ref::next_tape_value() const noexcept { static_assert(sizeof(T) == sizeof(uint64_t)); return *reinterpret_cast(&doc->tape[json_index + 1]); } // // document::array inline implementation // really_inline document::array::array() noexcept : tape_ref() {} really_inline document::array::array(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) {} inline document::array::iterator document::array::begin() const noexcept { return iterator(doc, json_index + 1); } inline document::array::iterator document::array::end() const noexcept { return iterator(doc, after_element() - 1); } // // document::array::iterator inline implementation // really_inline document::array::iterator::iterator(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { } inline document::element document::array::iterator::operator*() const noexcept { return element(doc, json_index); } inline bool document::array::iterator::operator!=(const document::array::iterator& other) const noexcept { return json_index != other.json_index; } inline void document::array::iterator::operator++() noexcept { json_index = after_element(); } // // document::object inline implementation // really_inline document::object::object() noexcept : tape_ref() {} really_inline document::object::object(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { }; inline document::object::iterator document::object::begin() const noexcept { return iterator(doc, json_index + 1); } inline document::object::iterator document::object::end() const noexcept { return iterator(doc, after_element() - 1); } inline document::element_result document::object::operator[](const std::string_view &key) const noexcept { iterator end_field = end(); for (iterator field = begin(); field != end_field; ++field) { if (key == field.key()) { return field.value(); } } return NO_SUCH_FIELD; } inline document::element_result document::object::operator[](const char *key) const noexcept { iterator end_field = end(); for (iterator field = begin(); field != end_field; ++field) { if (!strcmp(key, field.key_c_str())) { return field.value(); } } return NO_SUCH_FIELD; } // // document::object::iterator inline implementation // really_inline document::object::iterator::iterator(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { } inline const document::key_value_pair document::object::iterator::operator*() const noexcept { return key_value_pair(key(), value()); } inline bool document::object::iterator::operator!=(const document::object::iterator& other) const noexcept { return json_index != other.json_index; } inline void document::object::iterator::operator++() noexcept { json_index++; json_index = after_element(); } inline std::string_view document::object::iterator::key() const noexcept { size_t string_buf_index = tape_value(); uint32_t len; memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len)); return std::string_view( reinterpret_cast(&doc->string_buf[string_buf_index + sizeof(uint32_t)]), len ); } inline const char* document::object::iterator::key_c_str() const noexcept { return reinterpret_cast(&doc->string_buf[tape_value() + sizeof(uint32_t)]); } inline document::element document::object::iterator::value() const noexcept { return element(doc, json_index + 1); } // // document::key_value_pair inline implementation // inline document::key_value_pair::key_value_pair(std::string_view _key, document::element _value) noexcept : key(_key), value(_value) {} // // document::element inline implementation // really_inline document::element::element() noexcept : tape_ref() {} really_inline document::element::element(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { } really_inline bool document::element::is_null() const noexcept { return type() == tape_type::NULL_VALUE; } really_inline bool document::element::is_bool() const noexcept { return type() == tape_type::TRUE_VALUE || type() == tape_type::FALSE_VALUE; } really_inline bool document::element::is_number() const noexcept { return type() == tape_type::UINT64 || type() == tape_type::INT64 || type() == tape_type::DOUBLE; } really_inline bool document::element::is_integer() const noexcept { return type() == tape_type::UINT64 || type() == tape_type::INT64; } really_inline bool document::element::is_string() const noexcept { return type() == tape_type::STRING; } really_inline bool document::element::is_array() const noexcept { return type() == tape_type::START_ARRAY; } really_inline bool document::element::is_object() const noexcept { return type() == tape_type::START_OBJECT; } inline document::element::operator bool() const noexcept(false) { return as_bool(); } inline document::element::operator const char*() const noexcept(false) { return as_c_str(); } inline document::element::operator std::string_view() const noexcept(false) { return as_string(); } inline document::element::operator uint64_t() const noexcept(false) { return as_uint64_t(); } inline document::element::operator int64_t() const noexcept(false) { return as_int64_t(); } inline document::element::operator double() const noexcept(false) { return as_double(); } inline document::element::operator document::array() const noexcept(false) { return as_array(); } inline document::element::operator document::object() const noexcept(false) { return as_object(); } inline document::element_result document::element::as_bool() const noexcept { switch (type()) { case tape_type::TRUE_VALUE: return true; case tape_type::FALSE_VALUE: return false; default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_c_str() const noexcept { switch (type()) { case tape_type::STRING: { size_t string_buf_index = tape_value(); return reinterpret_cast(&doc->string_buf[string_buf_index + sizeof(uint32_t)]); } default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_string() const noexcept { switch (type()) { case tape_type::STRING: { size_t string_buf_index = tape_value(); uint32_t len; memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len)); return std::string_view( reinterpret_cast(&doc->string_buf[string_buf_index + sizeof(uint32_t)]), len ); } default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_uint64_t() const noexcept { switch (type()) { case tape_type::UINT64: return next_tape_value(); case tape_type::INT64: { int64_t result = next_tape_value(); if (result < 0) { return NUMBER_OUT_OF_RANGE; } return static_cast(result); } default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_int64_t() const noexcept { switch (type()) { case tape_type::UINT64: { uint64_t result = next_tape_value(); // Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std if (result > (std::numeric_limits::max)()) { return NUMBER_OUT_OF_RANGE; } return static_cast(result); } case tape_type::INT64: return next_tape_value(); default: std::cout << "Incorrect " << json_index << " = " << char(type()) << std::endl; return INCORRECT_TYPE; } } inline document::element_result document::element::as_double() const noexcept { switch (type()) { case tape_type::UINT64: return next_tape_value(); case tape_type::INT64: { return next_tape_value(); int64_t result = tape_value(); if (result < 0) { return NUMBER_OUT_OF_RANGE; } return result; } case tape_type::DOUBLE: return next_tape_value(); default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_array() const noexcept { switch (type()) { case tape_type::START_ARRAY: return array(doc, json_index); default: return INCORRECT_TYPE; } } inline document::element_result document::element::as_object() const noexcept { switch (type()) { case tape_type::START_OBJECT: return object(doc, json_index); default: return INCORRECT_TYPE; } } inline document::element_result document::element::operator[](const std::string_view &key) const noexcept { auto [obj, error] = as_object(); if (error) { return error; } return obj[key]; } inline document::element_result document::element::operator[](const char *key) const noexcept { auto [obj, error] = as_object(); if (error) { return error; } return obj[key]; } } // namespace simdjson #endif // SIMDJSON_INLINE_DOCUMENT_H