Compare commits

..

9 Commits

Author SHA1 Message Date
Daniel Lemire 33d926bd44 Minor fix. 2021-07-31 13:52:01 -04:00
Daniel Lemire e9b893ff1b Preparing the actual release. 2021-06-05 11:33:32 -04:00
Daniel Lemire 438c2a28ff Preparing patch release. 2021-06-04 17:09:21 -04:00
Daniel Lemire 45182ec11b Tagging the version. 2021-05-27 16:21:00 -04:00
Daniel Lemire a0c020d46d Patching. 2021-05-27 16:20:07 -04:00
Daniel Lemire 076f41ae4b Patch release candidate. 2021-05-15 15:45:02 -04:00
Daniel Lemire 6a37fc1871 Disabling perf testing on version 0.9 2021-05-14 09:22:48 -04:00
Daniel Lemire c6c29c2827 Better definition for fallthrough. 2021-03-31 14:38:52 -04:00
Daniel Lemire 941e903f28 Prerelease commit. 2021-03-31 13:48:43 -04:00
12 changed files with 491 additions and 46 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ project(simdjson
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 9)
set(PROJECT_VERSION_PATCH 1)
set(SIMDJSON_SEMANTIC_VERSION "0.9.1" CACHE STRING "simdjson semantic version")
set(PROJECT_VERSION_PATCH 7)
set(SIMDJSON_SEMANTIC_VERSION "0.9.7" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "8.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "8" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "0.9.1"
PROJECT_NUMBER = "0.9.7"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+2 -1
View File
@@ -37,4 +37,5 @@ if (TARGET benchmark::benchmark)
endif()
endif()
include(checkperf.cmake)
# deliberately disabling.
# include(checkperf.cmake)
+19
View File
@@ -251,4 +251,23 @@ namespace std {
#endif
#endif
#if SIMDJSON_CPLUSPLUS17
// if we have C++, then fallthrough is a default attribute
# define simdjson_fallthrough [[fallthrough]]
// check if we have __attribute__ support
#elif defined(__has_attribute)
// check if we have the __fallthrough__ attribute
#if __has_attribute(__fallthrough__)
// we are good to go:
# define simdjson_fallthrough __attribute__((__fallthrough__))
#endif
#endif
// on some systems, we simply do not have support for fallthrough, so use a default:
#ifndef simdjson_fallthrough
# define simdjson_fallthrough do {} while (0) /* fallthrough */
#endif
#endif // SIMDJSON_COMMON_DEFS_H
+1 -1
View File
@@ -286,7 +286,7 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
#if SIMDJSON_EXCEPTIONS
template<typename T>
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) noexcept { return out << value.value(); }
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) { return out << value.value(); }
#endif // SIMDJSON_EXCEPTIONS
@@ -6,6 +6,7 @@ simdjson_really_inline json_iterator::json_iterator(json_iterator &&other) noexc
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth}
{
other.parser = nullptr;
@@ -14,6 +15,7 @@ simdjson_really_inline json_iterator &json_iterator::operator=(json_iterator &&o
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
other.parser = nullptr;
return *this;
@@ -139,21 +139,35 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
return false;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
//
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first_field()) {
// If we're at the beginning of the object, we definitely have a field
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
@@ -166,14 +180,16 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else if (!is_open()) {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
has_value = false;
_json_iter->reenter_child(_start_position + 1, _depth);
at_first = true;
has_value = started_object();
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
@@ -189,8 +205,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else {
// Finish the previous value and see if , or } is next
// If someone queried a key but they did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
@@ -211,10 +233,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
// First, we scan from that point to the end.
// If we don't find a match, we loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// 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
@@ -222,8 +240,11 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
@@ -236,31 +257,44 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
_json_iter->reenter_child(_start_position + 1, _depth);
has_value = started_object();
while (_json_iter->position() < search_start) {
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
@@ -273,16 +307,28 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at.
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION 0.9.1
#define SIMDJSON_VERSION 0.9.7
namespace simdjson {
enum {
@@ -19,7 +19,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 1
SIMDJSON_VERSION_REVISION = 7
};
} // namespace simdjson
+1 -1
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2021-03-18 11:30:40 -0400. Do not edit! */
/* auto-generated on 2021-06-05 11:33:32 -0400. Do not edit! */
/* begin file src/simdjson.cpp */
#include "simdjson.h"
+88 -21
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2021-03-18 11:30:40 -0400. Do not edit! */
/* auto-generated on 2021-06-05 11:33:32 -0400. Do not edit! */
/* begin file include/simdjson.h */
#ifndef SIMDJSON_H
#define SIMDJSON_H
@@ -2045,6 +2045,25 @@ namespace std {
#endif
#endif
#if SIMDJSON_CPLUSPLUS17
// if we have C++, then fallthrough is a default attribute
# define simdjson_fallthrough [[fallthrough]]
// check if we have __attribute__ support
#elif defined(__has_attribute)
// check if we have the __fallthrough__ attribute
#if __has_attribute(__fallthrough__)
// we are good to go:
# define simdjson_fallthrough __attribute__((__fallthrough__))
#endif
#endif
// on some systems, we simply do not have support for fallthrough, so use a default:
#ifndef simdjson_fallthrough
# define simdjson_fallthrough do {} while (0) /* fallthrough */
#endif
#endif // SIMDJSON_COMMON_DEFS_H
/* end file include/simdjson/common_defs.h */
@@ -2059,7 +2078,7 @@ SIMDJSON_DISABLE_UNDESIRED_WARNINGS
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION 0.9.1
#define SIMDJSON_VERSION 0.9.7
namespace simdjson {
enum {
@@ -2074,7 +2093,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 1
SIMDJSON_VERSION_REVISION = 7
};
} // namespace simdjson
@@ -2368,7 +2387,7 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
#if SIMDJSON_EXCEPTIONS
template<typename T>
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) noexcept { return out << value.value(); }
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) { return out << value.value(); }
#endif // SIMDJSON_EXCEPTIONS
@@ -21695,6 +21714,7 @@ simdjson_really_inline json_iterator::json_iterator(json_iterator &&other) noexc
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth}
{
other.parser = nullptr;
@@ -21703,6 +21723,7 @@ simdjson_really_inline json_iterator &json_iterator::operator=(json_iterator &&o
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
other.parser = nullptr;
return *this;
@@ -22088,21 +22109,35 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
return false;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
//
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first_field()) {
// If we're at the beginning of the object, we definitely have a field
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
@@ -22115,14 +22150,16 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else if (!is_open()) {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
has_value = false;
_json_iter->reenter_child(_start_position + 1, _depth);
at_first = true;
has_value = started_object();
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
@@ -22138,8 +22175,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else {
// Finish the previous value and see if , or } is next
// If someone queried a key but they did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
@@ -22160,10 +22203,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
// First, we scan from that point to the end.
// If we don't find a match, we loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// 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
@@ -22171,8 +22210,11 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
@@ -22185,31 +22227,44 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
_json_iter->reenter_child(_start_position + 1, _depth);
has_value = started_object();
while (_json_iter->position() < search_start) {
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
@@ -22222,16 +22277,28 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at.
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
+130
View File
@@ -6,6 +6,135 @@ using namespace simdjson;
namespace array_tests {
using namespace std;
using simdjson::ondemand::json_type;
bool issue1588() {
TEST_START();
const auto json = R"({
"nodes" : [
{
"rotation" : [
0.16907575726509094,
0.7558803558349609,
-0.27217137813568115,
0.570947527885437
],
"translation" : [
4.076245307922363,
5.903861999511719,
-1.0054539442062378
]
},
{
"camera" : 0,
"rotation" : [
-0.7071067690849304,
0,
0,
0.7071067690849304
]
},
{
"children" : [
1
],
"translation" : [
7.358891487121582,
4.958309173583984,
6.925790786743164
]
},
{
"mesh" : 1,
"scale" : [
4.7498908042907715,
4.7498908042907715,
4.7498908042907715
]
}
]
})"_padded;
// we query 'rotation', 'scale', 'translation' in sequence
const bool expected_value[][3] = { {true, false, true},
{true, false, false}, {false, false, true}, {false, true, false} };
SUBTEST("ondemand::issue1588::sequence", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::array array;
ASSERT_SUCCESS( doc_result["nodes"].get(array) );
size_t i=0;
for (auto value : array) {
ondemand::object current_object;
ASSERT_SUCCESS( value.get_object().get(current_object) );
std::cout << "[ondemand::issue1588::sequence] acquired a new object ==========" << std::endl;
simdjson::ondemand::array rotation;
if(expected_value[i][0]) {
ASSERT_SUCCESS( current_object["rotation"].get(rotation) );
std::cout << "[ondemand::issue1588::sequence] found 'rotation' " << std::endl;
} else {
ASSERT_ERROR( current_object["rotation"].get(rotation), NO_SUCH_FIELD );
std::cout << "[ondemand::issue1588::sequence] rotation not found" << std::endl;
}
simdjson::ondemand::array scale;
if(expected_value[i][1]) {
ASSERT_SUCCESS( current_object["scale"].get(scale) );
std::cout << "[ondemand::issue1588::sequence] found 'scale' " << std::endl;
} else {
ASSERT_ERROR( current_object["scale"].get(scale), NO_SUCH_FIELD );
std::cout << "[ondemand::issue1588::sequence] scale not found" << std::endl;
}
simdjson::ondemand::array translation;
if(expected_value[i][2]) {
ASSERT_SUCCESS( current_object["translation"].get(translation) );
std::cout << "[ondemand::issue1588::sequence] found 'translation' " << std::endl;
} else {
ASSERT_ERROR( current_object["translation"].get(translation), NO_SUCH_FIELD );
std::cout << "[ondemand::issue1588::sequence] translation not found" << std::endl;
}
i++;
}
ASSERT_EQUAL(i, 4);
return true;
}));
SUBTEST("ondemand::issue1588::originalcode", test_ondemand_doc(json, [&](auto doc_result) {
int count_nodes = 0;
simdjson::ondemand::array doc_nodes;
auto error = doc_result["nodes"].get(doc_nodes);
ASSERT_SUCCESS( error );
for (auto node_iterator : doc_nodes) {
ondemand::object node_obj;
ASSERT_SUCCESS( node_iterator.get_object().get(node_obj) );
simdjson::ondemand::array rotation;
std::cout << "checking rotation" << std::endl;
if(expected_value[count_nodes][0]) {
ASSERT_SUCCESS( node_obj["rotation"].get(rotation) );
} else {
ASSERT_ERROR( node_obj["rotation"].get(rotation), NO_SUCH_FIELD );
}
simdjson::ondemand::array scale;
std::cout << "checking scale" << std::endl;
if(expected_value[count_nodes][1]) {
ASSERT_SUCCESS( node_obj["scale"].get(scale) );
} else {
ASSERT_ERROR( node_obj["scale"].get(scale), NO_SUCH_FIELD );
}
simdjson::ondemand::array translation;
std::cout << "checking translation" << std::endl;
if (!error) { std::cout << "translation!\n"; }
if(expected_value[count_nodes][2]) {
ASSERT_SUCCESS( node_obj["translation"].get(translation) );
} else {
ASSERT_ERROR( node_obj["translation"].get(translation), NO_SUCH_FIELD );
}
++count_nodes;
}
ASSERT_EQUAL(count_nodes, 4);
return true;
}));
TEST_SUCCEED();
}
bool iterate_document_array() {
TEST_START();
@@ -341,6 +470,7 @@ namespace array_tests {
bool run() {
return
issue1588() &&
iterate_array() &&
iterate_document_array() &&
iterate_empty_array() &&
+180
View File
@@ -7,6 +7,178 @@ namespace object_tests {
using namespace std;
using simdjson::ondemand::json_type;
// In this test, no non-trivial object in an array have a missing key
bool no_missing_keys() {
TEST_START();
simdjson::ondemand::parser parser;
simdjson::padded_string docdata = R"([{"a":"a"},{}])"_padded;
simdjson::ondemand::document doc;
auto error = parser.iterate(docdata).get(doc);
if(error != simdjson::SUCCESS) { return false; }
simdjson::ondemand::array a;
error = doc.get_array().get(a);
if(error != simdjson::SUCCESS) { return false; }
size_t counter{0};
for(auto elem : a) {
error = elem.find_field_unordered("a").error();
if(counter == 0) {
ASSERT_EQUAL( error, simdjson::SUCCESS);
} else {
ASSERT_EQUAL( error, simdjson::NO_SUCH_FIELD);
}
counter++;
}
return true;
}
bool missing_key_continue() {
TEST_START();
simdjson::ondemand::parser parser;
simdjson::padded_string docdata = R"({"a":0, "b":1, "c":2})"_padded;
simdjson::ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
int64_t num;
ASSERT_SUCCESS(doc["a"].get(num));
ASSERT_EQUAL(num, 0);
ASSERT_SUCCESS(doc["b"].get(num));
ASSERT_EQUAL(num, 1);
ASSERT_SUCCESS(doc["c"].get(num));
ASSERT_EQUAL(num, 2);
// Start again, but omit a key
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc["a"].get(num));
ASSERT_EQUAL(num, 0);
ASSERT_SUCCESS(doc["c"].get(num));
ASSERT_EQUAL(num, 2);
// Start again, but request a missing key
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
simdjson::ondemand::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
ASSERT_SUCCESS(obj["c"].get(num));
ASSERT_EQUAL(num, 2);
// Start again, but request a missing key first
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_object().get(obj));
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
// Start again, but request a missing key twice
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_object().get(obj));
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
assert_error(obj["z"].get(num), NO_SUCH_FIELD);
// Because we do a full circle, you can query the same
// key twice!!!
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
ASSERT_SUCCESS(obj["b"].get(num));
ASSERT_EQUAL(num, 1);
ASSERT_SUCCESS(obj["b"].get(num));
ASSERT_EQUAL(num, 1);
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
ASSERT_SUCCESS(obj["a"].get(num));
ASSERT_EQUAL(num, 0);
TEST_SUCCEED();
}
bool missing_keys() {
TEST_START();
simdjson::ondemand::parser parser;
simdjson::padded_string docdata = R"([{"a":"a"},{}])"_padded;
simdjson::ondemand::document doc;
auto error = parser.iterate(docdata).get(doc);
if(error != simdjson::SUCCESS) { return false; }
simdjson::ondemand::array a;
error = doc.get_array().get(a);
if(error != simdjson::SUCCESS) { return false; }
for(auto elem : a) {
error = elem.find_field_unordered("keynotfound").error();
if(error != simdjson::NO_SUCH_FIELD) {
std::cout << error << std::endl;
return false;
}
}
return true;
}
#if SIMDJSON_EXCEPTIONS
// used in issue_1521
// difficult to use as a lambda because it is recursive.
void broken_descend(ondemand::object node) {
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type == "child") {
auto n = node.find_field_unordered("name");
if(n.error() == simdjson::SUCCESS) {
std::cout << std::string_view(n) << std::endl;
}
} else {
for (ondemand::object child_node : node["nodes"]) { broken_descend(child_node); }
}
}
bool broken_issue_1521() {
TEST_START();
ondemand::parser parser;
padded_string json = R"({"type":"root","nodes":[{"type":"child","nodes":[]},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
ondemand::document file_tree = parser.iterate(json);
try {
broken_descend(file_tree);
} catch(simdjson::simdjson_error& e) {
std::cout << "The document is valid JSON: " << json << std::endl;
TEST_FAIL(e.error());
}
TEST_SUCCEED();
}
bool fixed_broken_issue_1521() {
TEST_START();
ondemand::parser parser;
// We omit the ',"nodes":[]'
padded_string json = R"({"type":"root","nodes":[{"type":"child"},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
ondemand::document file_tree = parser.iterate(json);
try {
broken_descend(file_tree);
} catch(simdjson::simdjson_error& e) {
std::cout << "The document is valid JSON: " << json << std::endl;
TEST_FAIL(e.error());
}
TEST_SUCCEED();
}
// used in issue_1521
// difficult to use as a lambda because it is recursive.
void descend(ondemand::object node) {
auto n = node.find_field_unordered("name");
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type == "child") {
if(n.error() == simdjson::SUCCESS) {
std::cout << std::string_view(n) << std::endl;
}
} else {
for (ondemand::object child_node : node["nodes"]) { descend(child_node); }
}
}
bool issue_1521() {
TEST_START();
ondemand::parser parser;
padded_string json = R"({"type":"root","nodes":[{"type":"child","nodes":[]},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
ondemand::document file_tree = parser.iterate(json);
try {
descend(file_tree);
} catch(simdjson::simdjson_error& e) {
std::cout << "The document is valid JSON: " << json << std::endl;
TEST_FAIL(e.error());
}
TEST_SUCCEED();
}
#endif
bool iterate_object() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c": 3 })"_padded;
@@ -893,6 +1065,14 @@ namespace object_tests {
bool run() {
return
no_missing_keys() &&
missing_key_continue() &&
missing_keys() &&
#if SIMDJSON_EXCEPTIONS
fixed_broken_issue_1521() &&
issue_1521() &&
broken_issue_1521() &&
#endif
iterate_object() &&
iterate_empty_object() &&
object_index() &&