#ifndef SIMDJSON_INLINE_ELEMENT_H #define SIMDJSON_INLINE_ELEMENT_H #include "simdjson/dom/array.h" #include "simdjson/dom/element.h" #include "simdjson/dom/object.h" #include #include namespace simdjson { // // simdjson_result inline implementation // really_inline simdjson_result::simdjson_result() noexcept : internal::simdjson_result_base() {} really_inline simdjson_result::simdjson_result(dom::element &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} really_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 really_inline bool simdjson_result::is() const noexcept { return !error() && first.is(); } template really_inline simdjson_result simdjson_result::get() const noexcept { if (error()) { return error(); } return first.get(); } template WARN_UNUSED really_inline error_code simdjson_result::get(T &value) const noexcept { if (error()) { return error(); } return first.get(value); } really_inline simdjson_result simdjson_result::get_array() const noexcept { if (error()) { return error(); } return first.get_array(); } really_inline simdjson_result simdjson_result::get_object() const noexcept { if (error()) { return error(); } return first.get_object(); } really_inline simdjson_result simdjson_result::get_c_str() const noexcept { if (error()) { return error(); } return first.get_c_str(); } really_inline simdjson_result simdjson_result::get_string_length() const noexcept { if (error()) { return error(); } return first.get_string_length(); } really_inline simdjson_result simdjson_result::get_string() const noexcept { if (error()) { return error(); } return first.get_string(); } really_inline simdjson_result simdjson_result::get_int64() const noexcept { if (error()) { return error(); } return first.get_int64(); } really_inline simdjson_result simdjson_result::get_uint64() const noexcept { if (error()) { return error(); } return first.get_uint64(); } really_inline simdjson_result simdjson_result::get_double() const noexcept { if (error()) { return error(); } return first.get_double(); } really_inline simdjson_result simdjson_result::get_bool() const noexcept { if (error()) { return error(); } return first.get_bool(); } really_inline bool simdjson_result::is_array() const noexcept { return !error() && first.is_array(); } really_inline bool simdjson_result::is_object() const noexcept { return !error() && first.is_object(); } really_inline bool simdjson_result::is_string() const noexcept { return !error() && first.is_string(); } really_inline bool simdjson_result::is_int64() const noexcept { return !error() && first.is_int64(); } really_inline bool simdjson_result::is_uint64() const noexcept { return !error() && first.is_uint64(); } really_inline bool simdjson_result::is_double() const noexcept { return !error() && first.is_double(); } really_inline bool simdjson_result::is_bool() const noexcept { return !error() && first.is_bool(); } really_inline bool simdjson_result::is_null() const noexcept { return !error() && first.is_null(); } really_inline simdjson_result simdjson_result::operator[](const std::string_view &key) const noexcept { if (error()) { return error(); } return first[key]; } really_inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { if (error()) { return error(); } return first[key]; } really_inline simdjson_result simdjson_result::at(const std::string_view &json_pointer) const noexcept { if (error()) { return error(); } return first.at(json_pointer); } really_inline simdjson_result simdjson_result::at(size_t index) const noexcept { if (error()) { return error(); } return first.at(index); } really_inline simdjson_result simdjson_result::at_key(const std::string_view &key) const noexcept { if (error()) { return error(); } return first.at_key(key); } really_inline simdjson_result simdjson_result::at_key_case_insensitive(const std::string_view &key) const noexcept { if (error()) { return error(); } return first.at_key_case_insensitive(key); } #if SIMDJSON_EXCEPTIONS really_inline simdjson_result::operator bool() const noexcept(false) { return get(); } really_inline simdjson_result::operator const char *() const noexcept(false) { return get(); } really_inline simdjson_result::operator std::string_view() const noexcept(false) { return get(); } really_inline simdjson_result::operator uint64_t() const noexcept(false) { return get(); } really_inline simdjson_result::operator int64_t() const noexcept(false) { return get(); } really_inline simdjson_result::operator double() const noexcept(false) { return get(); } really_inline simdjson_result::operator dom::array() const noexcept(false) { return get(); } really_inline simdjson_result::operator dom::object() const noexcept(false) { return get(); } really_inline dom::array::iterator simdjson_result::begin() const noexcept(false) { if (error()) { throw simdjson_error(error()); } return first.begin(); } really_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 // really_inline element::element() noexcept : tape{} {} really_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { } inline element_type element::type() const noexcept { 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 { 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 { 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 { 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 { 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 { if(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 { if(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 { // 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(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 { 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 { switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: return object(tape); default: return INCORRECT_TYPE; } } template WARN_UNUSED really_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<> WARN_UNUSED really_inline error_code element::get(element &value) const noexcept { value = element(tape); return SUCCESS; } template really_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_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[](const 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 simdjson_result element::at(const std::string_view &json_pointer) const noexcept { switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: return object(tape).at(json_pointer); case internal::tape_type::START_ARRAY: return array(tape).at(json_pointer); default: return INCORRECT_TYPE; } } inline simdjson_result element::at(size_t index) const noexcept { return get().at(index); } inline simdjson_result element::at_key(const std::string_view &key) const noexcept { return get().at_key(key); } inline simdjson_result element::at_key_case_insensitive(const std::string_view &key) const noexcept { return get().at_key_case_insensitive(key); } inline bool element::dump_raw_tape(std::ostream &out) const noexcept { return tape.doc->dump_raw_tape(out); } inline std::ostream& operator<<(std::ostream& out, const element &value) { return out << minify(value); } 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 template<> inline std::ostream& minifier::print(std::ostream& out) { using tape_type=internal::tape_type; size_t depth = 0; constexpr size_t MAX_DEPTH = 16; bool is_object[MAX_DEPTH]; is_object[0] = false; bool after_value = false; internal::tape_ref iter(value.tape); do { // print commas after each value if (after_value) { out << ","; } // If we are in an object, print the next key and :, and skip to the next value. if (is_object[depth]) { out << '"' << internal::escape_json_string(iter.get_string_view()) << "\":"; iter.json_index++; } switch (iter.tape_ref_type()) { // Arrays case tape_type::START_ARRAY: { // If we're too deep, we need to recurse to go deeper. depth++; if (unlikely(depth >= MAX_DEPTH)) { out << minify(dom::array(iter)); iter.json_index = iter.matching_brace_index() - 1; // Jump to the ] depth--; break; } // Output start [ out << '['; iter.json_index++; // Handle empty [] (we don't want to come back around and print commas) if (iter.tape_ref_type() == tape_type::END_ARRAY) { out << ']'; depth--; break; } is_object[depth] = false; after_value = false; continue; } // Objects case tape_type::START_OBJECT: { // If we're too deep, we need to recurse to go deeper. depth++; if (unlikely(depth >= MAX_DEPTH)) { out << minify(dom::object(iter)); iter.json_index = iter.matching_brace_index() - 1; // Jump to the } depth--; break; } // Output start { out << '{'; iter.json_index++; // Handle empty {} (we don't want to come back around and print commas) if (iter.tape_ref_type() == tape_type::END_OBJECT) { out << '}'; depth--; break; } is_object[depth] = true; after_value = false; continue; } // Scalars case tape_type::STRING: out << '"' << internal::escape_json_string(iter.get_string_view()) << '"'; break; case tape_type::INT64: out << iter.next_tape_value(); iter.json_index++; // numbers take up 2 spots, so we need to increment extra break; case tape_type::UINT64: out << iter.next_tape_value(); iter.json_index++; // numbers take up 2 spots, so we need to increment extra break; case tape_type::DOUBLE: out << iter.next_tape_value(); iter.json_index++; // numbers take up 2 spots, so we need to increment extra break; case tape_type::TRUE_VALUE: out << "true"; break; case tape_type::FALSE_VALUE: out << "false"; break; case tape_type::NULL_VALUE: out << "null"; break; // These are impossible case tape_type::END_ARRAY: case tape_type::END_OBJECT: case tape_type::ROOT: out << "unexpected content!!!"; // abort() usage is forbidden in the library } iter.json_index++; after_value = true; // Handle multiple ends in a row while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || iter.tape_ref_type() == tape_type::END_OBJECT)) { out << char(iter.tape_ref_type()); depth--; iter.json_index++; } // Stop when we're at depth 0 } while (depth != 0); return out; } #if SIMDJSON_EXCEPTIONS template<> really_inline std::ostream& minifier>::print(std::ostream& out) { if (value.error()) { throw simdjson_error(value.error()); } return out << minify(value.first); } really_inline std::ostream& operator<<(std::ostream& out, const simdjson_result &value) noexcept(false) { return out << minify>(value); } #endif } // namespace simdjson #endif // SIMDJSON_INLINE_ELEMENT_H