Isolated jkeiser fix for issue 1632: make it so that INCORRECT_TYPE is a recoverable condition in On Demand (#1663)

This commit is contained in:
Daniel Lemire 2021-07-23 11:32:26 -04:00 committed by GitHub
parent 01645cbd69
commit 47a62db559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1738 additions and 856 deletions

View File

@ -11,7 +11,7 @@ executors:
environment:
CXX: g++-8
CC: gcc-8
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc9:
@ -20,7 +20,7 @@ executors:
environment:
CXX: g++-9
CC: gcc-9
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc10:
@ -29,7 +29,7 @@ executors:
environment:
CXX: g++-10
CC: gcc-10
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang10:
@ -38,7 +38,7 @@ executors:
environment:
CXX: clang++-10
CC: clang-10
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang9:
@ -47,7 +47,7 @@ executors:
environment:
CXX: clang++-9
CC: clang-9
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang6:
@ -56,7 +56,7 @@ executors:
environment:
CXX: clang++-6.0
CC: clang-6.0
BUILD_FLAGS:
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
# Reusable test commands (and initializer for clang 6)
@ -191,7 +191,7 @@ jobs:
sanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
@ -201,13 +201,21 @@ jobs:
threadsanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
nocheckeof-clang10:
description: Validate that when __SIMDJSON_CHECK_EOF=0, everything still succeeds
environment:
CXXFLAGS: -D__SIMDJSON_CHECK_EOF=0
CMAKE_BUILD_FLAGS: --target ondemand_tests
CTEST_FLAGS: --output-on-failure -R ondemand_
executor: clang10
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake dynamic build
@ -262,7 +270,7 @@ jobs:
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build

View File

@ -19,6 +19,8 @@ char *to_chars(char *first, const char *last, double value);
* Defined in src/from_chars
*/
double from_chars(const char *first) noexcept;
double from_chars(const char *first, const char* end) noexcept;
}
#ifndef SIMDJSON_EXCEPTIONS
@ -251,6 +253,12 @@ namespace std {
#endif
#endif
// Feature flag for partially-implemented "don't require padding" feature
// TODO remove once feature complete.
#ifndef __SIMDJSON_CHECK_EOF
# define __SIMDJSON_CHECK_EOF 1
#endif
#if SIMDJSON_CPLUSPLUS17
// if we have C++, then fallthrough is a default attribute
# define simdjson_fallthrough [[fallthrough]]

View File

@ -37,6 +37,7 @@ enum error_code {
PARSER_IN_USE, ///< parser is already in use.
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order
INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it.
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early.
NUM_ERROR_CODES
};

View File

@ -305,6 +305,20 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
@ -709,6 +723,56 @@ simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to 9,223,372,036,854,775,808 (INT64_MAX).
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
@ -751,6 +815,50 @@ simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(co
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + negative;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
@ -828,6 +936,90 @@ simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(cons
}
return d;
}
// Never read at src_end or beyond
simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += negative;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src-negative, src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
} //namespace {}
#endif // SIMDJSON_SKIPNUMBERPARSING

View File

@ -57,8 +57,9 @@ simdjson_really_inline simdjson_result<array> array::start_root(value_iterator &
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_really_inline array array::started(value_iterator &iter) noexcept {
simdjson_unused bool has_value = iter.started_array();
simdjson_really_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
@ -175,6 +176,10 @@ simdjson_really_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEME
if (error()) { return error(); }
return first.count_elements();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);

View File

@ -110,7 +110,7 @@ protected:
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_really_inline array started(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
@ -160,6 +160,7 @@ public:
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> begin() noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> end() noexcept;
simdjson_really_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
};

View File

@ -21,13 +21,37 @@ inline std::string document::to_debug_string() noexcept {
}
simdjson_really_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_checkpoint());
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_really_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_really_inline value document::resume_value() noexcept {
return resume_value_iterator();
simdjson_really_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_really_inline simdjson_result<value> document::get_value_unsafe() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
switch (*iter.peek()) {
case '[': {
array result;
SIMDJSON_TRY( get_array().get(result) );
return value(result.iter);
}
case '{': {
object result;
SIMDJSON_TRY( get_object().get(result) );
return value(result.iter);
}
default:
// TODO it is still wrong to convert this to a value! get_root_bool / etc. will not be
// called if you do this.
return value(get_root_value_iterator());
}
}
simdjson_really_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
@ -107,22 +131,22 @@ simdjson_really_inline simdjson_result<array_iterator> document::end() & noexcep
}
simdjson_really_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return resume_value().find_field(key);
return start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return resume_value().find_field(key);
return start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return resume_value().find_field_unordered(key);
return start_or_resume_object().find_field_unordered(key);
}
simdjson_really_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return resume_value().find_field_unordered(key);
return start_or_resume_object().find_field_unordered(key);
}
simdjson_really_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return resume_value()[key];
return start_or_resume_object()[key];
}
simdjson_really_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return resume_value()[key];
return start_or_resume_object()[key];
}
simdjson_really_inline error_code document::consume() noexcept {
@ -153,7 +177,7 @@ simdjson_really_inline simdjson_result<std::string_view> document::raw_json_toke
simdjson_really_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->resume_value();
return this->get_value_unsafe();
}
json_type t;
SIMDJSON_TRY(type().get(t));

View File

@ -369,7 +369,8 @@ protected:
simdjson_really_inline value_iterator resume_value_iterator() noexcept;
simdjson_really_inline value_iterator get_root_value_iterator() noexcept;
simdjson_really_inline value resume_value() noexcept;
simdjson_really_inline simdjson_result<value> get_value_unsafe() noexcept;
simdjson_really_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_really_inline document start(ondemand::json_iterator &&iter) noexcept;
//

View File

@ -26,7 +26,7 @@ simdjson_really_inline json_iterator &json_iterator::operator=(json_iterator &&o
}
simdjson_really_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, _parser->implementation->structural_indexes.get()),
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
@ -35,10 +35,11 @@ simdjson_really_inline json_iterator::json_iterator(const uint8_t *buf, ondemand
{
logger::log_headers();
assert_more_tokens();
}
inline void json_iterator::rewind() noexcept {
token.index = _root;
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
@ -83,6 +84,10 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if __SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // __SIMDJSON_CHECK_EOF
break;
/*case '"':
if(*peek() == ':') {
@ -107,8 +112,7 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
auto end = &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
while (token.index <= end) {
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
@ -136,27 +140,43 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_really_inline bool json_iterator::at_root() const noexcept {
return token.position() == root_checkpoint();
return position() == root_position();
}
simdjson_really_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
simdjson_really_inline token_position json_iterator::root_checkpoint() const noexcept {
simdjson_really_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_really_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
// Visual Studio Clang treats unique_ptr.get() as "side effecting."
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( token.index == _root );
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_really_inline bool json_iterator::at_eof() const noexcept {
return token.index == &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
simdjson_really_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_really_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_really_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_really_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
@ -179,26 +199,45 @@ simdjson_really_inline void json_iterator::abandon() noexcept {
}
simdjson_really_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
// The following assert_more_tokens is currently disabled because rely on end-of-file buffering.
// assert_more_tokens();
// This is almost surely related to __SIMDJSON_CHECK_EOF but given that __SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.return_current_and_advance();
}
simdjson_really_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
// The following assert_more_tokens is currently disabled because rely on end-of-file buffering.
// assert_more_tokens(delta+1);
// This is almost surely related to __SIMDJSON_CHECK_EOF but given that __SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(delta);
}
simdjson_really_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
assert_more_tokens(delta+1);
return token.peek_length(delta);
}
simdjson_really_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to __SIMDJSON_CHECK_EOF but given that __SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_really_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to __SIMDJSON_CHECK_EOF but given that __SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek_length(position);
}
simdjson_really_inline token_position json_iterator::last_document_position() const noexcept {
simdjson_really_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
@ -207,7 +246,7 @@ simdjson_really_inline token_position json_iterator::last_document_position() co
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_really_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_document_position());
return token.peek(last_position());
}
simdjson_really_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
@ -240,6 +279,7 @@ simdjson_really_inline error_code json_iterator::report_error(error_code _error,
simdjson_really_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_really_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
@ -253,9 +293,11 @@ simdjson_really_inline void json_iterator::reenter_child(token_position position
}
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
simdjson_really_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
return parser->start_positions[depth];
}
simdjson_really_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
parser->start_positions[depth] = position;
}
@ -271,9 +313,11 @@ simdjson_really_inline error_code json_iterator::optional_error(error_code _erro
template<int N>
simdjson_warn_unused simdjson_really_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept {
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Truncate whitespace to fit the buffer.
if (max_len > N-1) {
if (jsoncharutils::is_not_structural_or_whitespace(json[N-1])) { return false; }
// if (jsoncharutils::is_not_structural_or_whitespace(json[N-1])) { return false; }
max_len = N-1;
}
@ -283,20 +327,6 @@ simdjson_warn_unused simdjson_really_inline bool json_iterator::copy_to_buffer(c
return true;
}
template<int N>
simdjson_warn_unused simdjson_really_inline bool json_iterator::peek_to_buffer(uint8_t (&tmpbuf)[N]) noexcept {
auto max_len = token.peek_length();
auto json = token.peek();
return copy_to_buffer(json, max_len, tmpbuf);
}
template<int N>
simdjson_warn_unused simdjson_really_inline bool json_iterator::advance_to_buffer(uint8_t (&tmpbuf)[N]) noexcept {
auto max_len = peek_length();
auto json = return_current_and_advance();
return copy_to_buffer(json, max_len, tmpbuf);
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -87,7 +87,7 @@ public:
/**
* Get the root value iterator
*/
simdjson_really_inline token_position root_checkpoint() const noexcept;
simdjson_really_inline token_position root_position() const noexcept;
/**
* Assert if the iterator is not at the start
@ -97,7 +97,7 @@ public:
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_really_inline bool at_eof() const noexcept;
simdjson_really_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
@ -110,10 +110,22 @@ public:
simdjson_really_inline void abandon() noexcept;
/**
* Advance the current token.
* Advance the current token without modifying depth.
*/
simdjson_really_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_really_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_really_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
@ -207,8 +219,6 @@ public:
simdjson_really_inline error_code optional_error(error_code error, const char *message) noexcept;
template<int N> simdjson_warn_unused simdjson_really_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept;
template<int N> simdjson_warn_unused simdjson_really_inline bool peek_to_buffer(uint8_t (&tmpbuf)[N]) noexcept;
template<int N> simdjson_warn_unused simdjson_really_inline bool advance_to_buffer(uint8_t (&tmpbuf)[N]) noexcept;
simdjson_really_inline token_position position() const noexcept;
simdjson_really_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
@ -225,7 +235,12 @@ public:
inline void rewind() noexcept;
protected:
simdjson_really_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
simdjson_really_inline token_position last_document_position() const noexcept;
/// The last token before the end
simdjson_really_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_really_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_really_inline token_position end() const noexcept;
friend class document;
friend class document_stream;

View File

@ -122,7 +122,7 @@ inline void log_headers() noexcept {
}
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, iter.token.index+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail);
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail);
}
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept {
if (LOG_ENABLED) {

View File

@ -34,15 +34,11 @@ simdjson_really_inline simdjson_result<value> object::find_field(const std::stri
}
simdjson_really_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
// We don't need to know if the object 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_object().get(has_value) );
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_really_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_object().get(has_value) );
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_really_inline error_code object::consume() noexcept {
@ -79,10 +75,11 @@ simdjson_really_inline simdjson_result<std::string_view> object::raw_json() noex
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_really_inline object object::started(value_iterator &iter) noexcept {
simdjson_unused bool has_value = iter.started_object();
return iter;
simdjson_really_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_really_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}

View File

@ -124,7 +124,7 @@ protected:
simdjson_really_inline error_code consume() noexcept;
static simdjson_really_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_really_inline object started(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_really_inline object resume(const value_iterator &iter) noexcept;
simdjson_really_inline object(const value_iterator &iter) noexcept;

View File

@ -2,18 +2,20 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
simdjson_really_inline token_iterator::token_iterator(const uint8_t *_buf, token_position _index) noexcept
: buf{_buf}, index{_index}
simdjson_really_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_really_inline uint32_t token_iterator::current_offset() const noexcept {
return *(index);
return *(_position);
}
simdjson_really_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(index++)];
return &buf[*(_position++)];
}
simdjson_really_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
@ -27,39 +29,39 @@ simdjson_really_inline uint32_t token_iterator::peek_length(token_position posit
}
simdjson_really_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(index+delta)];
return &buf[*(_position+delta)];
}
simdjson_really_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(index+delta);
return *(_position+delta);
}
simdjson_really_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(index+delta+1) - *(index+delta);
return *(_position+delta+1) - *(_position+delta);
}
simdjson_really_inline token_position token_iterator::position() const noexcept {
return index;
return _position;
}
simdjson_really_inline void token_iterator::set_position(token_position target_checkpoint) noexcept {
index = target_checkpoint;
simdjson_really_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_really_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return index == other.index;
return _position == other._position;
}
simdjson_really_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return index != other.index;
return _position != other._position;
}
simdjson_really_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return index > other.index;
return _position > other._position;
}
simdjson_really_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return index >= other.index;
return _position >= other._position;
}
simdjson_really_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return index < other.index;
return _position < other._position;
}
simdjson_really_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return index <= other.index;
return _position <= other._position;
}
} // namespace ondemand

View File

@ -23,8 +23,6 @@ public:
/**
* Advance to the next token (returning the current one).
*
* Does not check or update depth/expect_value. Caller is responsible for that.
*/
simdjson_really_inline const uint8_t *return_current_and_advance() noexcept;
/**
@ -60,8 +58,6 @@ public:
*
* @param position The position of the token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
*/
simdjson_really_inline const uint8_t *peek(token_position position) const noexcept;
/**
@ -74,13 +70,13 @@ public:
simdjson_really_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Save the current index to be restored later.
* Return the current index.
*/
simdjson_really_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_really_inline void set_position(token_position target_checkpoint) noexcept;
simdjson_really_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
@ -93,7 +89,7 @@ public:
simdjson_really_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_really_inline token_iterator(const uint8_t *buf, token_position index) noexcept;
simdjson_really_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
@ -115,7 +111,7 @@ protected:
simdjson_really_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position index{};
token_position _position{};
friend class json_iterator;
friend class value_iterator;

View File

@ -2,57 +2,72 @@ 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_really_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
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"); }
SIMDJSON_TRY( start_container('{', "Not an object", "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->streaming() ) {
// For document streams, we do not know the "last" structural of the current document, so peek_last() is nonesense.
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_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object() noexcept {
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, _start_position);
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
end_container();
return false;
}
logger::log_start_value(*_json_iter, "object");
return true;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if (! _json_iter->streaming() && (*_json_iter->peek_last() != '}')) {
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
return started_object();
}
simdjson_warn_unused simdjson_really_inline error_code value_iterator::end_container() noexcept {
#if __SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // __SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
_json_iter->ascend_to(depth()-1);
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return _json_iter->report_error(TAPE_ERROR, "Missing comma between object fields");
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
@ -108,7 +123,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
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; }
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
@ -197,7 +212,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// 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();
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
@ -214,17 +229,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else {
// If someone queried a key but they did access the value, then we are left pointing
// If someone queried a key but they not 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; }
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
@ -241,7 +253,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ^ (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
@ -290,7 +301,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// 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();
SIMDJSON_TRY(reset_object().get(has_value));
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
@ -344,33 +355,26 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return _json_iter->report_error(TAPE_ERROR, "Object key is not a string"); }
if (*(key++) != '"') { return 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->return_current_and_advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
if (*_json_iter->return_current_and_advance() != ':') { return 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"); }
SIMDJSON_TRY( start_container('[', "Not an array", "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->streaming() ) {
// For document streams, we do not know the "last" structural of the current document, so peek_last() is nonesense.
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;
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
@ -380,35 +384,46 @@ inline std::string value_iterator::to_string() const noexcept {
return answer;
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array() noexcept {
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
SIMDJSON_TRY( end_container() );
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);
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() && (*_json_iter->peek_last() != ']')) {
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
return started_array();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
_json_iter->ascend_to(depth()-1);
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return _json_iter->report_error(TAPE_ERROR, "Missing comma between array elements");
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
@ -427,24 +442,35 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> va
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");
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
advance_scalar("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"));
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
return numberparsing::parse_integer(advance_non_root_scalar("int64"));
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_double() noexcept {
return numberparsing::parse_double(advance_non_root_scalar("double"));
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
return parse_bool(advance_non_root_scalar("bool"));
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_really_inline bool value_iterator::is_null() noexcept {
return parse_null(advance_non_root_scalar("null"));
auto result = parse_null(peek_non_root_scalar("null"));
if(result) { advance_non_root_scalar("null"); }
return result;
}
constexpr const uint32_t MAX_INT_LENGTH = 1024;
@ -457,42 +483,63 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
}
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");
auto json = peek_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);
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;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() != INCORRECT_TYPE) { advance_root_scalar("uint64"); }
return result;
}
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");
auto json = peek_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);
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;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() != INCORRECT_TYPE) { advance_root_scalar("int64"); }
return result;
}
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.
auto json = peek_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);
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;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() != INCORRECT_TYPE) { advance_root_scalar("double"); }
return result;
}
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");
auto json = peek_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"); }
advance_root_scalar("bool");
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]));
auto json = peek_root_scalar("null");
auto result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(result) { advance_root_scalar("null"); }
return result;
}
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->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
@ -513,17 +560,17 @@ simdjson_really_inline bool value_iterator::is_open() const noexcept {
}
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_end() const noexcept {
return _json_iter->at_end();
}
simdjson_really_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.index == _start_position;
return _json_iter->token.position() == 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_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_really_inline void value_iterator::abandon() noexcept {
@ -547,66 +594,99 @@ simdjson_warn_unused simdjson_really_inline json_iterator &value_iterator::json_
}
simdjson_really_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(_start_position);
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);
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);
simdjson_really_inline const uint8_t *value_iterator::peek_scalar(const char *type) 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->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
return result;
return _json_iter->peek();
}
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);
simdjson_really_inline void value_iterator::advance_scalar(const char *type) 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; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_really_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) 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.
const uint8_t *json;
if (!is_at_start()) {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
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->return_current_and_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);
simdjson_really_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
auto result = _json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
return result;
return _json_iter->peek();
}
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);
simdjson_really_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) 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->return_current_and_advance();
return _json_iter->peek();
}
simdjson_really_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_really_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_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);
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;
return position() == start_position();
}
simdjson_really_inline bool value_iterator::is_at_key() const noexcept {
@ -618,50 +698,50 @@ simdjson_really_inline bool value_iterator::is_at_key() const noexcept {
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;
auto delta = position() - 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->token._position == _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->token._position == _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->token._position > _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;
_json_iter->token.set_position(_start_position);
}
simdjson_really_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.index = _start_position + 1;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_really_inline bool value_iterator::reset_array() noexcept {
simdjson_really_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
move_at_container_start();
return started_array();
}
simdjson_really_inline bool value_iterator::reset_object() noexcept {
simdjson_really_inline simdjson_result<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->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
@ -684,8 +764,7 @@ 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 {
simdjson_really_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
@ -706,6 +785,26 @@ simdjson_really_inline simdjson_result<json_type> value_iterator::type() noexcep
}
}
simdjson_really_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_really_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_really_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_really_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_really_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -46,7 +46,7 @@ public:
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_really_inline bool at_eof() const noexcept;
simdjson_really_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
@ -83,7 +83,7 @@ public:
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
simdjson_really_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
@ -113,11 +113,23 @@ public:
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator.
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_really_inline bool started_object() noexcept;
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
@ -127,6 +139,7 @@ public:
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> has_next_field() noexcept;
@ -223,13 +236,25 @@ public:
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Start an array iteration after the user has already checked and moved past the [.
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator.
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_really_inline bool started_array() noexcept;
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
@ -285,12 +310,12 @@ protected:
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_really_inline bool reset_array() noexcept;
simdjson_really_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_really_inline bool reset_object() noexcept;
simdjson_really_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
@ -315,12 +340,58 @@ protected:
simdjson_really_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_really_inline const uint8_t *peek_start() const noexcept;
simdjson_really_inline uint32_t peek_start_length() const noexcept;
simdjson_really_inline const uint8_t *advance_start(const char *type) const noexcept;
simdjson_really_inline error_code advance_container_start(const char *type, const uint8_t *&json) const noexcept;
simdjson_really_inline const uint8_t *advance_root_scalar(const char *type) const noexcept;
simdjson_really_inline const uint8_t *advance_non_root_scalar(const char *type) const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_really_inline void advance_scalar(const char *type) noexcept;
simdjson_really_inline void advance_root_scalar(const char *type) noexcept;
simdjson_really_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_really_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_really_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_really_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_really_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_really_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_really_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_really_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_really_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_really_inline bool is_at_start() const noexcept;
/**
@ -347,6 +418,18 @@ protected:
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_really_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_really_inline token_position position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_really_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_really_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_really_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class array;

View File

@ -134,6 +134,86 @@ decimal parse_decimal(const char *&p) noexcept {
return answer;
}
// This should always succeed since it follows a call to parse_number.
// Will not read at or beyond the "end" pointer.
decimal parse_decimal(const char *&p, const char * end) noexcept {
decimal answer;
answer.num_digits = 0;
answer.decimal_point = 0;
answer.truncated = false;
if(p == end) { return answer; } // should never happen
answer.negative = (*p == '-');
if ((*p == '-') || (*p == '+')) {
++p;
}
while ((p != end) && (*p == '0')) {
++p;
}
while ((p != end) && is_integer(*p)) {
if (answer.num_digits < max_digits) {
answer.digits[answer.num_digits] = uint8_t(*p - '0');
}
answer.num_digits++;
++p;
}
if ((p != end) && (*p == '.')) {
++p;
if(p == end) { return answer; } // should never happen
const char *first_after_period = p;
// if we have not yet encountered a zero, we have to skip it as well
if (answer.num_digits == 0) {
// skip zeros
while (*p == '0') {
++p;
}
}
while ((p != end) && is_integer(*p)) {
if (answer.num_digits < max_digits) {
answer.digits[answer.num_digits] = uint8_t(*p - '0');
}
answer.num_digits++;
++p;
}
answer.decimal_point = int32_t(first_after_period - p);
}
if(answer.num_digits > 0) {
const char *preverse = p - 1;
int32_t trailing_zeros = 0;
while ((*preverse == '0') || (*preverse == '.')) {
if(*preverse == '0') { trailing_zeros++; };
--preverse;
}
answer.decimal_point += int32_t(answer.num_digits);
answer.num_digits -= uint32_t(trailing_zeros);
}
if(answer.num_digits > max_digits ) {
answer.num_digits = max_digits;
answer.truncated = true;
}
if ((p != end) && (('e' == *p) || ('E' == *p))) {
++p;
if(p == end) { return answer; } // should never happen
bool neg_exp = false;
if ('-' == *p) {
neg_exp = true;
++p;
} else if ('+' == *p) {
++p;
}
int32_t exp_number = 0; // exponential part
while ((p != end) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - '0');
if (exp_number < 0x10000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
answer.decimal_point += (neg_exp ? -exp_number : exp_number);
}
return answer;
}
namespace {
// remove all final zeroes
@ -473,6 +553,12 @@ adjusted_mantissa parse_long_mantissa(const char *first) {
return compute_float<binary>(d);
}
template <typename binary>
adjusted_mantissa parse_long_mantissa(const char *first, const char *end) {
decimal d = parse_decimal(first, end);
return compute_float<binary>(d);
}
double from_chars(const char *first) noexcept {
bool negative = first[0] == '-';
if (negative) {
@ -489,5 +575,22 @@ double from_chars(const char *first) noexcept {
return value;
}
double from_chars(const char *first, const char *end) noexcept {
bool negative = first[0] == '-';
if (negative) {
first++;
}
adjusted_mantissa am = parse_long_mantissa<binary_format<double>>(first, end);
uint64_t word = am.mantissa;
word |= uint64_t(am.power2)
<< binary_format<double>::mantissa_explicit_bits();
word = negative ? word | (uint64_t(1) << binary_format<double>::sign_index())
: word;
double value;
std::memcpy(&value, &word, sizeof(double));
return value;
}
} // internal
} // simdjson

View File

@ -167,7 +167,6 @@ using namespace simd;
}
this->prev_incomplete = is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1]);
this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1];
}
}
// do not forget to call check_eof!

View File

@ -30,7 +30,8 @@ namespace internal {
{ UNEXPECTED_ERROR, "Unexpected error, consider reporting this problem as you may have found a bug in simdjson" },
{ PARSER_IN_USE, "Cannot parse a new document while a document is still in use." },
{ OUT_OF_ORDER_ITERATION, "Objects and arrays can only be iterated when they are first encountered." },
{ INSUFFICIENT_PADDING, "simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length." }
{ INSUFFICIENT_PADDING, "simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length." },
{ INCOMPLETE_ARRAY_OR_OBJECT, "JSON document ended early in the middle of an object or array." }
}; // error_messages[]
} // namespace internal

View File

@ -62,18 +62,18 @@ namespace array_error_tests {
TEST_START();
ONDEMAND_SUBTEST("missing comma", "[1 1]", assert_iterate(doc, { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[1,,1]", assert_iterate(doc, { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { INCORRECT_TYPE }));
ONDEMAND_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { INCORRECT_TYPE, TAPE_ERROR }));
TEST_SUCCEED();
}
bool top_level_array_iterate_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1 ", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed extra comma", "[,,", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[", assert_iterate(doc, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", "[1 ", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed extra comma", "[,,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", "[1", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", "[", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
@ -81,21 +81,29 @@ namespace array_error_tests {
TEST_START();
ONDEMAND_SUBTEST("missing comma", R"({ "a": [1 1] })", assert_iterate(doc["a"], { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,1] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { INCORRECT_TYPE }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR}));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
TEST_SUCCEED();
}
bool array_iterate_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1 )", assert_iterate(doc["a"], { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1 )", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
// TODO These pass the user values that may run past the end of the buffer if they aren't careful
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1,)", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1)", assert_iterate(doc["a"], { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [)", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool array_iterate_incomplete_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed after array", R"([ [1] )", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed after array", R"([ [1,])", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed after array", R"([ [1])", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed after array", R"([ [])", assert_iterate(doc.get_array().at(0), { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
@ -177,6 +185,7 @@ namespace array_error_tests {
top_level_array_iterate_unclosed_error() &&
array_iterate_error() &&
array_iterate_unclosed_error() &&
array_iterate_incomplete_error() &&
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
out_of_order_array_iteration_error() &&
out_of_order_top_level_array_iteration_error() &&

View File

@ -153,7 +153,7 @@ namespace json_pointer_tests {
ASSERT_ERROR(doc.at_pointer(json_pointer).get(val), simdjson::STRING_ERROR);
std::cout << "\t- unclosed_object" << std::endl;
ASSERT_SUCCESS(parser.iterate(unclosed_object).get(doc));
ASSERT_ERROR(doc.at_pointer(json_pointer).get(val), simdjson::TAPE_ERROR);
ASSERT_ERROR(doc.at_pointer(json_pointer).get(val), simdjson::INCOMPLETE_ARRAY_OR_OBJECT);
std::cout << "\t- missing_bracket_before" << std::endl;
ASSERT_SUCCESS(parser.iterate(missing_bracket_before).get(doc));
ASSERT_ERROR(doc.at_pointer(json_pointer).get(val), simdjson::TAPE_ERROR);

View File

@ -5,6 +5,250 @@ using namespace simdjson;
namespace misc_tests {
using namespace std;
bool issue1661a() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"":],"global-groups":[[]}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::value global_groups;
ASSERT_SUCCESS(doc["global-groups"].get(global_groups));
ondemand::json_type global_type;
ASSERT_SUCCESS(global_groups.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::array);
TEST_SUCCEED();
}
bool issue1660() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_object(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_bool() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_bool(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_uint64() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_uint64(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_int64() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_int64(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_double() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_double(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_null() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_TRUE(!shadowable.is_null());
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_string() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
ondemand::value global;
ASSERT_SUCCESS(global_field.value().get(global));
ondemand::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, ondemand::json_type::object);
ondemand::object global_object;
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_string(), INCORRECT_TYPE);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1661() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"({"":],"global-groups":[[]}})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ondemand::object global_groups;
ASSERT_ERROR(doc["global-groups"].get(global_groups), INCORRECT_TYPE);
ondemand::object globals;
auto error = doc["globals"].get(globals);
if(error == SUCCESS) { return false; }
TEST_SUCCEED();
}
simdjson_warn_unused bool big_integer() {
TEST_START();
simdjson::ondemand::parser parser;
@ -89,6 +333,15 @@ namespace misc_tests {
bool run() {
return
issue1660_with_uint64() &&
issue1660_with_int64() &&
issue1660_with_double() &&
issue1660_with_null() &&
issue1660_with_string() &&
issue1660_with_bool() &&
issue1661a() &&
issue1660() &&
issue1661() &&
big_integer_in_string() &&
big_integer() &&
raw_json_token() &&

View File

@ -61,13 +61,20 @@ namespace object_error_tests {
}
bool object_iterate_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
// TODO These next two pass the user a value that may run past the end of the buffer if they aren't careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool object_iterate_incomplete_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": 1, })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": 1 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": })", assert_iterate_object(doc.get_object(), { INCORRECT_TYPE, INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a" })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed", R"({ "x": { })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
@ -81,11 +88,13 @@ namespace object_error_tests {
}
bool object_lookup_unclosed_error() {
TEST_START();
// TODO This one passes the user a value that may run past the end of the buffer if they aren't careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
#if __SIMDJSON_CHECK_EOF
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
#else
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_success(doc["a"]));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["a"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["a"], TAPE_ERROR));
#endif
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
TEST_SUCCEED();
}
@ -109,13 +118,13 @@ namespace object_error_tests {
}
bool object_lookup_miss_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
// TODO These next two pass the user a value that may run past the end of the buffer if they aren't careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
TEST_SUCCEED();
}
bool object_lookup_miss_next_error() {

View File

@ -0,0 +1,191 @@
#include "simdjson.h"
#include "test_ondemand.h"
using namespace simdjson;
namespace object_tests {
using namespace std;
using simdjson::ondemand::json_type;
bool object_find_field_unordered() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_find_field_unordered() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc_result.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_object_find_field_unordered() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result.find_field_unordered("outer").get(object) );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result.find_field_unordered("outer");
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool object_find_field() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_find_field() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( doc.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( doc.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( doc_result.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( doc_result.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_object_find_field() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result.find_field("outer").get(object) );
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result.find_field("outer");
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool run() {
return
object_find_field_unordered() &&
document_object_find_field_unordered() &&
value_object_find_field_unordered() &&
object_find_field() &&
document_object_find_field() &&
value_object_find_field() &&
true;
}
} // namespace object_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, object_tests::run);
}

View File

@ -0,0 +1,432 @@
#include "simdjson.h"
#include "test_ondemand.h"
using namespace simdjson;
namespace object_tests {
using namespace std;
using simdjson::ondemand::json_type;
bool object_index() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_index() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc_result["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc_result["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_object_index() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result["outer"].get(object) );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result["outer"];
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
bool nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object = doc_result.get_object();
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
bool value_nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> x = doc_result["x"];
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value x;
ASSERT_SUCCESS( doc_result["x"].get(x) );
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
bool object_index_partial_children() {
TEST_START();
auto json = R"(
{
"scalar_ignore": 0,
"empty_array_ignore": [],
"empty_object_ignore": {},
"object_break": { "x": 3, "y": 33 },
"object_break_unused": { "x": 4, "y": 44 },
"object_index": { "x": 5, "y": 55 },
"object_index_unused": { "x": 6, "y": 66 },
"array_break": [ 7, 77, 777 ],
"array_break_unused": [ 8, 88, 888 ],
"quadruple_nested_break": { "a": [ { "b": [ 9, 99 ], "c": 999 }, 9999 ], "d": 99999 },
"actual_value": 10
}
)"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_SUCCESS( object["scalar_ignore"] );
std::cout << " - After ignoring empty scalar ..." << std::endl;
ASSERT_SUCCESS( object["empty_array_ignore"] );
std::cout << " - After ignoring empty array ..." << std::endl;
ASSERT_SUCCESS( object["empty_object_ignore"] );
std::cout << " - After ignoring empty object ..." << std::endl;
// Break after using first value in child object
{
auto value = object["object_break"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
uint64_t x;
ASSERT_SUCCESS( child_field.value().get(x) );
ASSERT_EQUAL(x, 3);
break; // Break after the first value
}
std::cout << " - After using first value in child object ..." << std::endl;
}
// Break without using first value in child object
{
auto value = object["object_break_unused"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
}
// Only look up one field in child object
{
auto value = object["object_index"];
uint64_t x;
ASSERT_SUCCESS( value["x"].get(x) );
ASSERT_EQUAL( x, 5 );
std::cout << " - After looking up one field in child object ..." << std::endl;
}
// Only look up one field in child object, but don't use it
{
auto value = object["object_index_unused"];
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
}
// Break after first value in child array
{
auto value = object["array_break"];
for (auto child_value : value) {
uint64_t x;
ASSERT_SUCCESS( child_value.get(x) );
ASSERT_EQUAL( x, 7 );
break;
}
std::cout << " - After using first value in child array ..." << std::endl;
}
// Break without using first value in child array
{
auto value = object["array_break_unused"];
for (auto child_value : value) {
ASSERT_SUCCESS(child_value);
break;
}
std::cout << " - After reaching (but not using) first value in child array ..." << std::endl;
}
// Break out of multiple child loops
{
auto value = object["quadruple_nested_break"];
for (auto child1 : value.get_object()) {
for (auto child2 : child1.value().get_array()) {
for (auto child3 : child2.get_object()) {
for (auto child4 : child3.value().get_array()) {
uint64_t x;
ASSERT_SUCCESS( child4.get(x) );
ASSERT_EQUAL( x, 9 );
break;
}
break;
}
break;
}
break;
}
std::cout << " - After breaking out of quadruply-nested arrays and objects ..." << std::endl;
}
// Test the actual value
{
auto value = object["actual_value"];
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
}
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_SUCCESS( doc_result["scalar_ignore"] );
std::cout << " - After ignoring empty scalar ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_array_ignore"] );
std::cout << " - After ignoring empty array ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_object_ignore"] );
std::cout << " - After ignoring empty doc_result ..." << std::endl;
// Break after using first value in child object
{
auto value = doc_result["object_break"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
uint64_t x;
ASSERT_SUCCESS( child_field.value().get(x) );
ASSERT_EQUAL(x, 3);
break; // Break after the first value
}
std::cout << " - After using first value in child object ..." << std::endl;
}
// Break without using first value in child object
{
auto value = doc_result["object_break_unused"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
}
// Only look up one field in child object
{
auto value = doc_result["object_index"];
uint64_t x;
ASSERT_SUCCESS( value["x"].get(x) );
ASSERT_EQUAL( x, 5 );
std::cout << " - After looking up one field in child object ..." << std::endl;
}
// Only look up one field in child object, but don't use it
{
auto value = doc_result["object_index_unused"];
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
}
// Break after first value in child array
{
auto value = doc_result["array_break"];
for (auto child_value : value) {
uint64_t x;
ASSERT_SUCCESS( child_value.get(x) );
ASSERT_EQUAL( x, 7 );
break;
}
std::cout << " - After using first value in child array ..." << std::endl;
}
// Break without using first value in child array
{
auto value = doc_result["array_break_unused"];
for (auto child_value : value) {
ASSERT_SUCCESS(child_value);
break;
}
std::cout << " - After reaching (but not using) first value in child array ..." << std::endl;
}
// Break out of multiple child loops
{
auto value = doc_result["quadruple_nested_break"];
for (auto child1 : value.get_object()) {
for (auto child2 : child1.value().get_array()) {
for (auto child3 : child2.get_object()) {
for (auto child4 : child3.value().get_array()) {
uint64_t x;
ASSERT_SUCCESS( child4.get(x) );
ASSERT_EQUAL( x, 9 );
break;
}
break;
}
break;
}
break;
}
std::cout << " - After breaking out of quadruply-nested arrays and objects ..." << std::endl;
}
// Test the actual value
{
auto value = doc_result["actual_value"];
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
}
return true;
}));
return true;
}
#if SIMDJSON_EXCEPTIONS
bool object_index_exception() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object = doc_result;
ASSERT_EQUAL( uint64_t(object["a"]), 1 );
ASSERT_EQUAL( uint64_t(object["b"]), 2 );
ASSERT_EQUAL( uint64_t(object["c/d"]), 3 );
return true;
}));
TEST_SUCCEED();
}
bool nested_object_index_exception() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( uint64_t(doc_result["x"]["y"]["z"]), 2 );
return true;
}));
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
object_index() &&
document_object_index() &&
value_object_index() &&
nested_object_index() &&
document_nested_object_index() &&
value_nested_object_index() &&
object_index_partial_children() &&
#if SIMDJSON_EXCEPTIONS
object_index_exception() &&
nested_object_index_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace object_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, object_tests::run);
}

View File

@ -406,247 +406,6 @@ namespace object_tests {
return true;
}
bool object_index_partial_children() {
TEST_START();
auto json = R"(
{
"scalar_ignore": 0,
"empty_array_ignore": [],
"empty_object_ignore": {},
"object_break": { "x": 3, "y": 33 },
"object_break_unused": { "x": 4, "y": 44 },
"object_index": { "x": 5, "y": 55 },
"object_index_unused": { "x": 6, "y": 66 },
"array_break": [ 7, 77, 777 ],
"array_break_unused": [ 8, 88, 888 ],
"quadruple_nested_break": { "a": [ { "b": [ 9, 99 ], "c": 999 }, 9999 ], "d": 99999 },
"actual_value": 10
}
)"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_SUCCESS( object["scalar_ignore"] );
std::cout << " - After ignoring empty scalar ..." << std::endl;
ASSERT_SUCCESS( object["empty_array_ignore"] );
std::cout << " - After ignoring empty array ..." << std::endl;
ASSERT_SUCCESS( object["empty_object_ignore"] );
std::cout << " - After ignoring empty object ..." << std::endl;
// Break after using first value in child object
{
auto value = object["object_break"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
uint64_t x;
ASSERT_SUCCESS( child_field.value().get(x) );
ASSERT_EQUAL(x, 3);
break; // Break after the first value
}
std::cout << " - After using first value in child object ..." << std::endl;
}
// Break without using first value in child object
{
auto value = object["object_break_unused"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
}
// Only look up one field in child object
{
auto value = object["object_index"];
uint64_t x;
ASSERT_SUCCESS( value["x"].get(x) );
ASSERT_EQUAL( x, 5 );
std::cout << " - After looking up one field in child object ..." << std::endl;
}
// Only look up one field in child object, but don't use it
{
auto value = object["object_index_unused"];
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
}
// Break after first value in child array
{
auto value = object["array_break"];
for (auto child_value : value) {
uint64_t x;
ASSERT_SUCCESS( child_value.get(x) );
ASSERT_EQUAL( x, 7 );
break;
}
std::cout << " - After using first value in child array ..." << std::endl;
}
// Break without using first value in child array
{
auto value = object["array_break_unused"];
for (auto child_value : value) {
ASSERT_SUCCESS(child_value);
break;
}
std::cout << " - After reaching (but not using) first value in child array ..." << std::endl;
}
// Break out of multiple child loops
{
auto value = object["quadruple_nested_break"];
for (auto child1 : value.get_object()) {
for (auto child2 : child1.value().get_array()) {
for (auto child3 : child2.get_object()) {
for (auto child4 : child3.value().get_array()) {
uint64_t x;
ASSERT_SUCCESS( child4.get(x) );
ASSERT_EQUAL( x, 9 );
break;
}
break;
}
break;
}
break;
}
std::cout << " - After breaking out of quadruply-nested arrays and objects ..." << std::endl;
}
// Test the actual value
{
auto value = object["actual_value"];
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
}
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_SUCCESS( doc_result["scalar_ignore"] );
std::cout << " - After ignoring empty scalar ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_array_ignore"] );
std::cout << " - After ignoring empty array ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_object_ignore"] );
std::cout << " - After ignoring empty doc_result ..." << std::endl;
// Break after using first value in child object
{
auto value = doc_result["object_break"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
uint64_t x;
ASSERT_SUCCESS( child_field.value().get(x) );
ASSERT_EQUAL(x, 3);
break; // Break after the first value
}
std::cout << " - After using first value in child object ..." << std::endl;
}
// Break without using first value in child object
{
auto value = doc_result["object_break_unused"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
}
// Only look up one field in child object
{
auto value = doc_result["object_index"];
uint64_t x;
ASSERT_SUCCESS( value["x"].get(x) );
ASSERT_EQUAL( x, 5 );
std::cout << " - After looking up one field in child object ..." << std::endl;
}
// Only look up one field in child object, but don't use it
{
auto value = doc_result["object_index_unused"];
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
}
// Break after first value in child array
{
auto value = doc_result["array_break"];
for (auto child_value : value) {
uint64_t x;
ASSERT_SUCCESS( child_value.get(x) );
ASSERT_EQUAL( x, 7 );
break;
}
std::cout << " - After using first value in child array ..." << std::endl;
}
// Break without using first value in child array
{
auto value = doc_result["array_break_unused"];
for (auto child_value : value) {
ASSERT_SUCCESS(child_value);
break;
}
std::cout << " - After reaching (but not using) first value in child array ..." << std::endl;
}
// Break out of multiple child loops
{
auto value = doc_result["quadruple_nested_break"];
for (auto child1 : value.get_object()) {
for (auto child2 : child1.value().get_array()) {
for (auto child3 : child2.get_object()) {
for (auto child4 : child3.value().get_array()) {
uint64_t x;
ASSERT_SUCCESS( child4.get(x) );
ASSERT_EQUAL( x, 9 );
break;
}
break;
}
break;
}
break;
}
std::cout << " - After breaking out of quadruply-nested arrays and objects ..." << std::endl;
}
// Test the actual value
{
auto value = doc_result["actual_value"];
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
}
return true;
}));
return true;
}
bool iterate_empty_object() {
TEST_START();
auto json = R"({})"_padded;
@ -669,60 +428,6 @@ namespace object_tests {
TEST_SUCCEED();
}
bool object_index() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_index() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc_result["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc_result["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_search_unescaped_key() {
TEST_START();
auto json = R"({"k\u0065y": 1})"_padded;
@ -760,32 +465,6 @@ namespace object_tests {
}));
TEST_SUCCEED();
}
bool value_object_index() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result["outer"].get(object) );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result["outer"];
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object["b"].get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object["a"].get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool issue_1480() {
TEST_START();
@ -862,223 +541,6 @@ namespace object_tests {
TEST_SUCCEED();
}
bool object_find_field_unordered() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_find_field_unordered() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( doc_result.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_object_find_field_unordered() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result.find_field_unordered("outer").get(object) );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result.find_field_unordered("outer");
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().value_unsafe(), 1 );
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool object_find_field() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_object_find_field() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( doc.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( doc.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( doc_result.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( doc_result.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( doc_result.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( doc_result.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool value_object_find_field() {
TEST_START();
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value object;
ASSERT_SUCCESS( doc_result.find_field("outer").get(object) );
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> object = doc_result.find_field("outer");
ASSERT_EQUAL( object.find_field("a").get_uint64().value_unsafe(), 1 );
ASSERT_EQUAL( object.find_field("b").get_uint64().value_unsafe(), 2 );
ASSERT_EQUAL( object.find_field("c/d").get_uint64().value_unsafe(), 3 );
ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD );
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();
}
bool document_nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
bool nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::object> object = doc_result.get_object();
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object;
ASSERT_SUCCESS( doc_result.get(object) );
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
bool value_nested_object_index() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::value> x = doc_result["x"];
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::value x;
ASSERT_SUCCESS( doc_result["x"].get(x) );
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool iterate_object_exception() {
@ -1134,30 +596,6 @@ namespace object_tests {
TEST_SUCCEED();
}
bool object_index_exception() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object = doc_result;
ASSERT_EQUAL( uint64_t(object["a"]), 1 );
ASSERT_EQUAL( uint64_t(object["b"]), 2 );
ASSERT_EQUAL( uint64_t(object["c/d"]), 3 );
return true;
}));
TEST_SUCCEED();
}
bool nested_object_index_exception() {
TEST_START();
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( uint64_t(doc_result["x"]["y"]["z"]), 2 );
return true;
}));
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
@ -1174,25 +612,10 @@ namespace object_tests {
#endif
iterate_object() &&
iterate_empty_object() &&
object_index() &&
document_object_index() &&
value_object_index() &&
object_find_field_unordered() &&
document_object_find_field_unordered() &&
value_object_find_field_unordered() &&
object_find_field() &&
document_object_find_field() &&
value_object_find_field() &&
nested_object_index() &&
document_nested_object_index() &&
value_nested_object_index() &&
iterate_object_partial_children() &&
object_index_partial_children() &&
issue_1480() &&
#if SIMDJSON_EXCEPTIONS
iterate_object_exception() &&
object_index_exception() &&
nested_object_index_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}

View File

@ -15,7 +15,7 @@ namespace wrong_type_error_tests {
return false; \
} \
{ \
padded_string a_json(std::string(R"({ "a": )") + JSON + " })"); \
padded_string a_json(std::string(R"({ "a": )") + JSON + " }"); \
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
if (!test_ondemand_doc(a_json, [&](auto doc_result) { \
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \