mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
374de826ab
* This introduces a reset functionality. * Minor simplification. * Tweaking further. * This should fix the tests.
707 lines
29 KiB
C++
707 lines
29 KiB
C++
namespace simdjson {
|
|
namespace SIMDJSON_IMPLEMENTATION {
|
|
namespace ondemand {
|
|
|
|
simdjson_really_inline value_iterator::value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept
|
|
: _json_iter{json_iter},
|
|
_depth{depth},
|
|
_start_position{start_index}
|
|
{
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_object() noexcept {
|
|
const uint8_t *json;
|
|
SIMDJSON_TRY( advance_container_start("object", json) );
|
|
if (*json != '{') { return incorrect_type_error("Not an object"); }
|
|
return started_object();
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
|
|
bool result;
|
|
SIMDJSON_TRY( start_object().get(result) );
|
|
if (*_json_iter->peek_last() != '}') { return _json_iter->report_error(TAPE_ERROR, "object invalid: { at beginning of document unmatched by } at end of document"); }
|
|
return result;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object() noexcept {
|
|
assert_at_container_start();
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
_json_iter->set_start_position(_depth, _start_position);
|
|
#endif
|
|
if (*_json_iter->peek() == '}') {
|
|
logger::log_value(*_json_iter, "empty object");
|
|
_json_iter->advance();
|
|
_json_iter->ascend_to(depth()-1);
|
|
return false;
|
|
}
|
|
logger::log_start_value(*_json_iter, "object");
|
|
return true;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
|
|
assert_at_next();
|
|
|
|
switch (*_json_iter->advance()) {
|
|
case '}':
|
|
logger::log_end_value(*_json_iter, "object");
|
|
_json_iter->ascend_to(depth()-1);
|
|
return false;
|
|
case ',':
|
|
return true;
|
|
default:
|
|
return _json_iter->report_error(TAPE_ERROR, "Missing comma between object fields");
|
|
}
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
|
|
error_code error;
|
|
bool has_value;
|
|
//
|
|
// Initially, the object can be in one of a few different places:
|
|
//
|
|
// 1. The start of the object, at the first field:
|
|
//
|
|
// ```
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 2, index 1)
|
|
// ```
|
|
if (at_first_field()) {
|
|
has_value = true;
|
|
|
|
//
|
|
// 2. When a previous search did not yield a value or the object is empty:
|
|
//
|
|
// ```
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 0)
|
|
// { }
|
|
// ^ (depth 0, index 2)
|
|
// ```
|
|
//
|
|
} else if (!is_open()) {
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
// If we're past the end of the object, we're being iterated out of order.
|
|
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
|
// this object iterator will blithely scan that object for fields.
|
|
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
|
#endif
|
|
return false;
|
|
|
|
// 3. When a previous search found a field or an iterator yielded a value:
|
|
//
|
|
// ```
|
|
// // When a field was not fully consumed (or not even touched at all)
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 2)
|
|
// // When a field was fully consumed
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// // When the last field was fully consumed
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// ```
|
|
//
|
|
} else {
|
|
if ((error = skip_child() )) { abandon(); return error; }
|
|
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
|
|
#endif
|
|
}
|
|
while (has_value) {
|
|
// Get the key and colon, stopping at the value.
|
|
raw_json_string actual_key;
|
|
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
|
|
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
|
|
// The depth is left unchanged by field_key().
|
|
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
|
|
// field_value() will advance and check that we find a ':' separating the
|
|
// key and the value. It will also increment the depth by one.
|
|
if ((error = field_value() )) { abandon(); return error; }
|
|
// If it matches, stop and return
|
|
// We could do it this way if we wanted to allow arbitrary
|
|
// key content (including escaped quotes).
|
|
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
|
|
// Instead we do the following which may trigger buffer overruns if the
|
|
// user provides an adversarial key (containing a well placed unescaped quote
|
|
// character and being longer than the number of bytes remaining in the JSON
|
|
// input).
|
|
if (actual_key.unsafe_is_equal(key)) {
|
|
logger::log_event(*this, "match", key, -2);
|
|
// If we return here, then we return while pointing at the ':' that we just checked.
|
|
return true;
|
|
}
|
|
|
|
// No match: skip the value and see if , or } is next
|
|
logger::log_event(*this, "no match", key, -2);
|
|
// The call to skip_child is meant to skip over the value corresponding to the key.
|
|
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
|
|
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
|
|
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
|
|
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
|
|
// then we are in error and we abort.
|
|
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
|
}
|
|
|
|
// If the loop ended, we're out of fields to look at.
|
|
return false;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
|
|
/**
|
|
* When find_field_unordered_raw is called, we can either be pointing at the
|
|
* first key, pointing outside (at the closing brace) or if a key was matched
|
|
* we can be either pointing right afterthe ':' right before the value (that we need skip),
|
|
* or we may have consumed the value and we might be at a comma or at the
|
|
* final brace (ready for a call to has_next_field()).
|
|
*/
|
|
error_code error;
|
|
bool has_value;
|
|
|
|
// First, we scan from that point to the end.
|
|
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
|
|
token_position search_start = _json_iter->position();
|
|
|
|
// We want to know whether we need to go back to the beginning.
|
|
bool at_first = at_first_field();
|
|
///////////////
|
|
// Initially, the object can be in one of a few different places:
|
|
//
|
|
// 1. At the first key:
|
|
//
|
|
// ```
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 2, index 1)
|
|
// ```
|
|
//
|
|
if (at_first) {
|
|
has_value = true;
|
|
|
|
// 2. When a previous search did not yield a value or the object is empty:
|
|
//
|
|
// ```
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 0)
|
|
// { }
|
|
// ^ (depth 0, index 2)
|
|
// ```
|
|
//
|
|
} else if (!is_open()) {
|
|
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
// If we're past the end of the object, we're being iterated out of order.
|
|
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
|
// this object iterator will blithely scan that object for fields.
|
|
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
|
#endif
|
|
has_value = reset_object();
|
|
at_first = true;
|
|
// 3. When a previous search found a field or an iterator yielded a value:
|
|
//
|
|
// ```
|
|
// // When a field was not fully consumed (or not even touched at all)
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 2)
|
|
// // When a field was fully consumed
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// // When the last field was fully consumed
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// ```
|
|
//
|
|
} else {
|
|
// If someone queried a key but they did access the value, then we are left pointing
|
|
// at the ':' and we need to move forward through the value... If the value was
|
|
// processed then skip_child() does not move the iterator (but may adjust the depth).
|
|
if ((error = skip_child() )) { abandon(); return error; }
|
|
search_start = _json_iter->position();
|
|
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
|
|
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
|
|
// then we are in error and we abort.
|
|
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
|
|
#endif
|
|
}
|
|
|
|
// After initial processing, we will be in one of two states:
|
|
//
|
|
// ```
|
|
// // At the beginning of a field
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 1)
|
|
// // At the end of the object
|
|
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
|
|
// ^ (depth 0)
|
|
// ```
|
|
//
|
|
|
|
// Next, we find a match starting from the current position.
|
|
while (has_value) {
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
|
|
|
|
// Get the key and colon, stopping at the value.
|
|
raw_json_string actual_key;
|
|
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
|
|
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
|
|
// The depth is left unchanged by field_key().
|
|
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
|
|
// field_value() will advance and check that we find a ':' separating the
|
|
// key and the value. It will also increment the depth by one.
|
|
if ((error = field_value() )) { abandon(); return error; }
|
|
|
|
// If it matches, stop and return
|
|
// We could do it this way if we wanted to allow arbitrary
|
|
// key content (including escaped quotes).
|
|
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
|
|
// Instead we do the following which may trigger buffer overruns if the
|
|
// user provides an adversarial key (containing a well placed unescaped quote
|
|
// character and being longer than the number of bytes remaining in the JSON
|
|
// input).
|
|
if (actual_key.unsafe_is_equal(key)) {
|
|
logger::log_event(*this, "match", key, -2);
|
|
// If we return here, then we return while pointing at the ':' that we just checked.
|
|
return true;
|
|
}
|
|
|
|
// No match: skip the value and see if , or } is next
|
|
logger::log_event(*this, "no match", key, -2);
|
|
// The call to skip_child is meant to skip over the value corresponding to the key.
|
|
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
|
|
SIMDJSON_TRY( skip_child() );
|
|
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
|
|
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
|
|
// then we are in error and we abort.
|
|
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
|
}
|
|
// Performance note: it maybe wasteful to rewind to the beginning when there might be
|
|
// no other query following. Indeed, it would require reskipping the whole object.
|
|
// Instead, you can just stay where you are. If there is a new query, there is always time
|
|
// to rewind.
|
|
if(at_first) { return false; }
|
|
|
|
// If we reach the end without finding a match, search the rest of the fields starting at the
|
|
// beginning of the object.
|
|
// (We have already run through the object before, so we've already validated its structure. We
|
|
// don't check errors in this bit.)
|
|
has_value = reset_object();
|
|
while (true) {
|
|
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
|
|
|
|
// Get the key and colon, stopping at the value.
|
|
raw_json_string actual_key;
|
|
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
|
|
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
|
|
// The depth is left unchanged by field_key().
|
|
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
|
|
// field_value() will advance and check that we find a ':' separating the
|
|
// key and the value. It will also increment the depth by one.
|
|
error = field_value(); SIMDJSON_ASSUME(!error);
|
|
|
|
// If it matches, stop and return
|
|
// We could do it this way if we wanted to allow arbitrary
|
|
// key content (including escaped quotes).
|
|
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
|
|
// Instead we do the following which may trigger buffer overruns if the
|
|
// user provides an adversarial key (containing a well placed unescaped quote
|
|
// character and being longer than the number of bytes remaining in the JSON
|
|
// input).
|
|
if (actual_key.unsafe_is_equal(key)) {
|
|
logger::log_event(*this, "match", key, -2);
|
|
// If we return here, then we return while pointing at the ':' that we just checked.
|
|
return true;
|
|
}
|
|
|
|
// No match: skip the value and see if , or } is next
|
|
logger::log_event(*this, "no match", key, -2);
|
|
// The call to skip_child is meant to skip over the value corresponding to the key.
|
|
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
|
|
SIMDJSON_TRY( skip_child() );
|
|
// If we reached the end of the key-value pair we started from, then we know
|
|
// that the key is not there so we return false. We are either right before
|
|
// the next comma or the final brace.
|
|
if(_json_iter->position() == search_start) { return false; }
|
|
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
|
|
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
|
|
// then we are in error and we abort.
|
|
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
|
|
// If we make the mistake of exiting here, then we could be left pointing at a key
|
|
// in the middle of an object. That's not an allowable state.
|
|
}
|
|
// If the loop ended, we're out of fields to look at. The program should
|
|
// never reach this point.
|
|
return false;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
|
|
assert_at_next();
|
|
|
|
const uint8_t *key = _json_iter->advance();
|
|
if (*(key++) != '"') { return _json_iter->report_error(TAPE_ERROR, "Object key is not a string"); }
|
|
return raw_json_string(key);
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline error_code value_iterator::field_value() noexcept {
|
|
assert_at_next();
|
|
|
|
if (*_json_iter->advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
|
|
_json_iter->descend_to(depth()+1);
|
|
return SUCCESS;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_array() noexcept {
|
|
const uint8_t *json;
|
|
SIMDJSON_TRY( advance_container_start("array", json) );
|
|
if (*json != '[') { return incorrect_type_error("Not an array"); }
|
|
return started_array();
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
|
|
bool result;
|
|
SIMDJSON_TRY( start_array().get(result) );
|
|
if (*_json_iter->peek_last() != ']') { return _json_iter->report_error(TAPE_ERROR, "array invalid: [ at beginning of document unmatched by ] at end of document"); }
|
|
return result;
|
|
}
|
|
|
|
inline std::string value_iterator::to_string() const noexcept {
|
|
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
|
|
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
|
|
answer += std::string(" ]");
|
|
return answer;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array() noexcept {
|
|
assert_at_container_start();
|
|
if (*_json_iter->peek() == ']') {
|
|
logger::log_value(*_json_iter, "empty array");
|
|
_json_iter->advance();
|
|
_json_iter->ascend_to(depth()-1);
|
|
return false;
|
|
}
|
|
logger::log_start_value(*_json_iter, "array");
|
|
_json_iter->descend_to(depth()+1);
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
_json_iter->set_start_position(_depth, _start_position);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
|
|
assert_at_next();
|
|
|
|
switch (*_json_iter->advance()) {
|
|
case ']':
|
|
logger::log_end_value(*_json_iter, "array");
|
|
_json_iter->ascend_to(depth()-1);
|
|
return false;
|
|
case ',':
|
|
_json_iter->descend_to(depth()+1);
|
|
return true;
|
|
default:
|
|
return _json_iter->report_error(TAPE_ERROR, "Missing comma between array elements");
|
|
}
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
|
|
auto not_true = atomparsing::str4ncmp(json, "true");
|
|
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
|
|
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
|
|
if (error) { return incorrect_type_error("Not a boolean"); }
|
|
return simdjson_result<bool>(!not_true);
|
|
}
|
|
simdjson_really_inline bool value_iterator::parse_null(const uint8_t *json) const noexcept {
|
|
return !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> value_iterator::get_string() noexcept {
|
|
return get_raw_json_string().unescape(_json_iter->string_buf_loc());
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
|
|
auto json = advance_start("string");
|
|
if (*json != '"') { return incorrect_type_error("Not a string"); }
|
|
return raw_json_string(json+1);
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
|
|
return numberparsing::parse_unsigned(advance_non_root_scalar("uint64"));
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
|
|
return numberparsing::parse_integer(advance_non_root_scalar("int64"));
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_double() noexcept {
|
|
return numberparsing::parse_double(advance_non_root_scalar("double"));
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
|
|
return parse_bool(advance_non_root_scalar("bool"));
|
|
}
|
|
simdjson_really_inline bool value_iterator::is_null() noexcept {
|
|
return parse_null(advance_non_root_scalar("null"));
|
|
}
|
|
|
|
constexpr const uint32_t MAX_INT_LENGTH = 1024;
|
|
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> value_iterator::get_root_string() noexcept {
|
|
return get_string();
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string() noexcept {
|
|
return get_raw_json_string();
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::get_root_uint64() noexcept {
|
|
auto max_len = peek_start_length();
|
|
auto json = advance_root_scalar("uint64");
|
|
uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer
|
|
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, _start_position, depth(), "Root number more than 20 characters"); return NUMBER_ERROR; }
|
|
return numberparsing::parse_unsigned(tmpbuf);
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_root_int64() noexcept {
|
|
auto max_len = peek_start_length();
|
|
auto json = advance_root_scalar("int64");
|
|
uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer
|
|
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, _start_position, depth(), "Root number more than 20 characters"); return NUMBER_ERROR; }
|
|
return numberparsing::parse_integer(tmpbuf);
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_root_double() noexcept {
|
|
auto max_len = peek_start_length();
|
|
auto json = advance_root_scalar("double");
|
|
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest number: -0.<fraction>e-308.
|
|
uint8_t tmpbuf[1074+8+1];
|
|
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, _start_position, depth(), "Root number more than 1082 characters"); return NUMBER_ERROR; }
|
|
return numberparsing::parse_double(tmpbuf);
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_root_bool() noexcept {
|
|
auto max_len = peek_start_length();
|
|
auto json = advance_root_scalar("bool");
|
|
uint8_t tmpbuf[5+1];
|
|
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { return incorrect_type_error("Not a boolean"); }
|
|
return parse_bool(tmpbuf);
|
|
}
|
|
simdjson_really_inline bool value_iterator::is_root_null() noexcept {
|
|
auto max_len = peek_start_length();
|
|
auto json = advance_root_scalar("null");
|
|
return max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
|
|
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[5]));
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline error_code value_iterator::skip_child() noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index > _start_position );
|
|
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
|
|
|
|
return _json_iter->skip_child(depth());
|
|
}
|
|
|
|
simdjson_really_inline value_iterator value_iterator::child() const noexcept {
|
|
assert_at_child();
|
|
return { _json_iter, depth()+1, _json_iter->token.position() };
|
|
}
|
|
|
|
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
|
|
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
|
|
// marked non-inline.
|
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
|
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
|
simdjson_really_inline bool value_iterator::is_open() const noexcept {
|
|
return _json_iter->depth() >= depth();
|
|
}
|
|
SIMDJSON_POP_DISABLE_WARNINGS
|
|
|
|
simdjson_really_inline bool value_iterator::at_eof() const noexcept {
|
|
return _json_iter->at_eof();
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::at_start() const noexcept {
|
|
return _json_iter->token.index == _start_position;
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::at_first_field() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index > _start_position );
|
|
return _json_iter->token.index == _start_position + 1;
|
|
}
|
|
|
|
simdjson_really_inline void value_iterator::abandon() noexcept {
|
|
_json_iter->abandon();
|
|
}
|
|
|
|
simdjson_warn_unused simdjson_really_inline depth_t value_iterator::depth() const noexcept {
|
|
return _depth;
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline error_code value_iterator::error() const noexcept {
|
|
return _json_iter->error;
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
|
|
return _json_iter->string_buf_loc();
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline const json_iterator &value_iterator::json_iter() const noexcept {
|
|
return *_json_iter;
|
|
}
|
|
simdjson_warn_unused simdjson_really_inline json_iterator &value_iterator::json_iter() noexcept {
|
|
return *_json_iter;
|
|
}
|
|
|
|
simdjson_really_inline const uint8_t *value_iterator::peek_start() const noexcept {
|
|
return _json_iter->peek(_start_position);
|
|
}
|
|
simdjson_really_inline uint32_t value_iterator::peek_start_length() const noexcept {
|
|
return _json_iter->peek_length(_start_position);
|
|
}
|
|
|
|
simdjson_really_inline const uint8_t *value_iterator::advance_start(const char *type) const noexcept {
|
|
logger::log_value(*_json_iter, _start_position, depth(), type);
|
|
// If we're not at the position anymore, we don't want to advance the cursor.
|
|
if (!is_at_start()) { return peek_start(); }
|
|
|
|
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
|
|
assert_at_start();
|
|
auto result = _json_iter->advance();
|
|
_json_iter->ascend_to(depth()-1);
|
|
return result;
|
|
}
|
|
simdjson_really_inline error_code value_iterator::advance_container_start(const char *type, const uint8_t *&json) const noexcept {
|
|
logger::log_start_value(*_json_iter, _start_position, depth(), type);
|
|
|
|
// If we're not at the position anymore, we don't want to advance the cursor.
|
|
if (!is_at_start()) {
|
|
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
|
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
|
|
#endif
|
|
json = peek_start();
|
|
return SUCCESS;
|
|
}
|
|
|
|
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
|
|
assert_at_start();
|
|
json = _json_iter->advance();
|
|
return SUCCESS;
|
|
}
|
|
simdjson_really_inline const uint8_t *value_iterator::advance_root_scalar(const char *type) const noexcept {
|
|
logger::log_value(*_json_iter, _start_position, depth(), type);
|
|
if (!is_at_start()) { return peek_start(); }
|
|
|
|
assert_at_root();
|
|
auto result = _json_iter->advance();
|
|
_json_iter->ascend_to(depth()-1);
|
|
return result;
|
|
}
|
|
simdjson_really_inline const uint8_t *value_iterator::advance_non_root_scalar(const char *type) const noexcept {
|
|
logger::log_value(*_json_iter, _start_position, depth(), type);
|
|
if (!is_at_start()) { return peek_start(); }
|
|
|
|
assert_at_non_root_start();
|
|
auto result = _json_iter->advance();
|
|
_json_iter->ascend_to(depth()-1);
|
|
return result;
|
|
}
|
|
|
|
simdjson_really_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
|
|
logger::log_error(*_json_iter, _start_position, depth(), message);
|
|
return INCORRECT_TYPE;
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::is_at_start() const noexcept {
|
|
return _json_iter->token.index == _start_position;
|
|
}
|
|
simdjson_really_inline bool value_iterator::is_at_iterator_start() const noexcept {
|
|
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
|
|
auto delta = _json_iter->token.index - _start_position;
|
|
return delta == 1 || delta == 2;
|
|
}
|
|
|
|
inline void value_iterator::assert_at_start() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index == _start_position );
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
|
|
SIMDJSON_ASSUME( _depth > 0 );
|
|
}
|
|
|
|
inline void value_iterator::assert_at_container_start() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index == _start_position + 1 );
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
|
|
SIMDJSON_ASSUME( _depth > 0 );
|
|
}
|
|
|
|
inline void value_iterator::assert_at_next() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index > _start_position );
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
|
|
SIMDJSON_ASSUME( _depth > 0 );
|
|
}
|
|
|
|
simdjson_really_inline void value_iterator::move_at_start() noexcept {
|
|
_json_iter->_depth = _depth;
|
|
_json_iter->token.index = _start_position;
|
|
}
|
|
|
|
simdjson_really_inline void value_iterator::move_at_container_start() noexcept {
|
|
_json_iter->_depth = _depth;
|
|
_json_iter->token.index = _start_position + 1;
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::reset_array() noexcept {
|
|
move_at_container_start();
|
|
return started_array();
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::reset_object() noexcept {
|
|
move_at_container_start();
|
|
return started_object();
|
|
}
|
|
|
|
inline void value_iterator::assert_at_child() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter->token.index > _start_position );
|
|
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
|
|
SIMDJSON_ASSUME( _depth > 0 );
|
|
}
|
|
|
|
inline void value_iterator::assert_at_root() const noexcept {
|
|
assert_at_start();
|
|
SIMDJSON_ASSUME( _depth == 1 );
|
|
}
|
|
|
|
inline void value_iterator::assert_at_non_root_start() const noexcept {
|
|
assert_at_start();
|
|
SIMDJSON_ASSUME( _depth > 1 );
|
|
}
|
|
|
|
inline void value_iterator::assert_is_valid() const noexcept {
|
|
SIMDJSON_ASSUME( _json_iter != nullptr );
|
|
}
|
|
|
|
simdjson_really_inline bool value_iterator::is_valid() const noexcept {
|
|
return _json_iter != nullptr;
|
|
}
|
|
|
|
|
|
simdjson_really_inline simdjson_result<json_type> value_iterator::type() noexcept {
|
|
switch (*peek_start()) {
|
|
case '{':
|
|
return json_type::object;
|
|
case '[':
|
|
return json_type::array;
|
|
case '"':
|
|
return json_type::string;
|
|
case 'n':
|
|
return json_type::null;
|
|
case 't': case 'f':
|
|
return json_type::boolean;
|
|
case '-':
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
return json_type::number;
|
|
default:
|
|
return TAPE_ERROR;
|
|
}
|
|
}
|
|
|
|
} // namespace ondemand
|
|
} // namespace SIMDJSON_IMPLEMENTATION
|
|
} // namespace simdjson
|
|
|
|
namespace simdjson {
|
|
|
|
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>::simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept
|
|
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(value)) {}
|
|
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
|
|
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(error) {}
|
|
|
|
} // namespace simdjson
|