#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H #ifndef SIMDJSON_CONDITIONAL_INCLUDE #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H #include "simdjson/jsonpathutil.h" #include "simdjson/generic/ondemand/base.h" #include "simdjson/generic/ondemand/array.h" #include "simdjson/generic/ondemand/array_iterator-inl.h" #include "simdjson/generic/ondemand/json_iterator.h" #include "simdjson/generic/ondemand/value.h" #include "simdjson/generic/ondemand/value_iterator-inl.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand { // // ### Live States // // While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is // always SUCCESS: // // - Start: This is the state when the array is first found and the iterator is just past the `{`. // In this state, at_start == true. // - Next: After we hand a scalar value to the user, or an array/object which they then fully // iterate over, the iterator is at the `,` before the next value (or `]`). In this state, // depth == iter->depth, at_start == false, and error == SUCCESS. // - Unfinished Business: When we hand an array/object to the user which they do not fully // iterate over, we need to finish that iteration by skipping child values until we reach the // Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS. // // ## Error States // // In error states, we will yield exactly one more value before stopping. iter->depth == depth // and at_start is always false. We decrement after yielding the error, moving to the Finished // state. // // - Chained Error: When the array iterator is part of an error chain--for example, in // `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an // array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and // iter->depth == depth, and at_start == false. We decrement depth when we yield the error. // - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements, // we flag that as an error and treat it exactly the same as a Chained Error. In this state, // error == TAPE_ERROR, iter->depth == depth, and at_start == false. // // ## Terminal State // // The terminal state has iter->depth < depth. at_start is always false. // // - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this // by decrementing depth. In this state, iter->depth < depth, at_start == false, and // error == SUCCESS. // simdjson_inline array::array(const value_iterator &_iter) noexcept : iter{_iter} { } simdjson_inline simdjson_result array::start(value_iterator &iter) noexcept { // We don't need to know if the array is empty to start iteration, but we do want to know if there // is an error--thus `simdjson_unused`. simdjson_unused bool has_value; SIMDJSON_TRY( iter.start_array().get(has_value) ); return array(iter); } simdjson_inline simdjson_result array::start_root(value_iterator &iter) noexcept { simdjson_unused bool has_value; SIMDJSON_TRY( iter.start_root_array().get(has_value) ); return array(iter); } simdjson_inline simdjson_result array::started(value_iterator &iter) noexcept { bool has_value; SIMDJSON_TRY(iter.started_array().get(has_value)); return array(iter); } simdjson_inline simdjson_result array::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif return array_iterator(iter); } simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; } simdjson_inline simdjson_result array::raw_json() noexcept { const uint8_t * starting_point{iter.peek_start()}; auto error = consume(); if(error) { return error; } // After 'consume()', we could be left pointing just beyond the document, but that // is ok because we are not going to dereference the final pointer position, we just // use it to compute the length in bytes. const uint8_t * final_point{iter._json_iter->unsafe_pointer()}; return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); } SIMDJSON_PUSH_DISABLE_WARNINGS SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING simdjson_inline simdjson_result array::count_elements() & noexcept { size_t count{0}; // Important: we do not consume any of the values. for(simdjson_unused auto v : *this) { count++; } // The above loop will always succeed, but we want to report errors. if(iter.error()) { return iter.error(); } // We need to move back at the start because we expect users to iterate through // the array after counting the number of elements. iter.reset_array(); return count; } SIMDJSON_POP_DISABLE_WARNINGS simdjson_inline simdjson_result array::is_empty() & noexcept { bool is_not_empty; auto error = iter.reset_array().get(is_not_empty); if(error) { return error; } return !is_not_empty; } inline simdjson_result array::reset() & noexcept { return iter.reset_array(); } inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; } json_pointer = json_pointer.substr(1); // - means "the append position" or "the element after the end of the array" // We don't support this, because we're returning a real element, not a position. if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; } // Read the array index size_t array_index = 0; size_t i; for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { uint8_t digit = uint8_t(json_pointer[i] - '0'); // Check for non-digit in array index. If it's there, we're trying to get a field in an object if (digit > 9) { return INCORRECT_TYPE; } array_index = array_index*10 + digit; } // 0 followed by other digits is invalid if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" // Empty string is invalid; so is a "/" with no digits before it if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" // Get the child auto child = at(array_index); // If there is an error, it ends here if(child.error()) { return child; } // If there is a /, we're not done yet, call recursively. if (i < json_pointer.length()) { child = child.at_pointer(json_pointer.substr(i)); } return child; } inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } return at_pointer(json_pointer); } inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { std::vector result; auto result_pair = get_next_key_and_json_path(json_path); std::string_view key = result_pair.first; std::string_view remaining_path = result_pair.second; // Wildcard case if(key=="*"){ for(auto element: *this){ if(element.error()){ return element.error(); } if(remaining_path.empty()){ // Use value_unsafe() because we've already checked for errors above. // The 'element' is a simdjson_result wrapper, and we need to extract // the underlying value. value_unsafe() is safe here because error() returned false. result.push_back(std::move(element).value_unsafe()); }else{ auto nested_result = element.at_path_with_wildcard(remaining_path); if(nested_result.error()){ return nested_result.error(); } // Same logic as above. std::vector nested_matches = std::move(nested_result).value_unsafe(); result.insert(result.end(), std::make_move_iterator(nested_matches.begin()), std::make_move_iterator(nested_matches.end())); } } return result; }else{ // Specific index case in which we access the element at the given index size_t idx=0; for(char c:key){ if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } idx = idx*10 + (c - '0'); } auto element = at(idx); if(element.error()){ return element.error(); } if(remaining_path.empty()){ result.push_back(std::move(element).value_unsafe()); return result; }else{ return element.at_path_with_wildcard(remaining_path); } } } simdjson_inline simdjson_result array::at(size_t index) noexcept { size_t i = 0; for (auto value : *this) { if (i == index) { return value; } i++; } return INDEX_OUT_OF_BOUNDS; } } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson namespace simdjson { simdjson_inline simdjson_result::simdjson_result( SIMDJSON_IMPLEMENTATION::ondemand::array &&value ) noexcept : implementation_simdjson_result_base( std::forward(value) ) { } simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : implementation_simdjson_result_base(error) { } simdjson_inline simdjson_result simdjson_result::begin() noexcept { if (error()) { return error(); } return first.begin(); } simdjson_inline simdjson_result simdjson_result::end() noexcept { if (error()) { return error(); } return first.end(); } simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { if (error()) { return error(); } return first.is_empty(); } simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { if (error()) { return error(); } return first.at_path(json_path); } simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { if (error()) { return error(); } return first.at_path_with_wildcard(json_path); } simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } return first.raw_json(); } } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H