#ifndef SIMDJSON_ELEMENT_INL_H #define SIMDJSON_ELEMENT_INL_H #include "simdjson/dom/base.h" #include "simdjson/dom/element.h" #include "simdjson/dom/document.h" #include "simdjson/dom/object.h" #include "simdjson/internal/tape_type.h" #include "simdjson/dom/object-inl.h" #include "simdjson/error-inl.h" #include "simdjson/jsonpathutil.h" #include #include namespace simdjson { // // simdjson_result inline implementation // simdjson_inline simdjson_result::simdjson_result() noexcept : internal::simdjson_result_base() {} simdjson_inline simdjson_result::simdjson_result(dom::element &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} inline simdjson_result simdjson_result::type() const noexcept { if (error()) { return error(); } return first.type(); } template simdjson_inline bool simdjson_result::is() const noexcept { return !error() && first.is(); } template simdjson_inline simdjson_result simdjson_result::get() const noexcept { if (error()) { return error(); } return first.get(); } template simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) const noexcept { if (error()) { return error(); } return first.get(value); } simdjson_inline simdjson_result simdjson_result::get_array() const noexcept { if (error()) { return error(); } return first.get_array(); } simdjson_inline simdjson_result simdjson_result::get_object() const noexcept { if (error()) { return error(); } return first.get_object(); } simdjson_inline simdjson_result simdjson_result::get_c_str() const noexcept { if (error()) { return error(); } return first.get_c_str(); } simdjson_inline simdjson_result simdjson_result::get_string_length() const noexcept { if (error()) { return error(); } return first.get_string_length(); } simdjson_inline simdjson_result simdjson_result::get_string() const noexcept { if (error()) { return error(); } return first.get_string(); } simdjson_inline simdjson_result simdjson_result::get_int64() const noexcept { if (error()) { return error(); } return first.get_int64(); } simdjson_inline simdjson_result simdjson_result::get_uint64() const noexcept { if (error()) { return error(); } return first.get_uint64(); } simdjson_inline simdjson_result simdjson_result::get_double() const noexcept { if (error()) { return error(); } return first.get_double(); } simdjson_inline simdjson_result simdjson_result::get_bool() const noexcept { if (error()) { return error(); } return first.get_bool(); } simdjson_inline bool simdjson_result::is_array() const noexcept { return !error() && first.is_array(); } simdjson_inline bool simdjson_result::is_object() const noexcept { return !error() && first.is_object(); } simdjson_inline bool simdjson_result::is_string() const noexcept { return !error() && first.is_string(); } simdjson_inline bool simdjson_result::is_int64() const noexcept { return !error() && first.is_int64(); } simdjson_inline bool simdjson_result::is_uint64() const noexcept { return !error() && first.is_uint64(); } simdjson_inline bool simdjson_result::is_double() const noexcept { return !error() && first.is_double(); } simdjson_inline bool simdjson_result::is_number() const noexcept { return !error() && first.is_number(); } simdjson_inline bool simdjson_result::is_bool() const noexcept { return !error() && first.is_bool(); } simdjson_inline bool simdjson_result::is_null() const noexcept { return !error() && first.is_null(); } simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { if (error()) { return error(); } return first[key]; } simdjson_inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { if (error()) { return error(); } return first[key]; } simdjson_inline simdjson_result simdjson_result::at_pointer(const std::string_view json_pointer) const noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } simdjson_inline simdjson_result simdjson_result::at_path(const std::string_view json_path) const noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } return at_pointer(json_pointer); } simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(const std::string_view json_path) const noexcept { if (error()) { return error(); } return first.at_path_with_wildcard(json_path); } #ifndef SIMDJSON_DISABLE_DEPRECATED_API [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] simdjson_inline simdjson_result simdjson_result::at(const std::string_view json_pointer) const noexcept { SIMDJSON_PUSH_DISABLE_WARNINGS SIMDJSON_DISABLE_DEPRECATED_WARNING if (error()) { return error(); } return first.at(json_pointer); SIMDJSON_POP_DISABLE_WARNINGS } #endif // SIMDJSON_DISABLE_DEPRECATED_API simdjson_inline simdjson_result simdjson_result::at(size_t index) const noexcept { if (error()) { return error(); } return first.at(index); } simdjson_inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { if (error()) { return error(); } return first.at_key(key); } simdjson_inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { if (error()) { return error(); } return first.at_key_case_insensitive(key); } #if SIMDJSON_EXCEPTIONS simdjson_inline simdjson_result::operator bool() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator const char *() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator std::string_view() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator uint64_t() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator int64_t() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator double() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator dom::array() const noexcept(false) { return get(); } simdjson_inline simdjson_result::operator dom::object() const noexcept(false) { return get(); } simdjson_inline dom::array::iterator simdjson_result::begin() const noexcept(false) { if (error()) { throw simdjson_error(error()); } return first.begin(); } simdjson_inline dom::array::iterator simdjson_result::end() const noexcept(false) { if (error()) { throw simdjson_error(error()); } return first.end(); } #endif // SIMDJSON_EXCEPTIONS namespace dom { // // element inline implementation // simdjson_inline element::element() noexcept : tape{} {} simdjson_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { } inline element_type element::type() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 auto tape_type = tape.tape_ref_type(); return tape_type == internal::tape_type::FALSE_VALUE ? element_type::BOOL : static_cast(tape_type); } inline simdjson_result element::get_bool() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 if(tape.is_true()) { return true; } else if(tape.is_false()) { return false; } return INCORRECT_TYPE; } inline simdjson_result element::get_c_str() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::STRING: { return tape.get_c_str(); } default: return INCORRECT_TYPE; } } inline simdjson_result element::get_string_length() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::STRING: { return tape.get_string_length(); } default: return INCORRECT_TYPE; } } inline simdjson_result element::get_string() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::STRING: return tape.get_string_view(); default: return INCORRECT_TYPE; } } inline simdjson_result element::get_uint64() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 if(simdjson_unlikely(!tape.is_uint64())) { // branch rarely taken if(tape.is_int64()) { int64_t result = tape.next_tape_value(); if (result < 0) { return NUMBER_OUT_OF_RANGE; } return uint64_t(result); } return INCORRECT_TYPE; } return tape.next_tape_value(); } inline simdjson_result element::get_int64() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 if(simdjson_unlikely(!tape.is_int64())) { // branch rarely taken if(tape.is_uint64()) { uint64_t result = tape.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 > uint64_t((std::numeric_limits::max)())) { return NUMBER_OUT_OF_RANGE; } return static_cast(result); } return INCORRECT_TYPE; } return tape.next_tape_value(); } inline simdjson_result element::get_double() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 // Performance considerations: // 1. Querying tape_ref_type() implies doing a shift, it is fast to just do a straight // comparison. // 2. Using a switch-case relies on the compiler guessing what kind of code generation // we want... But the compiler cannot know that we expect the type to be "double" // most of the time. // We can expect get to refer to a double type almost all the time. // It is important to craft the code accordingly so that the compiler can use this // information. (This could also be solved with profile-guided optimization.) if(simdjson_unlikely(!tape.is_double())) { // branch rarely taken if(tape.is_uint64()) { return double(tape.next_tape_value()); } else if(tape.is_int64()) { return double(tape.next_tape_value()); } return INCORRECT_TYPE; } // this is common: return tape.next_tape_value(); } inline simdjson_result element::get_array() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::START_ARRAY: return array(tape); default: return INCORRECT_TYPE; } } inline simdjson_result element::get_object() const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: return object(tape); default: return INCORRECT_TYPE; } } template simdjson_warn_unused simdjson_inline error_code element::get(T &value) const noexcept { return get().get(value); } // An element-specific version prevents recursion with simdjson_result::get(value) template<> simdjson_warn_unused simdjson_inline error_code element::get(element &value) const noexcept { value = element(tape); return SUCCESS; } template inline void element::tie(T &value, error_code &error) && noexcept { error = get(value); } template simdjson_inline bool element::is() const noexcept { auto result = get(); return !result.error(); } template<> inline simdjson_result element::get() const noexcept { return get_array(); } template<> inline simdjson_result element::get() const noexcept { return get_object(); } template<> inline simdjson_result element::get() const noexcept { return get_c_str(); } template<> inline simdjson_result element::get() const noexcept { return get_string(); } template<> inline simdjson_result element::get() const noexcept { return get_int64(); } template<> inline simdjson_result element::get() const noexcept { return get_uint64(); } template<> inline simdjson_result element::get() const noexcept { return get_double(); } template<> inline simdjson_result element::get() const noexcept { return get_bool(); } inline bool element::is_array() const noexcept { return is(); } inline bool element::is_object() const noexcept { return is(); } inline bool element::is_string() const noexcept { return is(); } inline bool element::is_int64() const noexcept { return is(); } inline bool element::is_uint64() const noexcept { return is(); } inline bool element::is_double() const noexcept { return is(); } inline bool element::is_bool() const noexcept { return is(); } inline bool element::is_number() const noexcept { return is_int64() || is_uint64() || is_double(); } inline bool element::is_null() const noexcept { return tape.is_null_on_tape(); } #if SIMDJSON_EXCEPTIONS inline element::operator bool() const noexcept(false) { return get(); } inline element::operator const char*() const noexcept(false) { return get(); } inline element::operator std::string_view() const noexcept(false) { return get(); } inline element::operator uint64_t() const noexcept(false) { return get(); } inline element::operator int64_t() const noexcept(false) { return get(); } inline element::operator double() const noexcept(false) { return get(); } inline element::operator array() const noexcept(false) { return get(); } inline element::operator object() const noexcept(false) { return get(); } inline array::iterator element::begin() const noexcept(false) { return get().begin(); } inline array::iterator element::end() const noexcept(false) { return get().end(); } #endif // SIMDJSON_EXCEPTIONS inline simdjson_result element::operator[](std::string_view key) const noexcept { return at_key(key); } inline simdjson_result element::operator[](const char *key) const noexcept { return at_key(key); } inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { if (simdjson_unlikely(json_pointer[0] != '/')) { return false; } size_t escape = json_pointer.find('~'); if (escape == std::string_view::npos) { return true; } if (escape == json_pointer.size() - 1) { return false; } if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { return false; } return true; } inline simdjson_result element::at_pointer(std::string_view json_pointer) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: return object(tape).at_pointer(json_pointer); case internal::tape_type::START_ARRAY: return array(tape).at_pointer(json_pointer); default: { if (!json_pointer.empty()) { // a non-empty string can be invalid, or accessing a primitive (issue 2154) if (is_pointer_well_formed(json_pointer)) { return NO_SUCH_FIELD; } return INVALID_JSON_POINTER; } // an empty string means that we return the current node dom::element copy(*this); return simdjson_result(std::move(copy)); } } } inline simdjson_result> element::at_path_with_wildcard(std::string_view json_path) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: return object(tape).at_path_with_wildcard(json_path); case internal::tape_type::START_ARRAY: return array(tape).at_path_with_wildcard(json_path); default: return std::vector{}; } } inline simdjson_result element::at_path(std::string_view json_path) const noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } return at_pointer(json_pointer); } #ifndef SIMDJSON_DISABLE_DEPRECATED_API [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] inline simdjson_result element::at(std::string_view json_pointer) const noexcept { // version 0.4 of simdjson allowed non-compliant pointers auto std_pointer = (json_pointer.empty() ? "" : "/") + std::string(json_pointer.begin(), json_pointer.end()); return at_pointer(std_pointer); } #endif // SIMDJSON_DISABLE_DEPRECATED_API inline simdjson_result element::at(size_t index) const noexcept { return get().at(index); } inline simdjson_result element::at_key(std::string_view key) const noexcept { return get().at_key(key); } inline simdjson_result element::at_key_case_insensitive(std::string_view key) const noexcept { return get().at_key_case_insensitive(key); } inline bool element::operator<(const element &other) const noexcept { return tape.json_index < other.tape.json_index; } inline bool element::operator==(const element &other) const noexcept { return tape.json_index == other.tape.json_index; } inline bool element::dump_raw_tape(std::ostream &out) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 return tape.doc->dump_raw_tape(out); } inline std::ostream& operator<<(std::ostream& out, element_type type) { switch (type) { case element_type::ARRAY: return out << "array"; case element_type::OBJECT: return out << "object"; case element_type::INT64: return out << "int64_t"; case element_type::UINT64: return out << "uint64_t"; case element_type::DOUBLE: return out << "double"; case element_type::STRING: return out << "string"; case element_type::BOOL: return out << "bool"; case element_type::NULL_VALUE: return out << "null"; default: return out << "unexpected content!!!"; // abort() usage is forbidden in the library } } } // namespace dom } // namespace simdjson #endif // SIMDJSON_ELEMENT_INL_H