Add tests for singlestage (copy of ondemand)

This commit is contained in:
John Keiser
2023-07-05 15:52:56 -07:00
parent ef6fc77861
commit 5b02e117bb
36 changed files with 10862 additions and 0 deletions
+1
View File
@@ -3,6 +3,7 @@ link_libraries(simdjson-internal-flags test-data simdjson-windows-headers)
include(${PROJECT_SOURCE_DIR}/cmake/add_cpp_test.cmake)
add_subdirectory(dom)
add_subdirectory(ondemand)
add_subdirectory(singlestage)
# All remaining tests link with simdjson proper
+40
View File
@@ -0,0 +1,40 @@
# All remaining tests link with simdjson proper
link_libraries(simdjson)
include_directories(..)
add_subdirectory(compilation_failure_tests)
add_cpp_test(singlestage_log_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_log_error_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_tostring_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_active_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_array_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_array_error_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_compilation_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_document_stream_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_error_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_error_location_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_json_pointer_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_key_string_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_misc_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_number_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_number_in_string_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_object_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_object_error_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_ordering_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_parse_api_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_readme_examples LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_scalar_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_twitter_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_wrong_type_error_tests LABELS singlestage acceptance per_implementation)
add_cpp_test(singlestage_iterate_many_csv LABELS singlestage acceptance per_implementation)
if(HAVE_POSIX_FORK AND HAVE_POSIX_WAIT) # assert tests use fork and wait, which aren't on MSVC
add_cpp_test(singlestage_assert_out_of_order_values LABELS assert per_implementation explicitonly singlestage)
endif()
# Copy the simdjson dll into the tests directory
if(MSVC AND BUILD_SHARED_LIBS)
add_custom_command(TARGET singlestage_parse_api_tests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$<TARGET_FILE:simdjson>" # <--this is in-file
"$<TARGET_FILE_DIR:singlestage_parse_api_tests>") # <--this is out-file path
endif(MSVC AND BUILD_SHARED_LIBS)
@@ -0,0 +1,20 @@
#
# This directory contains files aimed to verify that constructs that
# are supposed to fail at compile time, indeed do so.
# To prevent bit rot, the same source file is compiled twice with
# the macro COMPILATION_TEST_USE_FAILING_CODE set to 0 or 1.
#
# adds a compilation test. Two targets are created, one expected to
# succeed compilation and one that is expected to fail.
function(add_dual_compile_test TEST_NAME)
add_cpp_test(${TEST_NAME}_should_compile SOURCES ${TEST_NAME}.cpp COMPILE_ONLY LABELS singlestage no_mingw)
add_cpp_test(${TEST_NAME}_should_not_compile SOURCES ${TEST_NAME}.cpp COMPILE_ONLY WILL_FAIL LABELS singlestage acceptance no_mingw)
target_compile_definitions(${TEST_NAME}_should_not_compile PRIVATE COMPILATION_TEST_USE_FAILING_CODE=1)
endfunction(add_dual_compile_test)
add_dual_compile_test(singlestage_iterate_char_star)
add_dual_compile_test(singlestage_iterate_string_view)
add_dual_compile_test(singlestage_iterate_temporary_buffer)
add_dual_compile_test(singlestage_first_second_access)
@@ -0,0 +1,28 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
auto json = "1"_padded;
singlestage::parser parser;
singlestage::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { return EXIT_FAILURE; }
simdjson_result<singlestage::value> query = doc.at_pointer("/");
#if COMPILATION_TEST_USE_FAILING_CODE
if(query.second == simdjson::SUCCESS) {
std::cout << "success" << std::endl;
std::cout << query.first << std::endl;
}
#else
singlestage::value val;
error = query.get(val);
if(error == simdjson::SUCCESS) {
std::cout << "success" << std::endl;
std::cout << val << std::endl;
}
#endif
return EXIT_SUCCESS;
}
@@ -0,0 +1,19 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
singlestage::parser parser;
#if COMPILATION_TEST_USE_FAILING_CODE
const char* json;
auto doc = parser.iterate(json, strlen(json));
#else
auto json = "1"_padded;
auto doc = parser.iterate(json);
#endif
int64_t value;
auto error = doc.get(value);
if (error) { exit(1); }
std::cout << value << std::endl;
}
@@ -0,0 +1,19 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
singlestage::parser parser;
#if COMPILATION_TEST_USE_FAILING_CODE
auto json = std::string_view("1");
auto doc = parser.iterate(json);
#else
auto json = "1"_padded;
auto doc = parser.iterate(json);
#endif
int64_t value;
auto error = doc.get(value);
if (error) { exit(1); }
std::cout << value << std::endl;
}
@@ -0,0 +1,18 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
singlestage::parser parser;
#if COMPILATION_TEST_USE_FAILING_CODE
auto doc = parser.iterate("1"_padded);
#else
auto json = "1"_padded;
auto doc = parser.iterate(json);
#endif
int64_t value;
auto error = doc.get(value);
if (error) { exit(1); }
std::cout << value << std::endl;
}
@@ -0,0 +1,71 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace active_tests {
#if SIMDJSON_EXCEPTIONS
bool parser_child() {
TEST_START();
singlestage::parser parser;
const padded_string json = R"({ "parent": {"child1": {"name": "John"} , "child2": {"name": "Daniel"}} })"_padded;
auto doc = parser.iterate(json);
singlestage::object parent = doc["parent"];
{
singlestage::object c1 = parent["child1"];
if(std::string_view(c1["name"]) != "John") { return false; }
}
{
singlestage::object c2 = parent["child2"];
if(std::string_view(c2["name"]) != "Daniel") { return false; }
}
return true;
}
bool parser_doc_correct() {
TEST_START();
singlestage::parser parser;
const padded_string json = R"({ "key1": 1, "key2":2, "key3": 3 })"_padded;
auto doc = parser.iterate(json);
singlestage::object root_object = doc.get_object();
int64_t k1 = root_object["key1"];
int64_t k2 = root_object["key2"];
int64_t k3 = root_object["key3"];
return (k1 == 1) && (k2 == 2) && (k3 == 3);
}
bool parser_doc_limits() {
TEST_START();
singlestage::parser parser;
const padded_string json = R"({ "key1": 1, "key2":2, "key3": 3 })"_padded;
auto doc = parser.iterate(json);
int64_t k1 = doc["key1"];
try {
int64_t k2 = doc["key2"];
(void) k2;
} catch (simdjson::simdjson_error &) {
return true; // we expect to fail.
}
(void) k1;
return false;
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
#if SIMDJSON_EXCEPTIONS
parser_child() &&
parser_doc_correct() &&
// parser_doc_limits() && // Failure is dependent on build type here ...
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace active_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, active_tests::run);
}
@@ -0,0 +1,216 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace array_error_tests {
using namespace std;
template<typename V, typename T>
bool assert_iterate(T array, V *expected, size_t N, simdjson::error_code *expected_error, size_t N2) {
/**
* We use printouts because the assert_iterate is abstract and hard to
* understand intuitively.
*/
std::cout << " --- assert_iterate ";
size_t count = 0;
for (auto elem : std::forward<T>(array)) {
std::cout << "-"; std::cout.flush();
V actual{};
auto actual_error = elem.get(actual);
if (count >= N) {
if (count >= (N+N2)) {
std::cerr << "FAIL: Extra error reported: " << actual_error << std::endl;
return false;
}
std::cout << "[ expect: " << expected_error[count - N] << " ]"; std::cout.flush();
ASSERT_ERROR(actual_error, expected_error[count - N]);
} else {
std::cout << "[ expect: SUCCESS ]"; std::cout.flush();
ASSERT_SUCCESS(actual_error);
std::cout << "{ expect value : "<< expected[count] << " }"; std::cout.flush();
ASSERT_EQUAL(actual, expected[count]);
}
count++;
}
ASSERT_EQUAL(count, N+N2);
std::cout << std::endl;
return true;
}
template<typename V, size_t N, size_t N2, typename T>
bool assert_iterate(T &array, V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<V, T&>(array, expected, N, expected_error, N2);
}
template<size_t N2, typename T>
bool assert_iterate(T &array, simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<int64_t, T&>(array, nullptr, 0, expected_error, N2);
}
template<typename V, size_t N, typename T>
bool assert_iterate(T &array, V (&&expected)[N]) {
return assert_iterate<V, T&&>(array, expected, N, nullptr, 0);
}
template<typename V, size_t N, size_t N2, typename T>
bool assert_iterate(T &&array, V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<V, T&&>(std::forward<T>(array), expected, N, expected_error, N2);
}
template<size_t N2, typename T>
bool assert_iterate(T &&array, simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<int64_t, T&&>(std::forward<T>(array), nullptr, 0, expected_error, N2);
}
template<typename V, size_t N, typename T>
bool assert_iterate(T &&array, V (&&expected)[N]) {
return assert_iterate<V, T&&>(std::forward<T>(array), expected, N, nullptr, 0);
}
bool top_level_array_iterate_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing comma", "[1 1]", assert_iterate(doc, { int64_t(1) }, { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", "[1,,1]", assert_iterate(doc, { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { INCORRECT_TYPE, TAPE_ERROR }));
TEST_SUCCEED();
}
bool top_level_array_iterate_unclosed_error() {
TEST_START();
SINGLESTAGE_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", "[1 ", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed extra comma", "[,,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", "[1", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", "[", assert_iterate(doc, { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool array_iterate_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing comma", R"({ "a": [1 1] })", assert_iterate(doc["a"], { int64_t(1) }, { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", R"({ "a": [1,,1] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR}));
SINGLESTAGE_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
TEST_SUCCEED();
}
bool array_iterate_unclosed_error() {
TEST_START();
SINGLESTAGE_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_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!
SINGLESTAGE_SUBTEST("unclosed ", R"({ "a": [1,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", R"({ "a": [1)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed ", R"({ "a": [)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool array_iterate_incomplete_error() {
TEST_START();
#if SIMDJSON_CHECK_EOF
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1] )", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1,])", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1])", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [])", assert_iterate(doc.get_array().at(0), { INCOMPLETE_ARRAY_OR_OBJECT }));
#else
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1] )", assert_iterate(doc.get_array().at(0), { int64_t(1) }));
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1,])", assert_iterate(doc.get_array().at(0), { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("unclosed after array", R"([ [1])", assert_iterate(doc.get_array().at(0), { int64_t(1) }));
#endif
TEST_SUCCEED();
}
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
bool out_of_order_array_iteration_error() {
TEST_START();
auto json = R"([ [ 1, 2 ] ])"_padded;
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
for (auto arr : doc) {
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
}
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
for (auto element : doc) {
singlestage::value arr;
ASSERT_SUCCESS( element.get(arr) );
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
}
return true;
}));
SUBTEST("simdjson_result<array>", test_singlestage_doc(json, [&](auto doc) {
for (auto element : doc) {
auto arr = element.get_array();
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
}
return true;
}));
SUBTEST("array", test_singlestage_doc(json, [&](auto doc) {
for (auto element : doc) {
singlestage::array arr;
ASSERT_SUCCESS( element.get(arr) );
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
}
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_top_level_array_iteration_error() {
TEST_START();
auto json = R"([ 1, 2 ])"_padded;
SUBTEST("simdjson_result<document>", test_singlestage_doc(json, [&](auto arr) {
for (auto element : arr) { ASSERT_SUCCESS(element); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("document", test_singlestage_doc(json, [&](auto doc) {
singlestage::document arr;
ASSERT_SUCCESS( std::move(doc).get(arr) );
for (auto element : arr) { ASSERT_SUCCESS(element); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("simdjson_result<array>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::array> arr = doc.get_array();
for (auto element : arr) { ASSERT_SUCCESS(element); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("array", test_singlestage_doc(json, [&](auto doc) {
singlestage::array arr;
ASSERT_SUCCESS( doc.get(arr) );
for (auto element : arr) { ASSERT_SUCCESS(element); }
ASSERT_ITERATE_ERROR( arr, OUT_OF_ORDER_ITERATION );
return true;
}));
TEST_SUCCEED();
}
#endif // SIMDJSON_DEVELOPMENT_CHECKS
bool run() {
return
top_level_array_iterate_error() &&
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() &&
#endif
true;
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, array_error_tests::run);
}
@@ -0,0 +1,875 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace array_tests {
using namespace std;
using simdjson::singlestage::json_type;
bool issue1977() {
TEST_START();
auto json = R"([1, 2] foo ])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
singlestage::array array;
ASSERT_SUCCESS(doc.get_array().get(array));
for (auto values : array) {
ASSERT_SUCCESS(values);
}
ASSERT_FALSE(doc.at_end());
TEST_SUCCEED();
}
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("singlestage::issue1588::sequence", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_SUCCESS( doc_result["nodes"].get(array) );
size_t i=0;
for (auto value : array) {
singlestage::object current_object;
ASSERT_SUCCESS( value.get_object().get(current_object) );
simdjson::singlestage::array rotation;
if(expected_value[i][0]) {
ASSERT_SUCCESS( current_object["rotation"].get(rotation) );
} else {
ASSERT_ERROR( current_object["rotation"].get(rotation), NO_SUCH_FIELD );
}
simdjson::singlestage::array scale;
if(expected_value[i][1]) {
ASSERT_SUCCESS( current_object["scale"].get(scale) );
} else {
ASSERT_ERROR( current_object["scale"].get(scale), NO_SUCH_FIELD );
}
simdjson::singlestage::array translation;
if(expected_value[i][2]) {
ASSERT_SUCCESS( current_object["translation"].get(translation) );
} else {
ASSERT_ERROR( current_object["translation"].get(translation), NO_SUCH_FIELD );
}
i++;
}
ASSERT_EQUAL(i, 4);
return true;
}));
SUBTEST("singlestage::issue1588::originalcode", test_singlestage_doc(json, [&](auto doc_result) {
int count_nodes = 0;
simdjson::singlestage::array doc_nodes;
auto error = doc_result["nodes"].get(doc_nodes);
ASSERT_SUCCESS( error );
for (auto node_iterator : doc_nodes) {
singlestage::object node_obj;
ASSERT_SUCCESS( node_iterator.get_object().get(node_obj) );
simdjson::singlestage::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::singlestage::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::singlestage::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 issue1876() {
TEST_START();
auto json = R"( [] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t count{};
ASSERT_SUCCESS(doc.count_elements().get(count));
singlestage::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
ASSERT_EQUAL(count, 0);
for (auto element : arr) {
double value;
ASSERT_SUCCESS(element.get_double().get(value));
std::cout << value << std::endl;
}
TEST_SUCCEED();
}
bool iterate_complex_array_count() {
TEST_START();
singlestage::parser parser;
auto cars_json = R"( { "zero":[], "test":[ { "val1":1, "val2":2 }, { "val1":1, "val2":2 } ] } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(cars_json).get(doc));
singlestage::array firstmyarray;
singlestage::array secondmyarray;
ASSERT_SUCCESS(doc.find_field("zero").get_array().get(firstmyarray));
size_t count{0};
ASSERT_SUCCESS(firstmyarray.count_elements().get(count));
ASSERT_EQUAL(count, 0);
size_t new_count{0};
for(simdjson_unused auto elem: firstmyarray) { new_count++; }
ASSERT_EQUAL(new_count, 0);
ASSERT_SUCCESS(doc.find_field("test").get_array().get(secondmyarray));
ASSERT_SUCCESS(secondmyarray.count_elements().get(count));
ASSERT_EQUAL(count, 2);
new_count = 0;
for(simdjson_unused auto elem: secondmyarray) { new_count++; }
ASSERT_EQUAL(count, new_count);
TEST_SUCCEED();
}
bool iterate_sub_array_count() {
TEST_START();
singlestage::parser parser;
auto key_value_json = R"( { "test":[ 1,2,3], "joe": [1,2] } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(key_value_json).get(doc));
singlestage::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
singlestage::value v;
ASSERT_SUCCESS(doc.find_field("test").get(v));
size_t count{};
ASSERT_SUCCESS(v.count_elements().get(count));
ASSERT_EQUAL(count, 3);
ASSERT_SUCCESS(doc.find_field("joe").get(v));
ASSERT_SUCCESS(v.count_elements().get(count));
ASSERT_EQUAL(count, 2);
TEST_SUCCEED();
}
bool iterate_array_count() {
TEST_START();
const auto json = R"([ 1, 10, 100 ])"_padded;
const vector<uint64_t> expected_value = { 1, 10, 100 };
SUBTEST("singlestage::count_elements", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get_array().get(array) );
size_t count{};
ASSERT_SUCCESS( array.count_elements().get(count) );
ASSERT_EQUAL(count, expected_value.size());
return true;
}));
SUBTEST("singlestage::count_elements_and_decode", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t count{};
ASSERT_SUCCESS( array.count_elements().get(count) );
ASSERT_EQUAL(count, expected_value.size());
size_t i = 0;
std::vector<uint64_t> receiver(count);
for (auto value : array) {
uint64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
ASSERT_EQUAL(actual, expected_value[i]);
receiver[i] = actual;
i++;
}
return true;
}));
TEST_SUCCEED();
}
bool iterate_empty_array_count() {
TEST_START();
const auto json = R"([])"_padded;
SUBTEST("singlestage::count_elements", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get_array().get(array) );
size_t count{};
ASSERT_SUCCESS( array.count_elements().get(count) );
ASSERT_EQUAL(count, 0);
return true;
}));
SUBTEST("singlestage::count_elements_and_decode", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t count{};
ASSERT_SUCCESS( array.count_elements().get(count) );
ASSERT_EQUAL(count, 0);
size_t i = 0;
std::vector<uint64_t> receiver(count);
for (auto value : array) {
uint64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
i++;
}
ASSERT_EQUAL(i, 0);
return true;
}));
TEST_SUCCEED();
}
bool iterate_bad_array_count() {
TEST_START();
const auto badjson = R"([ 1, 10 100 ])"_padded;
SUBTEST("singlestage::count_elements", test_singlestage_doc(badjson, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t count{};
auto e = array.count_elements().get(count);
if( e != TAPE_ERROR) {
std::cout << e << "\n";
std::cout << "expected: " << TAPE_ERROR << "\n";
std::cout << "count = " << count << "\n";
return false;
}
return true;
}));
TEST_SUCCEED();
}
bool iterate_document_array_count() {
TEST_START();
auto empty = R"( [] )"_padded;
SUBTEST("singlestage::empty_doc_array", test_singlestage_doc(empty, [&](auto doc_result) {
size_t count{};
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.count_elements().get(count) );
ASSERT_EQUAL( count, 0 );
return true;
}));
auto basic = R"( [-1.234, 100000000000000, null, [1,2,3], {"t":true, "f":false}] )"_padded;
SUBTEST("singlestage::basic_doc_array", test_singlestage_doc(basic, [&](auto doc_result) {
size_t count{};
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.count_elements().get(count) );
ASSERT_EQUAL( count, 5 );
return true;
}));
TEST_SUCCEED();
}
bool iterate_bad_document_array_count() {
TEST_START();
padded_string bad_jsons[2] = {R"( [1, 10 1000] )"_padded, R"( [1.23, 2.34 )"_padded};
std::string names[2] = {"missing_comma", "missing_bracket"};
simdjson::error_code errors[2] = {TAPE_ERROR, INCOMPLETE_ARRAY_OR_OBJECT};
size_t count{0};
for (auto name : names) {
SUBTEST("singlestage::" + name, test_singlestage_doc(bad_jsons[count], [&](auto doc_result) {
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_ERROR(doc_result.count_elements(), errors[count]);
return true;
}));
count++;
}
ASSERT_EQUAL(count, 2);
TEST_SUCCEED();
}
bool iterate_document_array() {
TEST_START();
const auto json = R"([ 1, 10, 100 ])"_padded;
const uint64_t expected_value[] = { 1, 10, 100 };
SUBTEST("singlestage::array", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t i = 0;
for (auto value : array) {
int64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
ASSERT_EQUAL(actual, expected_value[i]);
i++;
}
ASSERT_EQUAL(i*sizeof(int64_t), sizeof(expected_value));
return true;
}));
SUBTEST("singlestage::array-document-rewind", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t i = 0;
for (auto value : array) { (void)value; i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
std::vector<int64_t> container(i); // container of size 'i'.
doc_result.rewind();
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
i = 0;
for (auto value : array) {
int64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
container[i] = actual;
i++;
}
ASSERT_EQUAL(i * sizeof(int64_t), sizeof(expected_value));
for(size_t j = 0; j < sizeof(expected_value)/sizeof(int64_t); j++) {
ASSERT_EQUAL(container[j], expected_value[j]);
}
return true;
}));
SUBTEST("singlestage::array-rewind", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t i = 0;
for (auto value : array) { (void)value; i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
std::vector<int64_t> container(i); // container of size 'i'.
array.reset();
i = 0;
for (auto value : array) {
int64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
container[i] = actual;
i++;
}
ASSERT_EQUAL(i * sizeof(int64_t), sizeof(expected_value));
for(size_t j = 0; j < sizeof(expected_value)/sizeof(int64_t); j++) {
ASSERT_EQUAL(container[j], expected_value[j]);
}
return true;
}));
SUBTEST("simdjson_result<singlestage::array>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::array> array = doc_result.get_array();
ASSERT_RESULT( doc_result.type(), json_type::array );
size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true;
}));
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_RESULT( doc.type(), json_type::array );
size_t i=0;
for (simdjson_unused auto value : doc) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true;
}));
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](auto doc_result) {
size_t i=0;
ASSERT_RESULT( doc_result.type(), json_type::array );
for (simdjson_unused auto value : doc_result) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true;
}));
TEST_SUCCEED();
}
bool empty_rewind() {
TEST_START();
const auto json = R"( [] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
singlestage::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
for(simdjson_unused auto i : arr) {
TEST_FAIL("should be empty?");
}
arr.reset();
for(simdjson_unused auto i : arr) {
TEST_FAIL("should be empty?");
}
TEST_SUCCEED();
}
bool count_empty(simdjson::singlestage::array arr) {
size_t count{};
ASSERT_SUCCESS(arr.count_elements().get(count));
ASSERT_EQUAL(count, 0);
bool is_empty;
ASSERT_SUCCESS(arr.is_empty().get(is_empty));
ASSERT_TRUE(is_empty);
return true;
}
bool issue1742() {
TEST_START();
auto json = R"( {
"code": 0,
"method": "subscribe",
"result": {
"instrument_name": "DAI_USDC",
"subscription": "trade.DAI_USDC",
"channel": "trade",
"data": [
[1,2,3,4]
]
}
} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
simdjson::singlestage::array data;
ASSERT_SUCCESS(doc["result"]["data"].get_array().get(data));
for (auto d : data) {
simdjson::singlestage::array arr;
ASSERT_SUCCESS(d.get_array().get(arr));
size_t count{};
ASSERT_SUCCESS(arr.count_elements().get(count));
ASSERT_EQUAL(count, 4);
}
TEST_SUCCEED();
}
bool value_to_array(simdjson::singlestage::value val) {
singlestage::json_type t;
ASSERT_SUCCESS(val.type().get(t));
ASSERT_EQUAL(t, singlestage::json_type::array);
simdjson::singlestage::array arr;
ASSERT_SUCCESS(val.get_array().get(arr));
if(count_empty(arr) != true) { return false; }
return true;
}
bool empty_rewind_convoluted() {
TEST_START();
const auto json = R"( [] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
singlestage::value val;
ASSERT_SUCCESS(doc.get_value().get(val));
if(!value_to_array(val)) { return false; }
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool value_to_array_except(simdjson::singlestage::value val) {
singlestage::json_type t = val.type();
ASSERT_EQUAL(t, singlestage::json_type::array);
if(count_empty(simdjson::singlestage::array(val)) != true) { return false; }
return true;
}
bool empty_rewind_convoluted_with_exceptions() {
TEST_START();
const auto json = R"( [] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
singlestage::value val;
ASSERT_SUCCESS(doc.get_value().get(val));
if(value_to_array_except(val) != true) { return false; }
TEST_SUCCEED();
}
#endif
bool iterate_array() {
TEST_START();
const auto json = R"( [ [ 1, 10, 100 ] ] )"_padded;
const uint64_t expected_value[] = { 1, 10, 100 };
SUBTEST("singlestage::array", test_singlestage_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<singlestage::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
singlestage::array array;
ASSERT_RESULT( array_result.type(), json_type::array );
ASSERT_SUCCESS( array_result.get(array) );
size_t i=0;
for (auto value : array) {
int64_t actual{};
ASSERT_SUCCESS( value.get(actual) );
ASSERT_EQUAL(actual, expected_value[i]);
i++;
}
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("simdjson_result<singlestage::array>", test_singlestage_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<singlestage::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
singlestage::array array;
ASSERT_RESULT( array_result.type(), json_type::array );
ASSERT_SUCCESS( array_result.get(array) );
size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<singlestage::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
singlestage::value array;
ASSERT_SUCCESS( std::move(array_result).get(array) );
ASSERT_RESULT( array.type(), json_type::array );
size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("simdjson_result<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<singlestage::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
size_t i=0;
ASSERT_RESULT( array_result.type(), json_type::array );
for (simdjson_unused auto value : array_result) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
TEST_SUCCEED();
}
bool iterate_array_partial_children() {
TEST_START();
auto json = R"(
[
0,
[],
{},
{ "x": 3, "y": 33 },
{ "x": 4, "y": 44 },
{ "x": 5, "y": 55 },
{ "x": 6, "y": 66 },
[ 7, 77, 777 ],
[ 8, 88, 888 ],
{ "a": [ { "b": [ 9, 99 ], "c": 999 }, 9999 ], "d": 99999 },
10
]
)"_padded;
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](auto doc_result) {
size_t i = 0;
for (auto value : doc_result) {
ASSERT_SUCCESS(value);
switch (i) {
case 0: {
std::cout << " - After ignoring empty scalar ..." << std::endl;
break;
}
case 1: {
std::cout << " - After ignoring empty array ..." << std::endl;
break;
}
case 2: {
std::cout << " - After ignoring empty object ..." << std::endl;
break;
}
// Break after using first value in child object
case 3: {
for (auto child_field : value.get_object()) {
singlestage::raw_json_string k;
ASSERT_SUCCESS( child_field.key().get(k) );
ASSERT_EQUAL(k, "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;
}
// Break without using first value in child object
case 4: {
for (auto child_field : value.get_object()) {
singlestage::raw_json_string k;
ASSERT_SUCCESS( child_field.key().get(k) );
ASSERT_EQUAL(k, "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
break;
}
// Only look up one field in child object
case 5: {
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;
break;
}
// Only look up one field in child object, but don't use it
case 6: {
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
break;
}
// Break after first value in child array
case 7: {
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;
}
// Break without using first value in child array
case 8: {
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;
}
// Break out of multiple child loops
case 9: {
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;
break;
}
// Test the actual value
case 10: {
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
break;
}
}
i++;
}
ASSERT_EQUAL( i, 11 ); // Make sure we found all the keys we expected
return true;
}));
return true;
}
bool iterate_empty_array() {
TEST_START();
auto json = "[]"_padded;
SUBTEST("singlestage::array", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_SUCCESS( doc_result.get(array) );
for (simdjson_unused auto value : array) { TEST_FAIL("Unexpected value"); }
return true;
}));
SUBTEST("simdjson_result<singlestage::array>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::array> array_result = doc_result.get_array();
for (simdjson_unused auto value : array_result) { TEST_FAIL("Unexpected value"); }
return true;
}));
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
for (simdjson_unused auto value : doc) { TEST_FAIL("Unexpected value"); }
return true;
}));
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](auto doc_result) {
for (simdjson_unused auto value : doc_result) { TEST_FAIL("Unexpected value"); }
return true;
}));
SUBTEST("singlestage::array-document-rewind", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t i = 0;
for (auto value : array) { (void) value; i++; }
ASSERT_EQUAL(i, 0);
doc_result.rewind();
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
i = 0;
for (auto value : array) { (void) value; i++; }
ASSERT_EQUAL(i, 0);
return true;
}));
SUBTEST("singlestage::array-rewind", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) );
size_t i = 0;
for (auto value : array) { (void) value; i++; }
ASSERT_EQUAL(i, 0);
array.reset();
i = 0;
for (auto value : array) { (void) value; i++; }
ASSERT_EQUAL(i, 0);
return true;
}));
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool iterate_array_exception() {
TEST_START();
auto json = R"([ 1, 10, 100 ])"_padded;
const uint64_t expected_value[] = { 1, 10, 100 };
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
size_t i=0;
for (int64_t actual : doc_result) { ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true;
}));
TEST_SUCCEED();
}
bool iterate_empty_object_exception() {
TEST_START();
auto json = R"({})"_padded;
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
for (simdjson_unused singlestage::field field : doc_result.get_object()) {
TEST_FAIL("Unexpected field");
}
return true;
}));
TEST_SUCCEED();
}
bool iterate_empty_array_exception() {
TEST_START();
auto json = "[]"_padded;
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
for (simdjson_unused singlestage::value value : doc_result) { TEST_FAIL("Unexpected value"); }
return true;
}));
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
issue1977() &&
issue1876() &&
issue1742() &&
empty_rewind_convoluted() &&
empty_rewind() &&
iterate_empty_array_count() &&
iterate_sub_array_count() &&
iterate_complex_array_count() &&
iterate_bad_array_count() &&
iterate_array_count() &&
issue1588() &&
iterate_array() &&
iterate_document_array_count() &&
iterate_bad_document_array_count() &&
iterate_document_array() &&
iterate_empty_array() &&
iterate_array_partial_children() &&
#if SIMDJSON_EXCEPTIONS
empty_rewind_convoluted_with_exceptions() &&
iterate_array_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace dom_api_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, array_tests::run);
}
@@ -0,0 +1,80 @@
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "simdjson.h"
// We get spurious "maybe used uninitialized" warnings under GCC 12.
using namespace simdjson;
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ >= 12
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#endif
#endif
// This ensures the compiler can't rearrange them into the proper order (which causes it to work!)
simdjson_never_inline bool check_point(simdjson_result<singlestage::value> xval, simdjson_result<singlestage::value> yval) {
// Verify the expected release behavior
uint64_t x, y;
if (!xval.get(x)) { return false; }
if (!yval.get(y)) { return false; }
std::cout << x << "," << y << std::endl;
return true;
}
bool test_check_point() {
auto json = R"(
{
"x": 1,
"y": 2 3
)"_padded;
singlestage::parser parser;
auto doc = parser.iterate(json);
return check_point(doc["x"], doc["y"]);
}
// Run a test function in a fork and check whether it exited with an assert signal
template<typename F>
bool assert_test(const F& f) {
pid_t pid = fork();
if (pid == -1) {
std::cerr << "fork failed" << std::endl;
exit(1);
}
if (!pid) {
//
// This code runs in the fork (so we run the test function)
//
bool succeeded = f();
exit(succeeded ? 0 : 1);
}
//
// This code runs in the original executable (so we wait for the fork and check the exit code)
//
int exit_code = 0;
if (waitpid(pid, &exit_code, 0) == pid_t(-1)) {
std::cerr << "waitpid failed: " << std::string_view(strerror(errno)) << std::endl;
exit(1);
}
// Test passes if the child exited with an assert signal
return WIFSIGNALED(exit_code);
}
int main(void) {
// To verify that the program asserts (which sends a signal), we fork a new process and use wait()
// to check the resulting error code. If it's a signal error code, we consider the
// test to have passed.
// From https://stackoverflow.com/a/33694733
bool succeeded = true;
#ifndef NDEBUG
succeeded |= assert_test(test_check_point);
#endif
return succeeded ? 0 : 1;
}
@@ -0,0 +1,66 @@
#include <set>
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
#if SIMDJSON_EXCEPTIONS
// bogus functions for compilation tests
void process1(int ) {}
void process2(int ) {}
void process3(int ) {}
// Do not run this, it is only meant to compile
void compilation_test_1() {
const padded_string bogus = ""_padded;
singlestage::parser parser;
auto doc = parser.iterate(bogus);
for (singlestage::object my_object : doc["mykey"]) {
for (auto field : my_object) {
if (field.key() == "key_value1") { process1(field.value()); }
else if (field.key() == "key_value2") { process2(field.value()); }
else if (field.key() == "key_value3") { process3(field.value()); }
}
}
}
// Do not run this, it is only meant to compile
void compilation_test_2() {
const padded_string bogus = ""_padded;
singlestage::parser parser;
auto doc = parser.iterate(bogus);
std::set<std::string_view> default_users;
singlestage::array tweets = doc["statuses"].get_array();
for (auto tweet_value : tweets) {
auto tweet = tweet_value.get_object();
singlestage::object user = tweet["user"].get_object();
std::string_view screen_name = user["screen_name"].get_string();
bool default_profile = user["default_profile"].get_bool();
if (default_profile) { default_users.insert(screen_name); }
}
}
// Do not run this, it is only meant to compile
void compilation_test_3() {
const padded_string bogus = ""_padded;
singlestage::parser parser;
auto doc = parser.iterate(bogus);
singlestage::array tweets;
if(! doc["statuses"].get(tweets)) { return; }
for (auto tweet_value : tweets) {
auto tweet = tweet_value.get_object();
for (auto field : tweet) {
std::string_view key = field.unescaped_key().value();
std::cout << "key = " << key << std::endl;
std::string_view val = std::string_view(field.value());
std::cout << "value (assuming it is a string) = " << val << std::endl;
}
}
}
#endif // SIMDJSON_EXCEPTIONS
int main(void) {
return 0;
}
@@ -0,0 +1,844 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace document_stream_tests {
template <typename T>
bool process_doc(T &docref) {
int64_t val{};
ASSERT_SUCCESS(docref.at_pointer("/4").get(val));
ASSERT_EQUAL(val, 5);
return true;
}
bool truncated_utf8() {
TEST_START();
// truncated UTF-8
auto json = "\"document1\" \xC3"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
auto oderror = parser.iterate_many(json).get(stream);
if (oderror) {
std::cerr << "singlestage iterate_many error: " << oderror << std::endl;
return false;
}
int document_index = 0;
auto i = stream.begin();
for (; i != stream.end(); ++i) {
singlestage::document_reference doc;
auto err = (*i).get(doc);
document_index++;
// With the fallback routine, we might detect a 'document' since
// it does not do UTF-8 validation in stage one, but it will do
// so when we access the document.
if(err == SUCCESS) {
singlestage::json_type t;
err = doc.type().get(t);
}
if((err == SUCCESS) && (document_index != 1)) {
std::cerr << "Only the first document should be valid." << std::endl;
return false;
}
if((err != SUCCESS) && (document_index != 2)) {
std::cerr << "singlestage iterate_many error: " << err << std::endl;
return false;
}
}
if(document_index < 1) {
std::cerr << "singlestage should have accessed at least one document" << std::endl;
return false;
}
TEST_SUCCEED();
}
bool issue1729() {
// This example is not a good application of iterate_many since there is
// a single JSON document.
TEST_START();
auto json = R"({
"t": 5649,
"ng": 5,
"lu": 4086,
"g": [{
"t": "Temps",
"xvy": 0,
"pd": 60,
"sz": 3,
"l": [
"Low Temp",
"High Temp",
"Smooth Avg"
],
"c": [
"green",
"orange",
"cyan"
],
"d": [
80.48750305,
80.82499694,
80.65625
]
},
{
"t": "Pump Status",
"xvy": 0,
"pd": 60,
"sz": 3,
"l": [
"Pump",
"Thermal",
"Light"
],
"c": [
"green",
"orange",
"cyan"
],
"d": [
0,
0,
0
]
},
{
"t": "Lux",
"xvy": 0,
"pd": 60,
"sz": 4,
"l": [
"Value",
"Smooth",
"Low",
"High"
],
"c": [
"green",
"orange",
"cyan",
"yellow"
],
"d": [
2274.62939453,
2277.45947265,
4050,
4500
]
},
{
"t": "Temp Diff",
"xvy": 0,
"pd": 60,
"sz": 4,
"l": [
"dFarenheit",
"sum",
"Low",
"High"
],
"c": [
"green",
"orange",
"cyan",
"yellow"
],
"d": [
0,
0,
0.5,
10
]
},
{
"t": "Power",
"xvy": 0,
"pd": 60,
"sz": 3,
"l": [
"watts (est. light) ",
"watts (1.9~gpm) ",
"watts (1gpm) "
],
"c": [
"green",
"orange",
"cyan"
],
"d": [
181.78063964,
114.88922882,
59.35943603
]
}
]
})"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
auto oderror = parser.iterate_many(json).get(stream);
if (oderror) {
std::cerr << "singlestage iterate_many error: " << oderror << std::endl;
return false;
}
auto i = stream.begin();
for (; i != stream.end(); ++i) {
singlestage::document_reference doc;
auto err = (*i).get(doc);
if(err != SUCCESS) {
std::cerr << "singlestage iterate_many error: " << err << std::endl;
return false;
}
}
TEST_SUCCEED();
}
bool issue1977() {
TEST_START();
std::string json = R"( 1111 })";
singlestage::parser odparser;
singlestage::document_stream odstream;
ASSERT_SUCCESS(odparser.iterate_many(json).get(odstream));
auto i = odstream.begin();
for (; i != odstream.end(); ++i) {
ASSERT_TRUE(false);
}
ASSERT_TRUE(i.current_index() == 0);
TEST_SUCCEED();
}
bool issue1683() {
TEST_START();
std::string json = R"([1,2,3,4,5]
[1,2,3,4,5]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]
[1,2,3,4,5])";
singlestage::parser odparser;
singlestage::document_stream odstream;
// iterate_many all at once
auto oderror = odparser.iterate_many(json, 50).get(odstream);
if (oderror) { std::cerr << "singlestage iterate_many error: " << oderror << std::endl; return false; }
size_t currindex = 0;
auto i = odstream.begin();
for (; i != odstream.end(); ++i) {
singlestage::document_reference doc;
auto err = (*i).get(doc);
if(err == SUCCESS) { if(!process_doc(doc)) {return false; } }
currindex = i.current_index();
if (err == simdjson::CAPACITY) {
ASSERT_EQUAL(currindex, 24);
ASSERT_EQUAL(odstream.truncated_bytes(), 305);
break;
} else if (err) {
TEST_FAIL(std::string("singlestage: error accessing jsonpointer: ") + simdjson::error_message(err));
}
}
ASSERT_EQUAL(odstream.truncated_bytes(), 305);
// iterate line-by-line
std::stringstream ss(json);
std::string oneline;
oneline.reserve(json.size() + SIMDJSON_PADDING);
while (getline(ss, oneline)) {
singlestage::document doc;
ASSERT_SUCCESS(odparser.iterate(oneline).get(doc));
if( ! process_doc(doc) ) { return false; }
}
TEST_SUCCEED();
}
bool simple_document_iteration() {
TEST_START();
auto json = R"([1,[1,2]] {"a":1,"b":2} {"o":{"1":1,"2":2}} [1,2,3])"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
size_t counter{0};
for(auto doc : stream) {
ASSERT_TRUE(counter < 4);
std::string_view view;
ASSERT_SUCCESS(to_json_string(doc).get(view));
ASSERT_EQUAL(view, expected[counter++]);
}
ASSERT_EQUAL(counter, 4);
TEST_SUCCEED();
}
bool simple_document_iteration_multiple_batches() {
TEST_START();
auto json = R"([1,[1,2]] {"a":1,"b":2} {"o":{"1":1,"2":2}} [1,2,3])"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json,32).get(stream));
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
size_t counter{0};
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_TRUE(counter < 4);
ASSERT_EQUAL(i.source(), expected[counter++]);
}
ASSERT_EQUAL(counter, 4);
TEST_SUCCEED();
}
bool simple_document_iteration_with_parsing() {
TEST_START();
auto json = R"([1,[1,2]] {"a":1,"b":2} {"o":{"1":1,"2":2}} [1,2,3])"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
size_t counter{0};
auto i = stream.begin();
int64_t x;
ASSERT_EQUAL(i.source(),expected[counter++]);
ASSERT_SUCCESS( (*i).at_pointer("/1/1").get(x) );
ASSERT_EQUAL(x,2);
++i;
ASSERT_EQUAL(i.source(),expected[counter++]);
simdjson_result<singlestage::document_reference> xxx = *i;
ASSERT_SUCCESS( xxx.find_field("a").get(x) );
ASSERT_EQUAL(x,1);
++i;
ASSERT_EQUAL(i.source(),expected[counter++]);
ASSERT_SUCCESS( (*i).at_pointer("/o/2").get(x) );
ASSERT_EQUAL(x,2);
++i;
ASSERT_EQUAL(i.source(),expected[counter++]);
ASSERT_SUCCESS( (*i).at_pointer("/2").get(x) );
ASSERT_EQUAL(x,3);
++i;
if (i != stream.end()) { return false; }
TEST_SUCCEED();
}
bool atoms_json() {
TEST_START();
auto json = R"(5 true 20.3 "string" )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
std::string_view expected[4] = {"5", "true", "20.3", "\"string\""};
size_t counter{0};
for (auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_EQUAL(i.source(), expected[counter++]);
}
ASSERT_EQUAL(counter,4);
TEST_SUCCEED();
}
bool doc_index() {
TEST_START();
auto json = R"({"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311])"_padded;
std::string_view expected[5] = {R"({"z":5})",R"({"1":1,"2":2,"4":4})","[7, 10, 9]","[15, 11, 12, 13]","[154, 110, 112, 1311]"};
size_t expected_indexes[5] = {0, 9, 29, 44, 65};
singlestage::parser parser;
singlestage::document_stream stream;
size_t counter{0};
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_TRUE(counter < 5);
ASSERT_EQUAL(i.current_index(), expected_indexes[counter]);
ASSERT_EQUAL(i.source(), expected[counter]);
counter++;
}
ASSERT_EQUAL(counter, 5);
TEST_SUCCEED();
}
bool doc_index_multiple_batches() {
TEST_START();
auto json = R"({"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311])"_padded;
std::string_view expected[5] = {R"({"z":5})",R"({"1":1,"2":2,"4":4})","[7, 10, 9]","[15, 11, 12, 13]","[154, 110, 112, 1311]"};
size_t expected_indexes[5] = {0, 9, 29, 44, 65};
singlestage::parser parser;
singlestage::document_stream stream;
size_t counter{0};
ASSERT_SUCCESS(parser.iterate_many(json,32).get(stream));
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_TRUE(counter < 5);
ASSERT_EQUAL(i.current_index(), expected_indexes[counter]);
ASSERT_EQUAL(i.source(), expected[counter]);
counter++;
}
ASSERT_EQUAL(counter, 5);
TEST_SUCCEED();
}
bool source_test() {
TEST_START();
auto json = R"([1,[1,2]] {"a":1,"b":2} {"o":{"1":1,"2":2}} [1,2,3] )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
size_t counter{0};
for (auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_EQUAL(i.source(), expected[counter++]);
}
ASSERT_EQUAL(counter,4);
TEST_SUCCEED();
}
bool truncated() {
TEST_START();
// The last JSON document is intentionally truncated.
auto json = R"([1,2,3] {"1":1,"2":3,"4":4} [1,2 )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
size_t counter{0};
for (auto i = stream.begin(); i != stream.end(); ++i) {
counter++;
}
size_t truncated = stream.truncated_bytes();
ASSERT_EQUAL(truncated, 6);
ASSERT_EQUAL(counter,2);
TEST_SUCCEED();
}
bool truncated_complete_docs() {
TEST_START();
auto json = R"([1,2,3] {"1":1,"2":3,"4":4} [1,2] )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
size_t counter{0};
for (auto i = stream.begin(); i != stream.end(); ++i) {
counter++;
}
size_t truncated = stream.truncated_bytes();
ASSERT_EQUAL(truncated, 0);
ASSERT_EQUAL(counter,3);
TEST_SUCCEED();
}
bool truncated_unclosed_string() {
TEST_START();
// The last JSON document is intentionally truncated.
auto json = R"([1,2,3] {"1":1,"2":3,"4":4} "intentionally unclosed string )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
// We use a window of json.size() though any large value would do.
ASSERT_SUCCESS( parser.iterate_many(json).get(stream) );
size_t counter{0};
for(auto i = stream.begin(); i != stream.end(); ++i) {
counter++;
}
size_t truncated = stream.truncated_bytes();
ASSERT_EQUAL(counter,2);
ASSERT_EQUAL(truncated,32);
TEST_SUCCEED();
}
bool truncated_unclosed_string_in_object() {
// The last JSON document is intentionally truncated.
auto json = R"([1,2,3] {"1":1,"2":3,"4":4} {"key":"intentionally unclosed string )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS( parser.iterate_many(json).get(stream) );
size_t counter{0};
for(auto i = stream.begin(); i != stream.end(); ++i) {
counter++;
}
size_t truncated = stream.truncated_bytes();
ASSERT_EQUAL(counter,2);
ASSERT_EQUAL(truncated,39);
TEST_SUCCEED();
}
bool small_window() {
TEST_START();
std::vector<char> input;
input.push_back('[');
for(size_t i = 1; i < 1024; i++) {
input.push_back('1');
input.push_back(i < 1023 ? ',' : ']');
}
auto json = simdjson::padded_string(input.data(),input.size());
singlestage::parser parser;
size_t window_size{1024}; // deliberately too small
singlestage::document_stream stream;
ASSERT_SUCCESS( parser.iterate_many(json, window_size).get(stream) );
auto i = stream.begin();
ASSERT_ERROR(i.error(), CAPACITY);
ASSERT_EQUAL(stream.truncated_bytes(), json.length());
TEST_SUCCEED();
}
bool large_window() {
TEST_START();
#if SIZE_MAX > 17179869184
auto json = R"({"error":[],"result":{"token":"xxx"}}{"error":[],"result":{"token":"xxx"}})"_padded;
singlestage::parser parser;
uint64_t window_size{17179869184}; // deliberately too big
singlestage::document_stream stream;
ASSERT_SUCCESS( parser.iterate_many(json, size_t(window_size)).get(stream) );
auto i = stream.begin();
ASSERT_ERROR(i.error(),CAPACITY);
#endif
TEST_SUCCEED();
}
bool test_leading_spaces() {
TEST_START();
auto input = R"( [1,1] [1,2] [1,3] [1,4] [1,5] [1,6] [1,7] [1,8] [1,9] [1,10] [1,11] [1,12] [1,13] [1,14] [1,15] )"_padded;;
size_t count{0};
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(input, 32).get(stream));
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_SUCCESS(i.error());
count++;
}
ASSERT_EQUAL(count,15);
TEST_SUCCEED();
}
bool test_crazy_leading_spaces() {
TEST_START();
auto input = R"( [1,1] [1,2] [1,3] [1,4] [1,5] [1,6] [1,7] [1,8] [1,9] [1,10] [1,11] [1,12] [1,13] [1,14] [1,15] )"_padded;;
size_t count{0};
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(input, 32).get(stream));
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_SUCCESS(i.error());
count++;
}
ASSERT_EQUAL(count,15);
TEST_SUCCEED();
}
bool adversarial_single_document() {
TEST_START();
auto json = R"({"f[)"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
size_t count{0};
for (auto doc : stream) {
(void)doc;
count++;
}
ASSERT_EQUAL(count,0);
TEST_SUCCEED();
}
bool adversarial_single_document_array() {
TEST_START();
auto json = R"(["this is an unclosed string ])"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
size_t count{0};
for (auto doc : stream) {
(void)doc;
count++;
}
ASSERT_EQUAL(count,0);
TEST_SUCCEED();
}
bool document_stream_test() {
TEST_START();
fflush(NULL);
const size_t n_records = 100;
std::string data;
std::vector<char> buf(1024);
// Generating data
for (size_t i = 0; i < n_records; ++i) {
size_t n = snprintf(buf.data(),
buf.size(),
"{\"id\": %zu, \"name\": \"name%zu\", \"gender\": \"%s\", "
"\"ete\": {\"id\": %zu, \"name\": \"eventail%zu\"}}",
i, i, (i % 2) ? "homme" : "femme", i % 10, i % 10);
if (n >= buf.size()) { abort(); }
data += std::string(buf.data(), n);
}
for(size_t batch_size = 1000; batch_size < 2000; batch_size += (batch_size>1050?10:1)) {
fflush(NULL);
simdjson::padded_string str(data);
singlestage::parser parser;
singlestage::document_stream stream;
size_t count{0};
ASSERT_SUCCESS( parser.iterate_many(str, batch_size).get(stream) );
for (auto doc : stream) {
int64_t keyid{};
ASSERT_SUCCESS( doc["id"].get(keyid) );
ASSERT_EQUAL( keyid, int64_t(count) );
count++;
}
ASSERT_EQUAL(count,n_records);
}
TEST_SUCCEED();
}
bool issue1668() {
TEST_START();
auto json = R"([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100])"_padded;
singlestage::parser odparser;
singlestage::document_stream odstream;
ASSERT_SUCCESS( odparser.iterate_many(json.data(), json.length(), 50).get(odstream) );
for (auto doc: odstream) {
singlestage::value val;
ASSERT_ERROR(doc.at_pointer("/40").get(val), CAPACITY);
ASSERT_EQUAL(odstream.truncated_bytes(), json.length());
}
TEST_SUCCEED();
}
bool issue1668_long() {
TEST_START();
auto json = R"([1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100])"_padded;
singlestage::parser odparser;
singlestage::document_stream odstream;
size_t counter{0};
ASSERT_SUCCESS( odparser.iterate_many(json.data(), json.length(), 50).get(odstream) );
for (auto doc: odstream) {
if(counter < 6) {
int64_t val{};
ASSERT_SUCCESS(doc.at_pointer("/4").get(val));
ASSERT_EQUAL(val, 5);
} else {
singlestage::value val;
ASSERT_ERROR(doc.at_pointer("/4").get(val), CAPACITY);
// We left 293 bytes unprocessed.
ASSERT_EQUAL(odstream.truncated_bytes(), 293);
}
counter++;
}
TEST_SUCCEED();
}
bool document_stream_utf8_test() {
TEST_START();
fflush(NULL);
const size_t n_records = 100;
std::string data;
std::vector<char> buf(1024);
// Generating data
for (size_t i = 0; i < n_records; ++i) {
size_t n = snprintf(buf.data(),
buf.size(),
"{\"id\": %zu, \"name\": \"name%zu\", \"gender\": \"%s\", "
"\"\xC3\xA9t\xC3\xA9\": {\"id\": %zu, \"name\": \"\xC3\xA9ventail%zu\"}}",
i, i, (i % 2) ? "\xE2\xBA\x83" : "\xE2\xBA\x95", i % 10, i % 10);
if (n >= buf.size()) { abort(); }
data += std::string(buf.data(), n);
}
for(size_t batch_size = 1000; batch_size < 2000; batch_size += (batch_size>1050?10:1)) {
fflush(NULL);
simdjson::padded_string str(data);
singlestage::parser parser;
singlestage::document_stream stream;
size_t count{0};
ASSERT_SUCCESS( parser.iterate_many(str, batch_size).get(stream) );
for (auto doc : stream) {
int64_t keyid{};
ASSERT_SUCCESS( doc["id"].get(keyid) );
ASSERT_EQUAL( keyid, int64_t(count) );
count++;
}
ASSERT_EQUAL( count, n_records )
}
TEST_SUCCEED();
}
bool stress_data_race() {
TEST_START();
// Correct JSON.
auto input = R"([1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] )"_padded;;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(input, 32).get(stream));
for(auto i = stream.begin(); i != stream.end(); ++i) {
ASSERT_SUCCESS(i.error());
}
TEST_SUCCEED();
}
bool stress_data_race_with_error() {
TEST_START();
#if SIMDJSON_THREAD_ENABLED
std::cout << "ENABLED" << std::endl;
#endif
// Intentionally broken
auto input = R"([1,23] [1,23] [1,23] [1,23 [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] [1,23] )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(input, 32).get(stream));
size_t count{0};
for(auto i = stream.begin(); i != stream.end(); ++i) {
auto error = i.error();
if(count <= 3) {
ASSERT_SUCCESS(error);
} else {
ASSERT_ERROR(error,TAPE_ERROR);
break;
}
count++;
}
TEST_SUCCEED();
}
bool fuzzaccess() {
TEST_START();
// Issue 38801 in oss-fuzz
auto json = "\xff \n~~\n{}"_padded;
singlestage::parser parser;
singlestage::document_stream docs;
ASSERT_SUCCESS(parser.iterate_many(json).get(docs));
size_t bool_count = 0;
size_t total_count = 0;
for (auto doc : docs) {
total_count++;
bool b;
if(doc.get_bool().get(b) == SUCCESS) { bool_count +=b; }
// If we don't access the document at all, other than get_bool(),
// then some simdjson kernels will allow you to iterate through
// 3 'documents'. By asking for the type, we make the iteration
// terminate after the first 'document'.
singlestage::json_type t;
if(doc.type().get(t) != SUCCESS) { break; }
}
return (bool_count == 0) && (total_count == 1);
}
bool string_with_trailing() {
TEST_START();
auto json = R"( "a string" badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
std::string_view view;
ASSERT_SUCCESS((*doc.begin()).get_string().get(view));
ASSERT_EQUAL(view,"a string");
TEST_SUCCEED();
}
bool uint64_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
uint64_t x;
ASSERT_SUCCESS((*doc.begin()).get_uint64().get(x));
ASSERT_EQUAL(x,133);
TEST_SUCCEED();
}
bool int64_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
int64_t x;
ASSERT_SUCCESS((*doc.begin()).get_int64().get(x));
ASSERT_EQUAL(x,133);
TEST_SUCCEED();
}
bool double_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
double x;
ASSERT_SUCCESS((*doc.begin()).get_double().get(x));
ASSERT_EQUAL(x,133);
TEST_SUCCEED();
}
bool bool_with_trailing() {
TEST_START();
auto json = R"( true badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
bool x;
ASSERT_SUCCESS((*doc.begin()).get_bool().get(x));
ASSERT_EQUAL(x,true);
TEST_SUCCEED();
}
bool null_with_trailing() {
TEST_START();
auto json = R"( null badstuff )"_padded;
singlestage::parser parser;
singlestage::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
bool n;
ASSERT_SUCCESS((*doc.begin()).is_null().get(n));
ASSERT_EQUAL(n,true);
TEST_SUCCEED();
}
bool run() {
return
issue1977() &&
string_with_trailing() &&
uint64_with_trailing() &&
int64_with_trailing() &&
bool_with_trailing() &&
null_with_trailing() &&
truncated_utf8() &&
issue1729() &&
fuzzaccess() &&
issue1683() &&
issue1668() &&
issue1668_long() &&
simple_document_iteration() &&
simple_document_iteration_multiple_batches() &&
simple_document_iteration_with_parsing() &&
atoms_json() &&
doc_index() &&
doc_index_multiple_batches() &&
source_test() &&
truncated() &&
truncated_complete_docs() &&
truncated_unclosed_string() &&
small_window() &&
large_window() &&
test_leading_spaces() &&
test_crazy_leading_spaces() &&
adversarial_single_document() &&
adversarial_single_document_array() &&
document_stream_test() &&
document_stream_utf8_test() &&
stress_data_race() &&
stress_data_race_with_error() &&
true;
}
} // document_stream_tests
int main (int argc, char *argv[]) {
return test_main(argc, argv, document_stream_tests::run);
}
@@ -0,0 +1,289 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace error_location_tests {
bool array() {
TEST_START();
auto json = R"( [1,2,3] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::vector<char> expected = {'1','2','3'};
std::vector<int64_t> expected_values = {1,2,3};
size_t count{0};
for (auto value : doc) {
int64_t i;
const char* c;
// Must call current_location first because get_int64() will consume values
ASSERT_SUCCESS(doc.current_location().get(c));
ASSERT_EQUAL(*c, expected[count]);
ASSERT_SUCCESS(value.get_int64().get(i));
ASSERT_EQUAL(i, expected_values[count]);
count++;
}
ASSERT_EQUAL(count,3);
ASSERT_ERROR(doc.current_location(), OUT_OF_BOUNDS);
TEST_SUCCEED();
}
bool object() {
TEST_START();
auto json = R"( {"a":1, "b":2, "c":3} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::vector<char> expected = {'1','2','3'};
std::vector<int64_t> expected_values = {1,2,3};
size_t count{0};
for (auto field : doc.get_object()) {
int64_t i;
const char* c;
// Must call current_location first because get_int64() will consume values
ASSERT_SUCCESS(doc.current_location().get(c));
ASSERT_EQUAL(*c,expected[count]);
ASSERT_SUCCESS(field.value().get(i));
ASSERT_EQUAL(i,expected_values[count]);
count++;
}
ASSERT_EQUAL(count,3);
ASSERT_ERROR(doc.current_location(), OUT_OF_BOUNDS);
TEST_SUCCEED();
}
bool json_pointer() {
TEST_START();
auto json = R"( {"a": [1,2,[3,4,5]], "b": {"c": [1.2, 2.3]}} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
uint64_t i;
ASSERT_SUCCESS(doc.at_pointer("/a/2/1").get(i));
ASSERT_EQUAL(i, 4);
ASSERT_SUCCESS(doc.current_location().get(ptr));
std::string expected = ",5]], \"b\": {\"c\": [1.2, 2.3]}} ";
ASSERT_EQUAL(std::string(ptr, expected.size()), expected);
double d;
ASSERT_SUCCESS(doc.at_pointer("/b/c/1").get(d));
ASSERT_EQUAL(d, 2.3);
ASSERT_SUCCESS(doc.current_location().get(ptr));
expected = "]}} ";
ASSERT_EQUAL(std::string(ptr, expected.size()), expected);
TEST_SUCCEED();
}
bool json_pointer_with_errors() {
TEST_START();
auto json = R"( {"a": [1,2,[3 4,5]], "b": {"c": [1.2., 2.3]}} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
double d;
ASSERT_ERROR(doc.at_pointer("/b/c/0").get(d), NUMBER_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
std::string expected = "1.2., 2.3]}} ";
ASSERT_EQUAL(std::string(ptr, expected.size()), expected);
uint64_t i;
ASSERT_ERROR(doc.at_pointer("/a/2/1").get(i), TAPE_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
expected = "4,5]], \"b\": {\"c\": [1.2., 2.3]}} ";
ASSERT_EQUAL(std::string(ptr, expected.size()), expected);
TEST_SUCCEED();
}
bool broken_json1() {
TEST_START();
auto json = " \xc3\x94\xc3\xb8\xe2\x84\xa6{\"a\":1, 3} "_padded;
singlestage::parser parser;
singlestage::document doc;
const char * ptr;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc["a"], INCORRECT_TYPE);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(std::string(ptr, 18), "\xc3\x94\xc3\xb8\xe2\x84\xa6{\"a\":1, 3} ");
TEST_SUCCEED();
}
bool broken_json2() {
TEST_START();
auto json = R"( [[[]] )"_padded;
singlestage::parser parser;
singlestage::document doc;
singlestage::array arr;
const char * ptr;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.get_array().get(arr));
ASSERT_ERROR(arr.count_elements(), TAPE_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(std::string(ptr - 2,2), "] ");
TEST_SUCCEED();
}
bool broken_json3() {
TEST_START();
auto json = R"( [1 1.23, 2] )"_padded;
singlestage::parser parser;
singlestage::document doc;
size_t count{0};
const char * ptr;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
for (auto val : doc) {
if (count == 1) {
ASSERT_ERROR(val, TAPE_ERROR);
break;
}
count++;
}
ASSERT_EQUAL(count, 1);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(std::string(ptr, strlen("1.23, 2] ")), "1.23, 2] ");
TEST_SUCCEED();
}
bool broken_json4() {
TEST_START();
auto json = R"( {"a":1, 3.5, "b":5} )"_padded;
singlestage::parser parser;
singlestage::document doc;
const char * ptr;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc["b"], TAPE_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(std::string(ptr, strlen("3.5, \"b\":5} ")), "3.5, \"b\":5} ");
TEST_SUCCEED();
}
bool incomplete_json() {
TEST_START();
auto json = R"( [1,2,3 )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
for (auto val : doc) {
ASSERT_ERROR(val, INCOMPLETE_ARRAY_OR_OBJECT);
}
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(std::string(ptr, strlen("[1,2,3 ")), "[1,2,3 ");
TEST_SUCCEED();
}
bool boolean_error() {
TEST_START();
auto json = R"( [tru, fals] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
for (auto val : doc) {
bool b;
ASSERT_ERROR(val.get(b), INCORRECT_TYPE);
}
ASSERT_ERROR(doc.current_location(), OUT_OF_BOUNDS);
TEST_SUCCEED();
}
bool no_such_field() {
TEST_START();
auto json = R"( {"a":5, "b":4} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc["c"], NO_SUCH_FIELD);
ASSERT_ERROR(doc.current_location(), OUT_OF_BOUNDS);
TEST_SUCCEED();
}
bool object_with_no_such_field() {
TEST_START();
auto json = R"( {"a":5, "b":4} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
uint64_t i;
ASSERT_SUCCESS(doc["a"].get(i));
ASSERT_EQUAL(i, 5);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(ptr, ", \"b\":4} ");
ASSERT_ERROR(doc["c"], NO_SUCH_FIELD);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(ptr, ", \"b\":4} ");
TEST_SUCCEED();
}
bool number_parsing_error() {
TEST_START();
auto json = R"( [13.34.514] )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
double d;
ASSERT_ERROR(doc.at_pointer("/0").get_double().get(d), NUMBER_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(ptr, "13.34.514] ");
TEST_SUCCEED();
}
bool number_parsing_root_error() {
TEST_START();
auto json = R"( 13.34.514 )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
const char * ptr;
double d;
ASSERT_ERROR(doc.get_double().get(d), NUMBER_ERROR);
ASSERT_SUCCESS(doc.current_location().get(ptr));
ASSERT_EQUAL(ptr, "13.34.514 ");
TEST_SUCCEED();
}
bool current_location_in_value() {
TEST_START();
auto json = R"( {"a": {"b": "c"}} )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
simdjson_result<singlestage::value> a = doc["a"];
ASSERT_SUCCESS(a);
const char * ptr;
ASSERT_SUCCESS(a.current_location().get(ptr));
ASSERT_EQUAL(ptr, R"({"b": "c"}} )");
simdjson_result<singlestage::value> b = a["b"];
ASSERT_SUCCESS(b);
ASSERT_SUCCESS(b.current_location().get(ptr));
ASSERT_EQUAL(ptr, R"("c"}} )");
double d;
ASSERT_ERROR(b.get_double().get(d), INCORRECT_TYPE);
ASSERT_SUCCESS(b.current_location().get(ptr));
ASSERT_EQUAL(ptr, R"("c"}} )");
TEST_SUCCEED();
}
bool run() {
return array() &&
object() &&
json_pointer() &&
json_pointer_with_errors() &&
broken_json1() &&
broken_json2() &&
broken_json3() &&
broken_json4() &&
incomplete_json() &&
boolean_error() &&
no_such_field() &&
object_with_no_such_field() &&
number_parsing_error() &&
number_parsing_root_error() &&
current_location_in_value() &&
true;
}
} // namespace error_location_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, error_location_tests::run);
}
@@ -0,0 +1,393 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace error_tests {
using namespace std;
bool badbadjson() {
TEST_START();
singlestage::parser parser;
auto json_string = R"({
"main": "therain"_"in_spain"_
})"_padded;
singlestage::document document;
auto error = parser.iterate(json_string).get(document);
if(error != simdjson::SUCCESS) {
return false;
}
singlestage::object obj;
error = document.get_object().get(obj);
if(error != simdjson::SUCCESS) {
return false;
}
std::string_view name_value{};
error = obj["name"].get_string().get(name_value);
ASSERT_ERROR(error,TAPE_ERROR);
// Check for "main" field
std::string_view main_value{};
error = obj["main"].get_string().get(main_value);
ASSERT_ERROR(error,TAPE_ERROR);
TEST_SUCCEED();
}
bool badbadjson2() {
TEST_START();
singlestage::parser parser;
auto json_string = R"({
"main": "therain"_"in_spain"_
})"_padded;
singlestage::document document;
auto error = parser.iterate(json_string).get(document);
if(error != simdjson::SUCCESS) {
return false;
}
singlestage::object obj;
error = document.get_object().get(obj);
if(error != simdjson::SUCCESS) {
return false;
}
std::string_view main_value{};
error = obj["main"].get_string().get(main_value);
ASSERT_ERROR(error, simdjson::SUCCESS);
std::string_view name_value{};
error = obj["name"].get_string().get(name_value);
ASSERT_ERROR(error,TAPE_ERROR);
TEST_SUCCEED();
}
bool issue1834() {
TEST_START();
singlestage::parser parser;
auto json = "[[]"_padded;
json.data()[json.size()] = ']';
auto doc = parser.iterate(json);
size_t cnt{};
auto error = doc.count_elements().get(cnt);
return error != simdjson::SUCCESS;
}
bool issue1834_2() {
TEST_START();
singlestage::parser parser;
auto json = "{\"a\":{}"_padded;
json.data()[json.size()] = '}';
auto doc = parser.iterate(json);
size_t cnt{};
auto error = doc.count_fields().get(cnt);
return error != simdjson::SUCCESS;
}
bool empty_document_error() {
TEST_START();
singlestage::parser parser;
auto json = ""_padded;
ASSERT_ERROR( parser.iterate(json), EMPTY );
TEST_SUCCEED();
}
bool raw_json_string_error() {
TEST_START();
singlestage::parser parser;
auto json = "{\"haha\":{\"df2\":3.5, \"df3\": \"fd\"}}"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
singlestage::raw_json_string rawjson;
ASSERT_ERROR( doc.get_raw_json_string().get(rawjson), INCORRECT_TYPE );
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool raw_json_string_except() {
TEST_START();
singlestage::parser parser;
auto json = "{\"haha\":{\"df2\":3.5, \"df3\": \"fd\"}}"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
try {
singlestage::raw_json_string rawjson = doc.get_raw_json_string();
(void)rawjson;
TEST_FAIL("Should have thrown an exception!")
} catch(simdjson_error& e) {
ASSERT_ERROR(e.error(), INCORRECT_TYPE);
TEST_SUCCEED();
}
}
bool raw_json_string_except_with_io() {
TEST_START();
singlestage::parser parser;
auto json = "{\"haha\":{\"df2\":3.5, \"df3\": \"fd\"}}"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
try {
auto rawjson = doc.get_raw_json_string();
std::cout << rawjson;
TEST_FAIL("Should have thrown an exception!")
} catch(simdjson_error& e) {
ASSERT_ERROR(e.error(), INCORRECT_TYPE);
TEST_SUCCEED();
}
}
#endif
bool parser_max_capacity() {
TEST_START();
singlestage::parser parser(1); // max_capacity set to 1 byte
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
auto error = parser.iterate(json);
ASSERT_ERROR(error,CAPACITY);
TEST_SUCCEED();
}
bool simple_error_example() {
TEST_START();
singlestage::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
double x;
ASSERT_ERROR( doc["bad number"].get_double().get(x), NUMBER_ERROR );
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool simple_error_example_except() {
TEST_START();
singlestage::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded;
try {
singlestage::document doc = parser.iterate(json);
double x = doc["bad number"].get_double();
TEST_FAIL("should throw got: "+std::to_string(x))
} catch(simdjson_error& e) {
ASSERT_ERROR(e.error(), NUMBER_ERROR);
}
TEST_SUCCEED();
}
#endif
bool get_fail_then_succeed_bool() {
TEST_START();
auto json = R"({ "val" : true })"_padded;
SUBTEST("simdjson_result<singlestage::value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> val = doc["val"];
// Get everything that can fail in both forward and backwards order
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
SUBTEST("singlestage::value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value val;
ASSERT_SUCCESS( doc["val"].get(val) );
// Get everything that can fail in both forward and backwards order
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
json = R"(true)"_padded;
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](simdjson_result<singlestage::document> val) {
// Get everything that can fail in both forward and backwards order
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_SUCCESS( val.get_bool());
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc) {
singlestage::document val;
ASSERT_SUCCESS( std::move(doc).get(val) ); // Get everything that can fail in both forward and backwards order
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
TEST_SUCCEED();
}
bool get_fail_then_succeed_null() {
TEST_START();
auto json = R"({ "val" : null })"_padded;
SUBTEST("simdjson_result<singlestage::value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> val = doc["val"];
// Get everything that can fail in both forward and backwards order
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
SUBTEST("singlestage::value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value val;
ASSERT_SUCCESS( doc["val"].get(val) );
// Get everything that can fail in both forward and backwards order
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
json = R"(null)"_padded;
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](simdjson_result<singlestage::document> val) {
// Get everything that can fail in both forward and backwards order
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc) {
singlestage::document val;
ASSERT_SUCCESS( std::move(doc).get(val) ); // Get everything that can fail in both forward and backwards order
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
TEST_SUCCEED();
}
bool invalid_type() {
TEST_START();
SINGLESTAGE_SUBTEST("]", "]", doc.type().error() == TAPE_ERROR);
SINGLESTAGE_SUBTEST("}", "}", doc.type().error() == TAPE_ERROR);
SINGLESTAGE_SUBTEST(":", ":", doc.type().error() == TAPE_ERROR);
SINGLESTAGE_SUBTEST(",", ",", doc.type().error() == TAPE_ERROR);
SINGLESTAGE_SUBTEST("+", "+", doc.type().error() == TAPE_ERROR);
TEST_SUCCEED();
}
bool run() {
return
badbadjson() &&
badbadjson2() &&
issue1834() &&
issue1834_2() &&
#if SIMDJSON_EXCEPTIONS
raw_json_string_except() &&
raw_json_string_except_with_io() &&
#endif
raw_json_string_error() &&
empty_document_error() &&
parser_max_capacity() &&
get_fail_then_succeed_bool() &&
get_fail_then_succeed_null() &&
invalid_type() &&
simple_error_example() &&
#if SIMDJSON_EXCEPTIONS
simple_error_example_except() &&
#endif
true;
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, error_tests::run);
}
@@ -0,0 +1,160 @@
#include "simdjson.h"
#include "test_singlestage.h"
#include <cstdint>
using namespace simdjson;
namespace iterate_many_csv_tests {
using namespace std;
bool normal() {
TEST_START();
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
singlestage::parser parser;
singlestage::document_stream doc_stream;
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
for (auto doc : doc_stream)
{
ASSERT_SUCCESS(doc);
}
TEST_SUCCEED();
}
bool small_batch_size() {
TEST_START();
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
singlestage::parser parser;
singlestage::document_stream doc_stream;
ASSERT_SUCCESS(parser.iterate_many(json, 32, true).get(doc_stream));
for (auto doc : doc_stream)
{
ASSERT_SUCCESS(doc);
}
TEST_SUCCEED();
}
bool trailing_comma() {
TEST_START();
auto json = R"(1,)"_padded;
singlestage::parser parser;
singlestage::document_stream doc_stream;
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
for (auto doc : doc_stream)
{
ASSERT_SUCCESS(doc);
}
TEST_SUCCEED();
}
bool check_parsed_values() {
TEST_START();
auto json = R"( 1 , "a" , [100, 1] , {"hello" : "world"} , )"_padded;
singlestage::parser parser;
singlestage::document_stream doc_stream;
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
auto begin = doc_stream.begin();
auto end = doc_stream.end();
int cnt = 0;
auto it = begin;
for (; it != end && cnt < 4; ++it, ++cnt) {
auto doc = *it;
switch (cnt)
{
case 0:
{
int64_t actual;
ASSERT_SUCCESS(doc.get_int64().get(actual));
ASSERT_EQUAL(actual, 1);
break;
}
case 1:
{
std::string_view sv;
ASSERT_SUCCESS(doc.get_string().get(sv));
ASSERT_EQUAL(sv, "a");
break;
}
case 2:
{
std::vector<int64_t> expected{100, 1};
singlestage::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
size_t element_count;
ASSERT_SUCCESS(arr.count_elements().get(element_count));
ASSERT_EQUAL(element_count, 2);
int i = 0;
for (auto a : arr)
{
int64_t actual;
ASSERT_SUCCESS(a.get(actual));
ASSERT_EQUAL(actual, expected[i++]);
}
break;
}
case 3:
{
singlestage::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
std::string_view sv;
obj.find_field("hello").get(sv);
ASSERT_EQUAL(sv, "world");
break;
}
default:
TEST_FAIL("Too many cases")
}
}
ASSERT_EQUAL(cnt, 4);
ASSERT_TRUE(!(it != end));
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool leading_comma() {
TEST_START();
auto json = R"(,1)"_padded;
singlestage::parser parser;
singlestage::document_stream doc_stream;
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
try {
auto begin = doc_stream.begin();
auto end = doc_stream.end();
for (auto it = begin; it != end; ++it) {}
} catch (simdjson_error& e) {
ASSERT_ERROR(e.error(), TAPE_ERROR);
}
TEST_SUCCEED();
}
#endif
bool run() {
return normal() &&
small_batch_size() &&
trailing_comma() &&
check_parsed_values() &&
#if SIMDJSON_EXCEPTIONS
leading_comma() &&
#endif
true;
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, iterate_many_csv_tests::run);
}
@@ -0,0 +1,491 @@
#include "simdjson.h"
#include "test_singlestage.h"
#include <string>
using namespace simdjson;
namespace json_pointer_tests {
const padded_string TEST_JSON = R"(
{
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
}
)"_padded;
const padded_string TEST_RFC_JSON = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_padded;
bool run_success_test(const padded_string & json,std::string_view json_pointer,std::string expected) {
TEST_START();
std::cout <<":"<< json_pointer<<std::endl;
singlestage::parser parser;
singlestage::document doc;
singlestage::value val;
std::string_view actual;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
ASSERT_SUCCESS(simdjson::to_json_string(val).get(actual));
ASSERT_EQUAL(actual,expected);
// We want to see if the value is usable besides to_json_string.
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
singlestage::json_type type;
ASSERT_SUCCESS(val.type().get(type));
switch (type) {
case singlestage::json_type::array:
ASSERT_SUCCESS(val.get_array().error());
break;
case singlestage::json_type::object:
ASSERT_SUCCESS(val.get_object().error());
break;
case singlestage::json_type::number:
ASSERT_SUCCESS(val.get_double().error());
break;
case singlestage::json_type::string:
ASSERT_SUCCESS(val.get_string().error());
break;
case singlestage::json_type::boolean:
ASSERT_SUCCESS(val.get_bool().error());
break;
case singlestage::json_type::null:
ASSERT_SUCCESS(val.is_null().error());
break;
default:
TEST_FAIL("unexpected type");
}
TEST_SUCCEED();
}
bool run_failure_test(const padded_string & json,std::string_view json_pointer,error_code expected) {
TEST_START();
std::cout << "json_pointer: " << json_pointer << std::endl;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_EQUAL(doc.at_pointer(json_pointer).error(),expected);
TEST_SUCCEED();
}
bool demo_test() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
singlestage::document cars;
double x;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
ASSERT_SUCCESS(cars.at_pointer("/0/tire_pressure/1").get(x));
ASSERT_EQUAL(x,39.9);
TEST_SUCCEED();
}
bool demo_relative_path() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
singlestage::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
for (auto car_element : cars) {
double x;
ASSERT_SUCCESS(car_element.at_pointer("/tire_pressure/1").get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) { return false; }
TEST_SUCCEED();
}
bool document_as_scalar() {
TEST_START();
auto number_json = R"( 1 )"_padded;
auto null_json = R"( null )"_padded;
auto string_json = R"( "a" )"_padded;
auto true_json = R"( true )"_padded;
auto false_json = R"( false )"_padded;
auto object_json = R"( {} )"_padded;
auto array_json = R"( {} )"_padded;
singlestage::parser parser;
singlestage::document doc;
singlestage::value val;
bool is_scalar;
std::cout << " checking number"<< std::endl;
ASSERT_SUCCESS(parser.iterate(number_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_pointer("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking null"<< std::endl;
ASSERT_SUCCESS(parser.iterate(null_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_pointer("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking string"<< std::endl;
ASSERT_SUCCESS(parser.iterate(string_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_pointer("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking false"<< std::endl;
ASSERT_SUCCESS(parser.iterate(false_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_pointer("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking true"<< std::endl;
ASSERT_SUCCESS(parser.iterate(true_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_pointer("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking object"<< std::endl;
ASSERT_SUCCESS(parser.iterate(object_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_FALSE(is_scalar);
ASSERT_SUCCESS(doc.at_pointer("").get(val));
std::cout << " checking array"<< std::endl;
ASSERT_SUCCESS(parser.iterate(array_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_FALSE(is_scalar);
ASSERT_SUCCESS(doc.at_pointer("").get(val));
TEST_SUCCEED();
}
bool many_json_pointers() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
singlestage::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
for (int i = 0; i < 3; i++) {
double x;
std::string json_pointer = std::string("/") + std::to_string(i) + std::string("/tire_pressure/1");
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) { return false; }
TEST_SUCCEED();
}
bool run_broken_tests() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
singlestage::value v;
std::string_view val;
auto invalid_escape_key = R"( {"hello": [0,1,2,3], "te\est": "foo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto invalid_escape_value = R"( {"hello": [0,1,2,3], "test": "fo\eo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto invalid_escape_value_at_jp = R"( {"hello": [0,1,2,3], "test": "foo", "bool": true, "num":1234, "success":"y\es"} )"_padded;
auto unclosed_object = R"( {"test": "foo", "bool": true, "num":1234, "success":"yes" )"_padded;
auto missing_bracket_before = R"( {"hello": [0,1,2,3, "test": "foo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto missing_bracket_after = R"( {"test": "foo", "bool": true, "num":1234, "success":"yes", "hello":[0,1,2,3} )"_padded;
std::string json_pointer = "/success";
std::cout << "\t- invalid_escape_key" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_key).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
std::cout << "\t- invalid_escape_value" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
std::cout << "\t- invalid_escape_value_at_jp_nomat" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value_at_jp).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(v));
std::cout << "\t- invalid_escape_value_at_jp" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value_at_jp).get(doc));
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::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);
std::cout << "\t- missing_bracket_after" << std::endl;
ASSERT_SUCCESS(parser.iterate(missing_bracket_after).get(doc));
ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
TEST_SUCCEED();
}
bool many_json_pointers_object_array() {
TEST_START();
auto dogcatpotato = R"( { "dog" : [1,2,3], "cat" : [5, 6, 7], "potato" : [1234]})"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(dogcatpotato).get(doc));
singlestage::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_pointer("/dog/1").get(x));
ASSERT_EQUAL(x, 2);
ASSERT_SUCCESS(obj.at_pointer("/potato/0").get(x));
ASSERT_EQUAL(x, 1234);
TEST_SUCCEED();
}
bool many_json_pointers_object() {
TEST_START();
auto cfoofoo2 = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(cfoofoo2).get(doc));
singlestage::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_pointer("/c/foo/a/1").get(x));
ASSERT_EQUAL(x, 20);
ASSERT_SUCCESS(obj.at_pointer("/d/foo2/a/2").get(x));
ASSERT_EQUAL(x, 30);
ASSERT_SUCCESS(obj.at_pointer("/e").get(x));
ASSERT_EQUAL(x, 120);
TEST_SUCCEED();
}
bool many_json_pointers_array() {
TEST_START();
auto cfoofoo2 = R"( [ 111, 2, 3, { "foo": { "a": [ 10, 20, 33 ] }}, { "foo2": { "a": [ 10, 20, 30 ] }}, 1001 ])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(cfoofoo2).get(doc));
singlestage::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
int64_t x;
ASSERT_SUCCESS(arr.at_pointer("/3/foo/a/1").get(x));
ASSERT_EQUAL(x, 20);
TEST_SUCCEED();
}
struct car_type {
std::string make;
std::string model;
uint64_t year;
std::vector<double> tire_pressure;
car_type(std::string_view _make, std::string_view _model, uint64_t _year,
std::vector<double>&& _tire_pressure) :
make{_make}, model{_model}, year(_year), tire_pressure(_tire_pressure) {}
};
bool json_pointer_invalidation() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
singlestage::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
singlestage::object obj;
std::string json_pointer = std::string("/") + std::to_string(i);
// Each successive at_pointer call invalidates
// previously parsed values, strings, objects and array.
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(obj));
// We materialize the object.
std::string_view make;
ASSERT_SUCCESS(obj["make"].get(make));
std::string_view model;
ASSERT_SUCCESS(obj["model"].get(model));
uint64_t year;
ASSERT_SUCCESS(obj["year"].get(year));
// We materialize the array.
singlestage::array arr;
ASSERT_SUCCESS(obj["tire_pressure"].get(arr));
std::vector<double> values;
for(auto x : arr) {
double value_double;
ASSERT_SUCCESS(x.get(value_double));
values.push_back(value_double);
}
content.emplace_back(make, model, year, std::move(values));
}
std::string expected[] = {"Toyota", "Kia", "Toyota"};
int i = 0;
for (car_type c : content) {
std::cout << c.make << " " << c.model << " " << c.year << "\n";
ASSERT_EQUAL(expected[i++], c.make);
}
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool json_pointer_invalidation_exceptions() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
std::vector<double> measured;
singlestage::document cars = parser.iterate(cars_json);
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
std::string json_pointer = std::string("/") + std::to_string(i);
// Each successive at_pointer call invalidates
// previously parsed values, strings, objects and array.
singlestage::object obj(cars.at_pointer(json_pointer).get_object());
// We materialize the object.
std::string_view make = obj["make"];
std::string_view model = obj["model"];
uint64_t year(obj["year"]);
// We materialize the array.
singlestage::array arr(obj["tire_pressure"].get_array());
std::vector<double> values;
for(auto x : arr) {
double value_double(x.get_double());
values.push_back(value_double);
}
content.emplace_back(make, model, year, std::move(values));
}
std::string expected[] = {"Toyota", "Kia", "Toyota"};
for (car_type c : content) {
std::cout << c.make << " " << c.model << " " << c.year << "\n";
}
TEST_SUCCEED();
}
#endif
bool run() {
return
#if SIMDJSON_EXCEPTIONS
json_pointer_invalidation_exceptions() &&
#endif
many_json_pointers_array() &&
many_json_pointers_object() &&
many_json_pointers_object_array() &&
run_broken_tests() &&
json_pointer_invalidation() &&
demo_test() &&
demo_relative_path() &&
run_success_test(TEST_RFC_JSON,"/foo",R"(["bar", "baz"])") &&
run_success_test(TEST_RFC_JSON,"/foo/0",R"("bar")") &&
run_success_test(TEST_RFC_JSON,"/",R"(0)") &&
run_success_test(TEST_RFC_JSON,"/a~1b",R"(1)") &&
run_success_test(TEST_RFC_JSON,"/c%d",R"(2)") &&
run_success_test(TEST_RFC_JSON,"/e^f",R"(3)") &&
run_success_test(TEST_RFC_JSON,"/g|h",R"(4)") &&
run_success_test(TEST_RFC_JSON,R"(/i\\j)",R"(5)") &&
run_success_test(TEST_RFC_JSON,R"(/k\"l)",R"(6)") &&
run_success_test(TEST_RFC_JSON,"/ ",R"(7)") &&
run_success_test(TEST_RFC_JSON,"/m~0n",R"(8)") &&
run_success_test(TEST_RFC_JSON,"",R"({
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
})") &&
run_success_test(TEST_JSON, "", R"({
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
})") &&
run_success_test(TEST_JSON, R"(/~1~001abc)", R"([
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
])") &&
run_success_test(TEST_JSON, R"(/~1~001abc/1)", R"({
"\\\" 0": [
"value0",
"value1"
]
})") &&
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0)", R"([
"value0",
"value1"
])") &&
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/0)", "\"value0\"") &&
run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/1)", "\"value1\"") &&
run_success_test(TEST_JSON, "/arr", R"([])") &&
run_success_test(TEST_JSON, "/0", "\"0 ok\"") &&
run_success_test(TEST_JSON, "/01", "\"01 ok\"") &&
run_success_test(TEST_JSON, "", R"({
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
})") &&
run_failure_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/2)", INDEX_OUT_OF_BOUNDS) &&
run_failure_test(TEST_JSON, "/arr/0", INDEX_OUT_OF_BOUNDS) &&
run_failure_test(TEST_JSON, "~1~001abc", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "/~01abc", NO_SUCH_FIELD) &&
run_failure_test(TEST_JSON, "/~1~001abc/01", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "/~1~001abc/", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "/~1~001abc/-", INDEX_OUT_OF_BOUNDS) &&
many_json_pointers() &&
document_as_scalar() &&
true;
}
} // json_pointer_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, json_pointer_tests::run);
}
@@ -0,0 +1,33 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace key_string_tests {
#if SIMDJSON_EXCEPTIONS
bool parser_key_value() {
TEST_START();
singlestage::parser parser;
const padded_string json = R"({ "1": "1", "2": "2", "3": "3", "abc": "abc", "\u0075": "\u0075" })"_padded;
auto doc = parser.iterate(json);
for(auto field : doc.get_object()) {
std::string_view keyv = field.unescaped_key();
std::string_view valuev = field.value();
if(keyv != valuev) { return false; }
}
return true;
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
#if SIMDJSON_EXCEPTIONS
parser_key_value() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace key_string_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, key_string_tests::run);
}
@@ -0,0 +1,67 @@
#define SIMDJSON_VERBOSE_LOGGING 1
#include "simdjson.h"
#include "test_singlestage.h"
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace simdjson;
namespace log_error_tests {
#if SIMDJSON_EXCEPTIONS
using namespace std;
bool tape_error()
{
TEST_START();
auto json = R"( {"a", "hello"} )"_padded;
singlestage::parser parser;
try {
singlestage::document doc = parser.iterate(json);
std::cout << doc["a"] << std::endl;
TEST_FAIL("Should have thrown an exception!")
} catch (simdjson_error& e) {
ASSERT_ERROR(e.error(), TAPE_ERROR);
}
TEST_SUCCEED();
}
bool no_such_field()
{
TEST_START();
auto json = R"( {"a": "hello"} )"_padded;
singlestage::parser parser;
try {
singlestage::document doc = parser.iterate(json);
std::cout << doc["missing_key"] << std::endl;
TEST_FAIL("Should have thrown an exception!")
} catch (simdjson_error& e) {
ASSERT_ERROR(e.error(), NO_SUCH_FIELD);
}
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::string str = "SIMDJSON_LOG_LEVEL=ERROR";
putenv(str.data());
bool rc =
#if SIMDJSON_EXCEPTIONS
tape_error() &&
no_such_field() &&
#endif // #if SIMDJSON_EXCEPTIONS
true;
SIMDJSON_POP_DISABLE_WARNINGS
return rc;
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, log_error_tests::run);
}
@@ -0,0 +1,30 @@
#define SIMDJSON_VERBOSE_LOGGING 1
#include "simdjson.h"
#include <iostream>
#include <vector>
#include <string>
using namespace simdjson;
int main() {
#if SIMDJSON_EXCEPTIONS
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
singlestage::parser parser;
singlestage::document doc = parser.iterate(cars_json);
std::vector<std::string> container;
for(singlestage::object o : doc) {
container.emplace_back(std::string_view(o["make"]));
}
std::cout << "---" << std::endl;
for(std::string& m : container) {
std::cout << m << std::endl;
}
#endif // #if SIMDJSON_EXCEPTIONS
return EXIT_SUCCESS;
}
@@ -0,0 +1,631 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace misc_tests {
using namespace std;
bool issue1981_success() {
auto error_phrase = R"(false)"_padded;
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(error_phrase).get(doc));
bool b;
ASSERT_SUCCESS( doc.get_bool().get(b));
ASSERT_FALSE(b);
TEST_SUCCEED();
}
bool issue1981_failure() {
auto error_phrase = R"(falseA)"_padded;
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(error_phrase).get(doc));
bool b;
ASSERT_ERROR( doc.get_bool().get(b), INCORRECT_TYPE);
TEST_SUCCEED();
}
bool replacement_char() {
auto fun_phrase = R"( ["I \u2665 Unicode. Even broken \ud800 Unicode." ])"_padded;
std::string_view expected_fun = "I \xe2\x99\xa5 Unicode. Even broken \xef\xbf\xbd Unicode.";
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(fun_phrase).get(doc));
singlestage::array arr;
ASSERT_SUCCESS( doc.get_array().get(arr));
std::string_view view;
ASSERT_SUCCESS( arr.at(0).get_string(true).get(view));
ASSERT_EQUAL(view, expected_fun);
TEST_SUCCEED();
}
bool wobbly_tests() {
auto lone_surrogate = R"( "\ud800" )"_padded;
std::string_view expected_lone = "\xed\xa0\x80";
auto fun_phrase = R"( ["I \u2665 Unicode. Even broken \ud800 Unicode." ])"_padded;
std::string_view expected_fun = "I \xe2\x99\xa5 Unicode. Even broken \xed\xa0\x80 Unicode.";
auto insane_url = R"({"input": "http://example.com/\uDC00\uD834\uDF06\uDC00"} )"_padded;
std::string_view expected_insane = "http://example.com/\xED\xB0\x80\xF0\x9D\x8C\x86\xED\xB0\x80";
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(lone_surrogate).get(doc));
std::string_view view;
ASSERT_SUCCESS( doc.get_wobbly_string().get(view));
ASSERT_EQUAL(view, expected_lone);
ASSERT_SUCCESS(parser.iterate(fun_phrase).get(doc));
singlestage::array arr;
ASSERT_SUCCESS( doc.get_array().get(arr));
ASSERT_SUCCESS( arr.at(0).get_wobbly_string().get(view));
ASSERT_EQUAL(view, expected_fun);
ASSERT_SUCCESS(parser.iterate(insane_url).get(doc));
singlestage::object obj;
ASSERT_SUCCESS( doc.get_object().get(obj));
ASSERT_SUCCESS( obj["input"].get_wobbly_string().get(view));
ASSERT_EQUAL(view, expected_insane);
TEST_SUCCEED();
}
bool test_get_value() {
TEST_START();
singlestage::parser parser;
padded_string json = R"({"a":[[1,null,3.0],["a","b",true],[10000000000,2,3]]})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
singlestage::value val;
ASSERT_SUCCESS(doc.get_value().get(val));
singlestage::object obj;
ASSERT_SUCCESS(val.get_object().get(obj));
singlestage::array arr;
ASSERT_SUCCESS(obj["a"].get_array().get(arr));
size_t count{};
ASSERT_SUCCESS(arr.count_elements().get(count));
ASSERT_EQUAL(3,count);
TEST_SUCCEED();
}
bool issue1661a() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"":],"global-groups":[[]}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::value global_groups;
ASSERT_SUCCESS(doc["global-groups"].get(global_groups));
singlestage::json_type global_type;
ASSERT_SUCCESS(global_groups.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::array);
TEST_SUCCEED();
}
bool doc_ref() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"":2,"v":[]})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::document_reference ref(doc);
singlestage::value arr;
ASSERT_SUCCESS(ref["v"].get(arr));
singlestage::json_type global_type;
ASSERT_SUCCESS(arr.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::array);
TEST_SUCCEED();
}
bool issue_uffff() {
TEST_START();
singlestage::parser parser;
auto json = R"( "wow:\uFFFF" )"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_SUCCESS( doc.get_string().get(view));
ASSERT_EQUAL(view, "wow:\xef\xbf\xbf");
TEST_SUCCEED();
}
bool issue_backslash() {
TEST_START();
singlestage::parser parser;
auto json = R"( "sc:\\./" )"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_SUCCESS( doc.get_string().get(view));
ASSERT_EQUAL(view, "sc:\\./");
TEST_SUCCEED();
}
bool issue1870() {
TEST_START();
singlestage::parser parser;
auto json = R"("\uDC00")"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR( doc.get_string().get(view), STRING_ERROR );
TEST_SUCCEED();
}
// Test a surrogate pair with the low surrogate out of range
bool issue1894() {
TEST_START();
singlestage::parser parser;
auto json = R"("\uD888\u1234")"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR(doc.get_string().get(view), STRING_ERROR);
TEST_SUCCEED();
}
bool issue1894toolarge() {
TEST_START();
singlestage::parser parser;
auto json = R"("\uD888\uE000")"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR(doc.get_string().get(view), STRING_ERROR);
TEST_SUCCEED();
}
// Test the smallest surrogate pair, largest surrogate pair, and a surrogate pair in range.
bool issue1894success() {
TEST_START();
singlestage::parser parser;
auto json = R"("\uD888\uDC00\uD800\uDC00\uDBFF\uDFFF")"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_SUCCESS(doc.get_string().get(view));
ASSERT_EQUAL(view, "\xf0\xb2\x80\x80\xf0\x90\x80\x80\xf4\x8f\xbf\xbf");
TEST_SUCCEED();
}
bool issue1660() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_object(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_bool() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_bool(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_uint64() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_uint64(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_int64() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_int64(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_double() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_double(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_null() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
bool is_null_value;
ASSERT_SUCCESS(shadowable.is_null().get(is_null_value));
ASSERT_TRUE(!is_null_value);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool issue1660_with_string() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"globals":{"a":{"shadowable":[}}}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object globals;
ASSERT_SUCCESS(doc["globals"].get(globals));
for (auto global_field : globals) {
singlestage::value global;
ASSERT_SUCCESS(global_field.value().get(global));
singlestage::json_type global_type;
ASSERT_SUCCESS(global.type().get(global_type));
ASSERT_EQUAL(global_type, singlestage::json_type::object);
singlestage::object global_object;
ASSERT_SUCCESS(global.get(global_object));
singlestage::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_ERROR(shadowable.get_string(), INCORRECT_TYPE);
singlestage::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
return false;
} else {
break;
}
}
TEST_SUCCEED();
}
bool is_alive_root_array() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"([1,2,3)"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_alive());
size_t count{0};
for(simdjson_unused simdjson_result<singlestage::value> val : doc) {
ASSERT_ERROR(val.error(), INCOMPLETE_ARRAY_OR_OBJECT);
count++;
}
ASSERT_EQUAL(count, 1);
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool is_alive_array() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"a":[1,2,3})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_alive());
singlestage::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
singlestage::array internal_arr;
ASSERT_SUCCESS(obj["a"].get_array().get(internal_arr));
size_t count{0};
for(simdjson_unused simdjson_result<singlestage::value> val : internal_arr) {
if(count < 3) {
ASSERT_SUCCESS(val.error());
ASSERT_TRUE(doc.is_alive());
} else {
ASSERT_ERROR(val.error(), TAPE_ERROR);
ASSERT_FALSE(doc.is_alive());
}
count++;
}
ASSERT_EQUAL(count, 4);
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool is_alive_root_object() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"a":1)"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_alive());
singlestage::object obj;
ASSERT_ERROR(doc.get_object().get(obj), INCOMPLETE_ARRAY_OR_OBJECT);
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool is_alive_object() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"([{"a":1])"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_alive());
singlestage::object obj;
ASSERT_SUCCESS(doc.at(0).get_object().get(obj));
ASSERT_TRUE(doc.is_alive());
size_t count{0};
for(simdjson_unused simdjson_result<singlestage::field> val : obj) {
if(count == 0) {
ASSERT_SUCCESS(val.error());
ASSERT_TRUE(doc.is_alive());
}
if(count == 1) {
ASSERT_ERROR(val.error(), TAPE_ERROR);
ASSERT_FALSE(doc.is_alive());
}
count++;
}
ASSERT_EQUAL(count, 2);
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool issue1661() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"({"":],"global-groups":[[]}})"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::object global_groups;
ASSERT_ERROR(doc["global-groups"].get(global_groups), INCORRECT_TYPE);
singlestage::object globals;
auto error = doc["globals"].get(globals);
if(error == SUCCESS) { return false; }
TEST_SUCCEED();
}
simdjson_warn_unused bool big_integer() {
TEST_START();
simdjson::singlestage::parser parser;
simdjson::padded_string docdata = R"({"value":12321323213213213213213213213211223})"_padded;
simdjson::singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
simdjson::singlestage::object o;
ASSERT_SUCCESS(doc.get_object().get(o));
string_view token;
ASSERT_SUCCESS(o["value"].raw_json_token().get(token));
ASSERT_EQUAL(token, "12321323213213213213213213213211223");
return true;
}
simdjson_warn_unused bool big_integer_in_string() {
TEST_START();
simdjson::singlestage::parser parser;
simdjson::padded_string docdata = R"({"value":"12321323213213213213213213213211223"})"_padded;
simdjson::singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
simdjson::singlestage::object o;
ASSERT_SUCCESS(doc.get_object().get(o));
string_view token;
ASSERT_SUCCESS(o["value"].raw_json_token().get(token));
ASSERT_EQUAL(token, "\"12321323213213213213213213213211223\"");
return true;
}
simdjson_warn_unused bool test_raw_json_token(string_view json, string_view expected_token, int expected_start_index = 0) {
string title("'");
title.append(json.data(), json.length());
title += std::string("'");
padded_string json_padded = json;
SUBTEST(title, test_singlestage_doc(json_padded, [&](auto doc) {
string_view token;
ASSERT_SUCCESS( doc.raw_json_token().get(token) );
ASSERT_EQUAL( token, expected_token );
// Validate the text is inside the original buffer
ASSERT_EQUAL( reinterpret_cast<const void*>(token.data()), reinterpret_cast<const void*>(&json_padded.data()[expected_start_index]));
return true;
}));
// Test values
auto json_in_hash = string(R"({"a":)");
json_in_hash.append(json.data(), json.length());
json_in_hash += std::string("}");
json_padded = json_in_hash;
title = std::string("'");
title.append(json_in_hash.data(), json_in_hash.length());
title += std::string("'");
SUBTEST(title, test_singlestage_doc(json_padded, [&](auto doc) {
string_view token;
ASSERT_SUCCESS( doc["a"].raw_json_token().get(token) );
ASSERT_EQUAL( token, expected_token );
// Validate the text is inside the original buffer
// Adjust for the {"a":
ASSERT_EQUAL( reinterpret_cast<const void*>(token.data()), reinterpret_cast<const void*>(&json_padded.data()[5+expected_start_index]));
return true;
}));
return true;
}
bool raw_json_token() {
TEST_START();
return
test_raw_json_token("{}", "{") &&
test_raw_json_token("{ }", "{ ") &&
test_raw_json_token("{ \n }", "{ \n ") &&
test_raw_json_token(" \n { \n } \n ", "{ \n ", 3) &&
test_raw_json_token("[]", "[") &&
test_raw_json_token("1", "1") &&
test_raw_json_token(" \n 1 \n ", "1 \n ", 3) &&
test_raw_json_token("-123.456e-789", "-123.456e-789") &&
test_raw_json_token(" \n -123.456e-789 \n ", "-123.456e-789 \n ", 3) &&
test_raw_json_token("true", "true") &&
test_raw_json_token("false", "false") &&
test_raw_json_token("null", "null") &&
test_raw_json_token("blah2", "blah2") &&
test_raw_json_token("true false", "true ") &&
test_raw_json_token("true \n false", "true \n ") &&
true;
}
bool run() {
return
issue1981_success() &&
issue1981_failure() &&
replacement_char() &&
wobbly_tests() &&
issue_uffff() &&
issue_backslash() &&
issue1870() &&
issue1894() &&
issue1894toolarge() &&
issue1894success() &&
is_alive_root_array() &&
is_alive_root_object() &&
is_alive_array() &&
is_alive_object() &&
test_get_value() &&
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() &&
doc_ref() &&
true;
}
} // namespace twitter_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, misc_tests::run);
}
@@ -0,0 +1,368 @@
#include "simdjson.h"
#include "test_singlestage.h"
#include <string>
using namespace simdjson;
namespace number_in_string_tests {
const padded_string CRYPTO_JSON = R"(
{
"ticker":{
"base":"BTC",
"target":"USD",
"price":"443.7807865468",
"volume":"31720.1493969300",
"change":"Infinity",
"markets":[
{
"market":"bitfinex",
"price":"447.5000000000",
"volume":"10559.5293639000"
},
{
"market":"bitstamp",
"price":"448.5400000000",
"volume":"11628.2880079300"
},
{
"market":"btce",
"price":"432.8900000000",
"volume":"8561.0563600000"
}
]
},
"timestamp":1399490941,
"timestampstr":"1399490941"
}
)"_padded;
bool in_document_int64() {
TEST_START();
auto json = R"( "-11232321" )"_padded;
singlestage::parser parser;
auto doc = parser.iterate(json);
int64_t result;
ASSERT_SUCCESS(doc.get_int64_in_string().get(result));
ASSERT_EQUAL(result,-11232321);
TEST_SUCCEED();
}
bool in_document_uint64() {
TEST_START();
auto json = R"( "11232321" )"_padded;
singlestage::parser parser;
auto doc = parser.iterate(json);
uint64_t result;
ASSERT_SUCCESS(doc.get_uint64_in_string().get(result));
ASSERT_EQUAL(result,11232321);
TEST_SUCCEED();
}
bool in_document_double() {
TEST_START();
auto json = R"( "11232321.12" )"_padded;
singlestage::parser parser;
auto doc = parser.iterate(json);
double result;
ASSERT_SUCCESS(doc.get_double_in_string().get(result));
ASSERT_EQUAL(result,11232321.12);
TEST_SUCCEED();
}
bool stream_of_pure_int64() {
TEST_START();
auto json = R"( 1 2 3 )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
int document_index = 0;
for (auto doc : stream) {
document_index++;
int64_t result;
ASSERT_SUCCESS(doc.get_int64().get(result));
ASSERT_EQUAL(result,document_index);
}
ASSERT_EQUAL(3,document_index);
TEST_SUCCEED();
}
bool stream_of_direct_int64() {
TEST_START();
auto json = R"( "1" "2" "3" )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
int document_index = 0;
for (auto doc : stream) {
document_index++;
int64_t result;
ASSERT_SUCCESS(doc.get_int64_in_string().get(result));
ASSERT_EQUAL(result,document_index);
}
ASSERT_EQUAL(3,document_index);
TEST_SUCCEED();
}
bool stream_of_int64() {
TEST_START();
auto json = R"( ["1"] ["2"] ["3"] )"_padded;
singlestage::parser parser;
singlestage::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
int document_index = 0;
for (auto doc : stream) {
document_index++;
int64_t result;
ASSERT_SUCCESS(doc.get_array().at(0).get_int64_in_string().get(result));
ASSERT_EQUAL(result,document_index);
}
ASSERT_EQUAL(3,document_index);
TEST_SUCCEED();
}
bool array_double() {
TEST_START();
auto json = R"(["1.2","2.3","-42.3","2.43442e3", "-1.234e3"])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::vector<double> expected = {1.2, 2.3, -42.3, 2434.42, -1234};
double d;
for (auto value : doc) {
ASSERT_SUCCESS(value.get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
}
TEST_SUCCEED();
}
bool array_int() {
TEST_START();
auto json = R"(["1", "2", "-3", "1000", "-7844", "-9223372036854775807", "9223372036854775807"])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::vector<int64_t> expected = {1, 2, -3, 1000, -7844, INT64_C(-9223372036854775807), INT64_C(9223372036854775807) };
int64_t i;
for (auto value : doc) {
ASSERT_SUCCESS(value.get_int64_in_string().get(i));
ASSERT_EQUAL(i,expected[counter++]);
}
TEST_SUCCEED();
}
bool array_unsigned() {
TEST_START();
auto json = R"(["1", "2", "24", "9000", "156934", "10588030077111859193", "18446744073709551615"])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::vector<uint64_t> expected = {1, 2, 24, 9000, 156934, UINT64_C(10588030077111859193), UINT64_C(18446744073709551615)};
uint64_t u;
for (auto value : doc) {
ASSERT_SUCCESS(value.get_uint64_in_string().get(u));
ASSERT_EQUAL(u,expected[counter++]);
}
TEST_SUCCEED();
}
bool object() {
TEST_START();
auto json = R"({"a":"1.2", "b":"-2.342e2", "c":"22", "d":"-112358", "e":"1080", "f":"123456789", "g":"10588030077111859193"})"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::vector<double> expected = {1.2, -234.2, 22, -112358, 1080, 123456789};
double d;
int64_t i;
uint64_t u;
// Doubles
ASSERT_SUCCESS(doc.find_field("a").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_SUCCESS(doc.find_field("b").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
// Integers
ASSERT_SUCCESS(doc.find_field("c").get_int64_in_string().get(i));
ASSERT_EQUAL(i,expected[counter++]);
ASSERT_SUCCESS(doc.find_field("d").get_int64_in_string().get(i));
ASSERT_EQUAL(i,expected[counter++]);
// Unsigned integers
ASSERT_SUCCESS(doc.find_field("e").get_uint64_in_string().get(u));
ASSERT_EQUAL(u,expected[counter++]);
ASSERT_SUCCESS(doc.find_field("f").get_uint64_in_string().get(u));
ASSERT_EQUAL(u,expected[counter++]);
ASSERT_SUCCESS(doc.find_field("g").get_uint64_in_string().get(u));
ASSERT_EQUAL(u, UINT64_C(10588030077111859193));
TEST_SUCCEED();
}
bool docs() {
TEST_START();
auto double_doc = R"( "-1.23e1" )"_padded;
auto int_doc = R"( "-243" )"_padded;
auto uint_doc = R"( "212213" )"_padded;
singlestage::parser parser;
singlestage::document doc;
double d;
int64_t i;
uint64_t u;
// Double
ASSERT_SUCCESS(parser.iterate(double_doc).get(doc));
ASSERT_SUCCESS(doc.get_double_in_string().get(d));
ASSERT_EQUAL(d,-12.3);
// Integer
ASSERT_SUCCESS(parser.iterate(int_doc).get(doc));
ASSERT_SUCCESS(doc.get_int64_in_string().get(i));
ASSERT_EQUAL(i,-243);
// Unsigned integer
ASSERT_SUCCESS(parser.iterate(uint_doc).get(doc));
ASSERT_SUCCESS(doc.get_uint64_in_string().get(u));
ASSERT_EQUAL(u,212213);
TEST_SUCCEED();
}
bool number_parsing_error() {
TEST_START();
auto json = R"( ["13.06.54", "1.0e", "2e3r4,,."])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::string expected[3] = {"13.06.54", "1.0e", "2e3r4,,."};
for (auto value : doc) {
double d;
std::string_view view;
ASSERT_ERROR(value.get_double_in_string().get(d),NUMBER_ERROR);
ASSERT_SUCCESS(value.get_string().get(view));
ASSERT_EQUAL(view,expected[counter++]);
}
ASSERT_EQUAL(counter,3);
TEST_SUCCEED();
}
bool incorrect_type_error() {
TEST_START();
auto json = R"( ["e", "i", "pi", "one", "zero"])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
size_t counter{0};
std::string expected[5] = {"e", "i", "pi", "one", "zero"};
for (auto value : doc) {
double d;
std::string_view view;
ASSERT_ERROR(value.get_double_in_string().get(d),INCORRECT_TYPE);
ASSERT_SUCCESS(value.get_string().get(view));
ASSERT_EQUAL(view,expected[counter++]);
}
ASSERT_EQUAL(counter,5);
TEST_SUCCEED();
}
bool json_pointer_test() {
TEST_START();
auto json = R"( ["12.34", { "a":["3","5.6"], "b":{"c":"1.23e1"} }, ["1", "3.5"] ])"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::vector<double> expected = {12.34, 5.6, 12.3, 1, 3.5};
size_t counter{0};
double d;
ASSERT_SUCCESS(doc.at_pointer("/0").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_SUCCESS(doc.at_pointer("/1/a/1").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_SUCCESS(doc.at_pointer("/1/b/c").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_SUCCESS(doc.at_pointer("/2/0").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_SUCCESS(doc.at_pointer("/2/1").get_double_in_string().get(d));
ASSERT_EQUAL(d,expected[counter++]);
ASSERT_EQUAL(counter,5);
TEST_SUCCEED();
}
bool crypto_timestamp() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
uint64_t u;
ASSERT_SUCCESS(doc.at_pointer("/timestampstr").get_uint64_in_string().get(u));
ASSERT_EQUAL(u,1399490941);
TEST_SUCCEED();
}
bool crypto_market() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
singlestage::array markets;
ASSERT_SUCCESS(doc.find_field("ticker").find_field("markets").get_array().get(markets));
std::string_view expected_views[3] = {"bitfinex", "bitstamp", "btce"};
double expected_prices[3] = {447.5, 448.54, 432.89};
double expected_volumes[3] = {10559.5293639, 11628.28800793, 8561.05636};
size_t counter{0};
for (auto value : markets) {
std::string_view view;
double price;
double volume;
ASSERT_SUCCESS(value.find_field("market").get_string().get(view));
ASSERT_EQUAL(view,expected_views[counter]);
ASSERT_SUCCESS(value.find_field("price").get_double_in_string().get(price));
ASSERT_EQUAL(price,expected_prices[counter]);
ASSERT_SUCCESS(value.find_field("volume").get_double_in_string().get(volume));
ASSERT_EQUAL(volume,expected_volumes[counter]);
counter++;
}
ASSERT_EQUAL(counter,3);
TEST_SUCCEED();
}
bool crypto_infinity() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
singlestage::value value;
double d;
std::string_view view;
ASSERT_SUCCESS(doc.find_field("ticker").find_field("change").get(value));
ASSERT_ERROR(value.get_double_in_string().get(d), INCORRECT_TYPE);
ASSERT_SUCCESS(value.get_string().get(view));
ASSERT_EQUAL(view,"Infinity");
TEST_SUCCEED();
}
bool run() {
return stream_of_pure_int64() &&
stream_of_direct_int64() &&
stream_of_int64() &&
in_document_int64() &&
in_document_uint64() &&
in_document_double() &&
array_double() &&
array_int() &&
array_unsigned() &&
object() &&
docs() &&
number_parsing_error() &&
incorrect_type_error() &&
json_pointer_test() &&
crypto_timestamp() &&
crypto_market() &&
crypto_infinity() &&
true;
}
} // number_in_string_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, number_in_string_tests::run);
}
@@ -0,0 +1,422 @@
#include <cmath>
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace number_tests {
bool small_integers() {
TEST_START();
for (int64_t m = 10; m < 20; m++) {
for (int64_t i = -1024; i < 1024; i++) {
if(!test_singlestage<int64_t>(std::to_string(i),
[&](int64_t actual) {
ASSERT_EQUAL(actual, i);
return true;
})) {
return false;
} // if
} // for i
} // for m
return true;
}
bool powers_of_two() {
TEST_START();
// converts the double "expected" to a padded string
auto format_into_padded=[](const double expected) -> padded_string
{
std::vector<char> buf(1024);
const auto n = std::snprintf(buf.data(),
buf.size(),
"%.*e",
std::numeric_limits<double>::max_digits10 - 1,
expected);
const auto nz=static_cast<size_t>(n);
if (n<0 || nz >= buf.size()) { std::abort(); }
return padded_string(buf.data(), nz);
};
for (int i = -1075; i < 1024; ++i) {// large negative values should be zero.
const double expected = std::pow(2, i);
const auto buf=format_into_padded(expected);
std::fflush(nullptr);
if(!test_singlestage<double>(buf,
[&](double actual) {
if(actual!=expected) {
std::cerr << "JSON '" << buf << " parsed to ";
std::fprintf( stderr," %18.18g instead of %18.18g\n", actual, expected); // formatting numbers is easier with printf
SIMDJSON_SHOW_DEFINE(FLT_EVAL_METHOD);
return false;
}
return true;
})) {
return false;
} // if
} // for i
return true;
}
static const double testing_power_of_ten[] = {
1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299,
1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290,
1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281,
1e-280, 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272,
1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263,
1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254,
1e-253, 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245,
1e-244, 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236,
1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227,
1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218,
1e-217, 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209,
1e-208, 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200,
1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191,
1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182,
1e-181, 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173,
1e-172, 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164,
1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155,
1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146,
1e-145, 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137,
1e-136, 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128,
1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119,
1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110,
1e-109, 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101,
1e-100, 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92,
1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83,
1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74,
1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65,
1e-64, 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56,
1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47,
1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38,
1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29,
1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11,
1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2,
1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25,
1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34,
1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43,
1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52,
1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61,
1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70,
1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88,
1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97,
1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106,
1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115,
1e116, 1e117, 1e118, 1e119, 1e120, 1e121, 1e122, 1e123, 1e124,
1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133,
1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142,
1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151,
1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 1e160,
1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169,
1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178,
1e179, 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187,
1e188, 1e189, 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196,
1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204, 1e205,
1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214,
1e215, 1e216, 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223,
1e224, 1e225, 1e226, 1e227, 1e228, 1e229, 1e230, 1e231, 1e232,
1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240, 1e241,
1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250,
1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259,
1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268,
1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277,
1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286,
1e287, 1e288, 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295,
1e296, 1e297, 1e298, 1e299, 1e300, 1e301, 1e302, 1e303, 1e304,
1e305, 1e306, 1e307, 1e308};
bool powers_of_ten() {
TEST_START();
std::vector<char> buf(1024);
const bool is_pow_correct{1e-308 == std::pow(10,-308)};
const int start_point = is_pow_correct ? -10000 : -307;
if(!is_pow_correct) {
std::cout << "On your system, the pow function is busted. Sorry about that. " << std::endl;
}
for (int i = start_point; i <= 308; ++i) {// large negative values should be zero.
const size_t n = std::snprintf(buf.data(), buf.size(), "1e%d", i);
if (n >= buf.size()) { std::abort(); }
std::fflush(nullptr);
const double expected = ((i >= -307) ? testing_power_of_ten[i + 307]: std::pow(10, i));
if(!test_singlestage<double>(padded_string(buf.data(), n), [&](double actual) {
if(actual!=expected) {
std::cerr << "JSON '" << buf.data() << " parsed to ";
std::fprintf( stderr," %18.18g instead of %18.18g\n", actual, expected); // formatting numbers is easier with printf
SIMDJSON_SHOW_DEFINE(FLT_EVAL_METHOD);
return false;
}
return true;
})) {
return false;
} // if
} // for i
return true;
}
void github_issue_1273() {
padded_string bad(std::string_view("0.0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000122978293824"));
simdjson::singlestage::parser parser;
simdjson_unused auto blah=parser.iterate(bad);
double x;
simdjson_unused auto blah2=blah.get(x);
}
bool issue_1898() {
TEST_START();
padded_string negative_zero_string(std::string_view("-1e-999"));
simdjson::singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(negative_zero_string).get(doc));
double x;
ASSERT_SUCCESS(doc.get(x));
// should be minus 0
ASSERT_TRUE(std::signbit(x));
ASSERT_TRUE(x == -0);
TEST_SUCCEED();
}
bool old_crashes() {
TEST_START();
github_issue_1273();
TEST_SUCCEED();
}
bool is_alive_root_array() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"([1,2,3)"_padded;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_alive());
size_t count{0};
for(simdjson_unused simdjson_result<singlestage::value> val : doc) {
ASSERT_ERROR(val.error(), INCOMPLETE_ARRAY_OR_OBJECT);
count++;
}
ASSERT_EQUAL(count, 1);
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool get_number_tests() {
TEST_START();
singlestage::parser parser;
padded_string docdata = R"([1.0, 3, 1, 3.1415,-13231232,9999999999999999999,-9223372036854775807,-9223372036854775808])"_padded;
singlestage::number_type expectedtypes[] = {singlestage::number_type::floating_point_number,
singlestage::number_type::signed_integer,
singlestage::number_type::signed_integer,
singlestage::number_type::floating_point_number,
singlestage::number_type::signed_integer,
singlestage::number_type::unsigned_integer,
singlestage::number_type::signed_integer,
singlestage::number_type::signed_integer
};
bool is_negative[] = {false, false, false, false, true, false, true, true};
bool is_integer[] = {false, true, true, false, true, true, true, true};
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
singlestage::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
size_t counter{0};
for(simdjson_result<singlestage::value> valr : arr) {
singlestage::value val;
ASSERT_SUCCESS(valr.get(val));
singlestage::number_type nt{};
ASSERT_SUCCESS(val.get_number_type().get(nt));
ASSERT_EQUAL(expectedtypes[counter], nt);
singlestage::number num;
ASSERT_SUCCESS(val.get_number().get(num));
ASSERT_EQUAL(is_negative[counter], val.is_negative());
bool intvalue{};
ASSERT_SUCCESS(val.is_integer().get(intvalue));
ASSERT_EQUAL(is_integer[counter], intvalue);
singlestage::number_type t = num.get_number_type();
ASSERT_EQUAL(expectedtypes[counter], t);
switch(t) {
case singlestage::number_type::signed_integer:
ASSERT_TRUE(num.is_int64());
break;
case singlestage::number_type::unsigned_integer:
ASSERT_TRUE(num.is_uint64());
break;
case singlestage::number_type::floating_point_number:
ASSERT_TRUE(num.is_double());
break;
}
if(counter == 0) {
ASSERT_EQUAL(num.get_double(), 1.0);
ASSERT_EQUAL((double)num, 1.0);
} else if(counter == 1) {
ASSERT_EQUAL(num.get_int64(), 3);
ASSERT_EQUAL((int64_t)num, 3);
} else if(counter == 2) {
ASSERT_EQUAL(num.get_int64(), 1);
ASSERT_EQUAL((int64_t)num, 1);
} else if(counter == 3) {
ASSERT_EQUAL(num.get_double(), 3.1415);
ASSERT_EQUAL((double)num, 3.1415);
} else if(counter == 4) {
ASSERT_EQUAL(num.get_int64(), -13231232);
ASSERT_EQUAL((int64_t)num, -13231232);
} else if(counter == 5) {
ASSERT_EQUAL(num.get_uint64(), UINT64_C(9999999999999999999));
ASSERT_EQUAL((uint64_t)num, UINT64_C(9999999999999999999));
}
counter++;
}
TEST_SUCCEED();
}
bool issue1878() {
TEST_START();
singlestage::parser parser;
auto json = R"(123_abc)"_padded;
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_int64().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_int64().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_uint64().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_uint64().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_double().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_double().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_number().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_number().error(), TRAILING_CONTENT);
}
TEST_SUCCEED();
}
bool get_root_number_tests() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
padded_string docdata;
singlestage::number number;
singlestage::number_type nt{};
bool intvalue{};
docdata = R"(1.0)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_FALSE(doc.is_negative());
ASSERT_SUCCESS(doc.get_number_type().get(nt));
ASSERT_EQUAL(nt, singlestage::number_type::floating_point_number);
ASSERT_SUCCESS(doc.is_integer().get(intvalue));
ASSERT_SUCCESS(doc.get_number().get(number));
ASSERT_FALSE(intvalue);
ASSERT_EQUAL(number.get_number_type(), singlestage::number_type::floating_point_number);
ASSERT_EQUAL(number.get_double(), 1.0);
docdata = R"(-3.132321)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_TRUE(doc.is_negative());
ASSERT_SUCCESS(doc.get_number_type().get(nt));
ASSERT_EQUAL(nt, singlestage::number_type::floating_point_number);
ASSERT_SUCCESS(doc.is_integer().get(intvalue));
ASSERT_SUCCESS(doc.get_number().get(number));
ASSERT_FALSE(intvalue);
ASSERT_EQUAL(number.get_number_type(), singlestage::number_type::floating_point_number);
ASSERT_EQUAL(number.get_double(), -3.132321);
docdata = R"(1233)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_FALSE(doc.is_negative());
ASSERT_SUCCESS(doc.get_number_type().get(nt));
ASSERT_EQUAL(nt, singlestage::number_type::signed_integer);
ASSERT_SUCCESS(doc.is_integer().get(intvalue));
ASSERT_SUCCESS(doc.get_number().get(number));
ASSERT_TRUE(intvalue);
ASSERT_EQUAL(number.get_number_type(), singlestage::number_type::signed_integer);
ASSERT_EQUAL(number.get_int64(), 1233);
docdata = R"(9999999999999999999)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_FALSE(doc.is_negative());
ASSERT_SUCCESS(doc.get_number_type().get(nt));
ASSERT_EQUAL(nt, singlestage::number_type::unsigned_integer);
ASSERT_SUCCESS(doc.is_integer().get(intvalue));
ASSERT_SUCCESS(doc.get_number().get(number));
ASSERT_TRUE(intvalue);
ASSERT_EQUAL(number.get_number_type(), singlestage::number_type::unsigned_integer);
ASSERT_EQUAL(number.get_uint64(), UINT64_C(9999999999999999999));
TEST_SUCCEED();
}
bool issue2017() {
TEST_START();
singlestage::parser parser;
singlestage::document doc;
padded_string docdata = R"({"score":0.8825149536132812})"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
double score;
ASSERT_SUCCESS(doc["score"].get_double().get(score));
ASSERT_EQUAL(score, 0.8825149536132812);
TEST_SUCCEED();
}
bool run() {
return issue2017() &&
issue_1898() &&
issue1878() &&
get_root_number_tests() &&
get_number_tests()&&
small_integers() &&
powers_of_two() &&
powers_of_ten() &&
old_crashes();
}
} // namespace number_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, number_tests::run);
}
@@ -0,0 +1,573 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace object_error_tests {
using namespace std;
template<typename V, typename T>
bool assert_iterate_object(T &&object, const char **expected_key, V *expected, size_t N, simdjson::error_code *expected_error, size_t N2) {
size_t count = 0;
for (auto field : object) {
V actual{};
auto actual_error = field.value().get(actual);
if (count >= N) {
ASSERT((count - N) < N2, "Extra error reported");
ASSERT_ERROR(actual_error, expected_error[count - N]);
} else {
ASSERT_SUCCESS(actual_error);
ASSERT_EQUAL(field.key().value_unsafe(), expected_key[count]);
ASSERT_EQUAL(actual, expected[count]);
}
count++;
}
ASSERT_EQUAL(count, N+N2);
return true;
}
template<typename V, size_t N, size_t N2, typename T>
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate_object<V, T>(std::forward<T>(object), expected_key, expected, N, expected_error, N2);
}
template<size_t N2, typename T>
bool assert_iterate_object(T &&object, simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate_object<int64_t, T>(std::forward<T>(object), nullptr, nullptr, 0, expected_error, N2);
}
template<typename V, size_t N, typename T>
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N]) {
return assert_iterate_object<V, T>(std::forward<T>(object), expected_key, expected, N, nullptr, 0);
}
bool object_iterate_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_iterate_object(doc.get_object(), { INCORRECT_TYPE, TAPE_ERROR }));
SINGLESTAGE_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
TEST_SUCCEED();
}
bool object_iterate_wrong_key_type_error() {
TEST_START();
SINGLESTAGE_SUBTEST("wrong key type", R"({ 1: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("wrong key type", R"({ true: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("wrong key type", R"({ false: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("wrong key type", R"({ null: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("wrong key type", R"({ []: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("wrong key type", R"({ {}: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
TEST_SUCCEED();
}
bool object_iterate_unclosed_error() {
TEST_START();
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": 1, )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "a" )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool object_iterate_incomplete_error() {
TEST_START();
SINGLESTAGE_SUBTEST("unclosed", R"({ "x": { "a": 1, })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "x": { "a": 1 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "x": { "a": })", assert_iterate_object(doc.get_object(), { INCORRECT_TYPE, INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "x": { "a" })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
SINGLESTAGE_SUBTEST("unclosed", R"({ "x": { })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT }));
TEST_SUCCEED();
}
bool object_lookup_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_error(doc["a"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_error(doc["a"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_success(doc["a"]));
SINGLESTAGE_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_success(doc["a"]));
TEST_SUCCEED();
}
bool object_lookup_unclosed_error() {
TEST_START();
#if SIMDJSON_CHECK_EOF
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
#else
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
#endif
SINGLESTAGE_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
SINGLESTAGE_SUBTEST("unclosed", R"({ )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT));
TEST_SUCCEED();
}
bool object_lookup_miss_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
TEST_SUCCEED();
}
bool object_lookup_miss_wrong_key_type_error() {
TEST_START();
SINGLESTAGE_SUBTEST("wrong key type", R"({ 1: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("wrong key type", R"({ true: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("wrong key type", R"({ false: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("wrong key type", R"({ null: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("wrong key type", R"({ []: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
SINGLESTAGE_SUBTEST("wrong key type", R"({ {}: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR));
TEST_SUCCEED();
}
bool object_lookup_miss_unclosed_error() {
TEST_START();
SINGLESTAGE_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!
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": 1 )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
SINGLESTAGE_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
SINGLESTAGE_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
SINGLESTAGE_SUBTEST("unclosed", R"({ )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
TEST_SUCCEED();
}
bool object_lookup_miss_next_error() {
TEST_START();
SINGLESTAGE_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", ([&]() {
auto obj = doc.get_object();
return assert_result(obj["a"], int64_t(1)) && assert_error(obj["b"], TAPE_ERROR);
})());
TEST_SUCCEED();
}
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
bool out_of_order_object_iteration_error() {
TEST_START();
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
for (auto element : doc) {
auto obj = element.get_object();
for (auto field : obj) { ASSERT_SUCCESS(field); }
ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION );
}
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
for (auto element : doc) {
singlestage::object obj;
ASSERT_SUCCESS( element.get(obj) );
for (auto field : obj) { ASSERT_SUCCESS(field); }
ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION );
}
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_top_level_object_iteration_error() {
TEST_START();
auto json = R"({ "x": 1, "y": 2 })"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
auto obj = doc.get_object();
for (auto field : obj) { ASSERT_SUCCESS(field); }
ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object obj;
ASSERT_SUCCESS( doc.get(obj) );
for (auto field : obj) { ASSERT_SUCCESS(field); }
ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION );
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_index_child_error() {
TEST_START();
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> obj;
for (auto element : doc) {
obj = element.get_object();
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> obj;
for (auto element : doc) {
obj = element;
ASSERT_SUCCESS( obj["x"] );
}
ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
ASSERT_SUCCESS( obj["x"] );
}
ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION );
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_index_sibling_error() {
TEST_START();
auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element.get_object();
uint64_t x;
ASSERT_SUCCESS(obj["x"].get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::object obj;
ASSERT_SUCCESS( element.get_object().get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj["x"].get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element;
uint64_t x;
ASSERT_SUCCESS(obj["x"].get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::value obj;
ASSERT_SUCCESS( element.get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj["x"].get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_find_field_child_error() {
TEST_START();
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> obj;
for (auto element : doc) {
obj = element.get_object();
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> obj;
for (auto element : doc) {
obj = element;
ASSERT_SUCCESS( obj.find_field("x") );
}
ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
ASSERT_SUCCESS( obj.find_field("x") );
}
ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_find_field_sibling_error() {
TEST_START();
auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element.get_object();
uint64_t x;
ASSERT_SUCCESS(obj.find_field("x").get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::object obj;
ASSERT_SUCCESS( element.get_object().get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj.find_field("x").get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element;
uint64_t x;
ASSERT_SUCCESS(obj.find_field("x").get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::value obj;
ASSERT_SUCCESS( element.get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj.find_field("x").get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_find_field_unordered_child_error() {
TEST_START();
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> obj;
for (auto element : doc) {
obj = element.get_object();
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
for (auto field : obj) { ASSERT_SUCCESS(field); }
}
ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> obj;
for (auto element : doc) {
obj = element;
ASSERT_SUCCESS( obj.find_field_unordered("x") );
}
ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value obj;
for (auto element : doc) {
ASSERT_SUCCESS( element.get(obj) );
ASSERT_SUCCESS( obj.find_field_unordered("x") );
}
ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION );
return true;
}));
TEST_SUCCEED();
}
bool out_of_order_object_find_field_unordered_sibling_error() {
TEST_START();
auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded;
SUBTEST("simdjson_result<object>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::object> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element.get_object();
uint64_t x;
ASSERT_SUCCESS(obj.find_field_unordered("x").get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("object", test_singlestage_doc(json, [&](auto doc) {
singlestage::object last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::object obj;
ASSERT_SUCCESS( element.get_object().get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj.find_field_unordered("x").get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("simdjson_result<value>", test_singlestage_doc(json, [&](auto doc) {
simdjson_result<singlestage::value> last_obj;
uint64_t i = 0;
for (auto element : doc) {
auto obj = element;
uint64_t x;
ASSERT_SUCCESS(obj.find_field_unordered("x").get(x));
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
SUBTEST("value", test_singlestage_doc(json, [&](auto doc) {
singlestage::value last_obj;
uint64_t i = 0;
for (auto element : doc) {
singlestage::value obj;
ASSERT_SUCCESS( element.get(obj) );
uint64_t x;
ASSERT_SUCCESS( obj.find_field_unordered("x").get(x) );
ASSERT_EQUAL(x, i);
if (i > 0) {
ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION);
break;
}
last_obj = obj;
i++;
}
return true;
}));
TEST_SUCCEED();
}
#endif
bool run() {
return
object_iterate_error() &&
object_iterate_wrong_key_type_error() &&
object_iterate_unclosed_error() &&
object_lookup_error() &&
object_lookup_unclosed_error() &&
object_lookup_miss_error() &&
object_lookup_miss_unclosed_error() &&
object_lookup_miss_wrong_key_type_error() &&
object_lookup_miss_next_error() &&
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
out_of_order_object_iteration_error() &&
out_of_order_top_level_object_iteration_error() &&
out_of_order_object_index_child_error() &&
out_of_order_object_index_sibling_error() &&
out_of_order_object_find_field_child_error() &&
out_of_order_object_find_field_sibling_error() &&
out_of_order_object_find_field_unordered_child_error() &&
out_of_order_object_find_field_unordered_sibling_error() &&
#endif
true;
}
} // namespace object_error_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, object_error_tests::run);
}
@@ -0,0 +1,191 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace object_tests {
using namespace std;
using simdjson::singlestage::json_type;
bool object_find_field_unordered() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::object>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::document>", test_singlestage_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("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::object>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::document>", test_singlestage_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("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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);
}
@@ -0,0 +1,432 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace object_tests {
using namespace std;
using simdjson::singlestage::json_type;
bool object_index() {
TEST_START();
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
SUBTEST("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::object>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::document>", test_singlestage_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("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::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<singlestage::document>", test_singlestage_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::object>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::object> object = doc_result.get_object();
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
simdjson_result<singlestage::value> x = doc_result["x"];
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
return true;
}));
SUBTEST("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::document>", test_singlestage_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("singlestage::object", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::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<singlestage::document>", test_singlestage_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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,167 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace ordering_tests {
using namespace std;
#if SIMDJSON_EXCEPTIONS
auto json = "{\"coordinates\":[{\"x\":1.1,\"y\":2.2,\"z\":3.3}]}"_padded;
bool in_order_object_index() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
x += double(point_object["x"]);
y += double(point_object["y"]);
z += double(point_object["z"]);
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool in_order_object_find_field_unordered() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
x += double(point_object.find_field_unordered("x"));
y += double(point_object.find_field_unordered("y"));
z += double(point_object.find_field_unordered("z"));
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool in_order_object_find_field() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
x += double(point_object.find_field("x"));
y += double(point_object.find_field("y"));
z += double(point_object.find_field("z"));
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool out_of_order_object_index() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
z += double(point_object["z"]);
x += double(point_object["x"]);
y += double(point_object["y"]);
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool out_of_order_object_find_field_unordered() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
z += double(point_object.find_field_unordered("z"));
x += double(point_object.find_field_unordered("x"));
y += double(point_object.find_field_unordered("y"));
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool out_of_order_object_find_field() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (singlestage::object point_object : doc["coordinates"]) {
z += double(point_object.find_field("z"));
ASSERT_ERROR( point_object.find_field("x"), NO_SUCH_FIELD );
ASSERT_ERROR( point_object.find_field("y"), NO_SUCH_FIELD );
}
return (x == 0) && (y == 0) && (z == 3.3);
}
bool foreach_object_field_lookup() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
double x{0};
double y{0};
double z{0};
for (auto point_object : doc["coordinates"]) {
for (auto field : point_object.get_object()) {
if (field.key() == "z") { z += double(field.value()); }
else if (field.key() == "x") { x += double(field.value()); }
else if (field.key() == "y") { y += double(field.value()); }
}
}
return (x == 1.1) && (y == 2.2) && (z == 3.3);
}
bool use_values_out_of_order_after_array() {
TEST_START();
singlestage::parser parser{};
auto doc = parser.iterate(json);
simdjson_result<singlestage::value> x{}, y{}, z{};
for (auto point_object : doc["coordinates"]) {
x = point_object["x"];
y = point_object["y"];
z = point_object["z"];
}
return (double(x) == 1.1) && (double(z) == 3.3) && (double(y) == 2.2);
}
bool use_object_multiple_times_out_of_order() {
TEST_START();
singlestage::parser parser{};
auto json2 = "{\"coordinates\":{\"x\":1.1,\"y\":2.2,\"z\":3.3}}"_padded;
auto doc = parser.iterate(json2);
auto x = doc["coordinates"]["x"];
auto y = doc["coordinates"]["y"];
auto z = doc["coordinates"]["z"];
return (double(x) == 1.1) && (double(z) == 3.3) && (double(y) == 2.2);
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
#if SIMDJSON_EXCEPTIONS
in_order_object_index() &&
in_order_object_find_field_unordered() &&
in_order_object_find_field() &&
out_of_order_object_index() &&
out_of_order_object_find_field_unordered() &&
out_of_order_object_find_field() &&
foreach_object_field_lookup() &&
use_values_out_of_order_after_array() &&
use_object_multiple_times_out_of_order() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace ordering_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, ordering_tests::run);
}
@@ -0,0 +1,278 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace parse_api_tests {
using namespace std;
const padded_string BASIC_JSON = "[1,2,3]"_padded;
const padded_string BASIC_NDJSON = "[1,2,3]\n[4,5,6]"_padded;
const padded_string EMPTY_NDJSON = ""_padded;
bool parser_iterate_empty() {
TEST_START();
FILE *p;
// Of course, we could just call iterate on the empty string, but
// we want to test the whole process.
const char *const tmpfilename = "emptysinglestage.txt";
if((p = fopen(tmpfilename, "w")) != nullptr) {
fclose(p);
auto json = padded_string::load(tmpfilename);
singlestage::document doc;
singlestage::parser parser;
auto error = parser.iterate(json).get(doc);
remove(tmpfilename);
if(error != simdjson::EMPTY) {
std::cerr << "Was expecting empty but got " << error << std::endl;
return false;
}
} else {
std::cout << "Warning: I could not create temporary file " << tmpfilename << std::endl;
std::cout << "We omit testing the empty file case." << std::endl;
}
TEST_SUCCEED();
}
bool parser_iterate() {
TEST_START();
singlestage::parser parser;
auto doc = parser.iterate(BASIC_JSON);
ASSERT_SUCCESS( doc.get_array() );
return true;
}
bool parser_iterate_padded() {
TEST_START();
singlestage::parser parser;
const char json_str[] = "12\0 ";// 64 bytes of padding
ASSERT_EQUAL(sizeof(json_str), 66);
ASSERT_EQUAL(strlen(json_str), 2);
{
cout << "- char*" << endl;
auto doc = parser.iterate(json_str, strlen(json_str), sizeof(json_str));
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- uint8_t*" << endl;
const uint8_t* json = reinterpret_cast<const uint8_t*>(json_str);
auto doc = parser.iterate(json, strlen(json_str), sizeof(json_str));
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- string_view" << endl;
std::string_view json(json_str);
auto doc = parser.iterate(json, sizeof(json_str));
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- string" << endl;
std::string json = "12";
json.reserve(json.length() + SIMDJSON_PADDING);
auto doc = parser.iterate(json);
ASSERT_SUCCESS( doc.get_double() );
}
TEST_SUCCEED();
}
bool parser_iterate_padded_string_view() {
TEST_START();
singlestage::parser parser;
const char json_str[] = "12\0 "; // 64 bytes of padding
ASSERT_EQUAL(sizeof(json_str), 66);
ASSERT_EQUAL(strlen(json_str), 2);
{
cout << "- padded_string_view(string_view)" << endl;
padded_string_view json(std::string_view(json_str), sizeof(json_str));
auto doc = parser.iterate(json);
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- padded_string_view(char*)" << endl;
auto doc = parser.iterate(padded_string_view(json_str, strlen(json_str), sizeof(json_str)));
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- padded_string_view(string)" << endl;
std::string json = "12";
json.reserve(json.length() + SIMDJSON_PADDING);
auto doc = parser.iterate(padded_string_view(json));
ASSERT_SUCCESS( doc.get_double() );
}
{
cout << "- padded_string_view(string_view(char*))" << endl;
padded_string_view json(json_str, sizeof(json_str));
auto doc = parser.iterate(json);
ASSERT_SUCCESS( doc.get_double() );
}
TEST_SUCCEED();
}
bool parser_iterate_insufficient_padding() {
TEST_START();
singlestage::parser parser;
constexpr char json_str[] = "12\0 "; // 63 bytes of padding
ASSERT_EQUAL(sizeof(json_str), 65);
ASSERT_EQUAL(strlen(json_str), 2);
ASSERT_EQUAL(padded_string_view(json_str, strlen(json_str), sizeof(json_str)).padding(), 63);
ASSERT_EQUAL(SIMDJSON_PADDING, 64);
{
cout << "- char*, 63 padding" << endl;
ASSERT_ERROR( parser.iterate(json_str, strlen(json_str), sizeof(json_str)), INSUFFICIENT_PADDING );
cout << "- char*, 0 padding" << endl;
ASSERT_ERROR( parser.iterate(json_str, strlen(json_str), strlen(json_str)), INSUFFICIENT_PADDING );
}
{
std::string_view json(json_str);
cout << "- string_view, 63 padding" << endl;
ASSERT_ERROR( parser.iterate(json, sizeof(json_str)), INSUFFICIENT_PADDING );
cout << "- string_view, 0 padding" << endl;
ASSERT_ERROR( parser.iterate(json, strlen(json_str)), INSUFFICIENT_PADDING );
}
{
std::string json = "12";
json.shrink_to_fit();
cout << "- string, 0 padding" << endl;
ASSERT_ERROR( parser.iterate(json), INSUFFICIENT_PADDING );
// It's actually kind of hard to allocate "just enough" capacity, since the string tends
// to grow more than you tell it to.
}
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool parser_iterate_exception() {
TEST_START();
singlestage::parser parser;
auto doc = parser.iterate(BASIC_JSON);
simdjson_unused singlestage::array array = doc;
TEST_SUCCEED();
}
bool parser_document_reuse() {
TEST_START();
singlestage::document doc;
// A document spans about 40 bytes. Nevertheless, some users
// would rather reuse them.
std::cout << sizeof(doc) << std::endl;
auto json = R"({"key": "value"})"_padded;
auto jsonbad = R"({"key": "value")"_padded; // deliberaty broken
auto jsonunclosedstring = "{\"coordinates:[{\"x\":1.1,\"y\":2.2,\"z\":3.3}]}"_padded;
std::string_view output;
singlestage::parser parser;
std::cout << "correct document (1)" << std::endl;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
ASSERT_SUCCESS(simdjson::to_json_string(doc).get(output));
std::cout << output << std::endl;
std::cout << "correct document (2)" << std::endl;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
for(singlestage::field field : doc.get_object() ) {
std::cout << "field: " << field.key() << std::endl;
}
std::cout << "unclosed string document " << std::endl;
simdjson::error_code error;
if((error = parser.iterate(jsonunclosedstring).get(doc)) == SUCCESS) {
// fallback kernel:
ASSERT_EQUAL( doc.get_object().find_field("coordinates").error(), TAPE_ERROR );
} else {
// regular kernels:
ASSERT_EQUAL( error, UNCLOSED_STRING );
}
std::cout << "truncated document " << std::endl;
ASSERT_SUCCESS( parser.iterate(jsonbad).get(doc) );
ASSERT_EQUAL( simdjson::to_json_string(doc).get(output), TAPE_ERROR );
std::cout << "correct document with new doc" << std::endl;
singlestage::document doc2;
ASSERT_SUCCESS( parser.iterate(json).get(doc2) );
for(singlestage::field field : doc2.get_object() ) {
std::cout << "field: " << field.key() << std::endl;
}
std::cout << "correct document (3): " << doc.to_debug_string() << std::endl;
std::cout << "correct document (3)" << std::endl;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
std::cout << doc.to_debug_string() << std::endl;
for(singlestage::field field : doc.get_object() ) {
std::cout << "field: " << field.key() << std::endl;
}
std::cout << "unclosed string document " << std::endl;
ASSERT_SUCCESS( parser.iterate(jsonbad).get(doc) );
ASSERT_EQUAL( simdjson::to_json_string(doc).get(output), TAPE_ERROR );
// next two lines are terrible code.
doc.~document();
doc = singlestage::document();
//
std::cout << "correct document (4)" << std::endl;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(output) );
std::cout << output << std::endl;
std::cout << "unclosed string document " << std::endl;
if((error = parser.iterate(jsonunclosedstring).get(doc)) == SUCCESS) {
// fallback kernel:
ASSERT_EQUAL( doc.get_object().find_field("coordinates").error(), TAPE_ERROR );
} else {
// regular kernels:
ASSERT_EQUAL( error, UNCLOSED_STRING );
}
// next two lines are terrible code.
doc.~document();
doc = singlestage::document();
//
std::cout << "correct document (5)" << std::endl;
ASSERT_SUCCESS( parser.iterate(json).get(doc) );
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(output) );
std::cout << output << std::endl;
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return parser_iterate_empty() &&
parser_iterate() &&
parser_iterate_padded() &&
parser_iterate_padded_string_view() &&
parser_iterate_insufficient_padding() &&
#if SIMDJSON_EXCEPTIONS
parser_document_reuse() &&
parser_iterate_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, parse_api_tests::run);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,402 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace scalar_tests {
using namespace std;
using simdjson::singlestage::json_type;
template<typename T> json_type expected_json_type();
template<> json_type expected_json_type<std::string_view>() { return json_type::string; }
template<> json_type expected_json_type<double>() { return json_type::number; }
template<> json_type expected_json_type<uint64_t>() { return json_type::number; }
template<> json_type expected_json_type<int64_t>() { return json_type::number; }
template<> json_type expected_json_type<bool>() { return json_type::boolean; }
template<typename T>
bool test_scalar_value(const padded_string &json, const T &expected, bool test_twice=true) {
std::cout << "- JSON: " << json << endl;
SUBTEST( "simdjson_result<document>", test_singlestage_doc(json, [&](auto doc_result) {
T actual{};
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
}
return true;
}));
SUBTEST( "document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
T actual{};
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
}
return true;
}));
{
padded_string whitespace_json = std::string(json) + " ";
std::cout << "- JSON: " << whitespace_json << endl;
SUBTEST( "simdjson_result<document>", test_singlestage_doc(whitespace_json, [&](auto doc_result) {
T actual{};
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
}
return true;
}));
SUBTEST( "document", test_singlestage_doc(whitespace_json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
T actual{};
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
}
return true;
}));
}
{
padded_string array_json = std::string("[") + std::string(json) + "]";
std::cout << "- JSON: " << array_json << endl;
SUBTEST( "simdjson_result<value>", test_singlestage_doc(array_json, [&](auto doc_result) {
int count = 0;
for (simdjson_result<singlestage::value> val_result : doc_result) {
T actual{};
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
}
count++;
}
ASSERT_EQUAL(count, 1);
return true;
}));
SUBTEST( "value", test_singlestage_doc(array_json, [&](auto doc_result) {
int count = 0;
for (simdjson_result<singlestage::value> val_result : doc_result) {
singlestage::value val;
ASSERT_SUCCESS( val_result.get(val) );
T actual{};
ASSERT_RESULT( val.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val.type(), expected_json_type<T>() );
}
count++;
}
ASSERT_EQUAL(count, 1);
return true;
}));
}
{
padded_string whitespace_array_json = std::string("[") + std::string(json) + " ]";
std::cout << "- JSON: " << whitespace_array_json << endl;
SUBTEST( "simdjson_result<value>", test_singlestage_doc(whitespace_array_json, [&](auto doc_result) {
int count = 0;
for (simdjson_result<singlestage::value> val_result : doc_result) {
T actual{};
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
}
count++;
}
ASSERT_EQUAL(count, 1);
return true;
}));
SUBTEST( "value", test_singlestage_doc(whitespace_array_json, [&](auto doc_result) {
int count = 0;
for (simdjson_result<singlestage::value> val_result : doc_result) {
singlestage::value val;
ASSERT_SUCCESS( val_result.get(val) );
T actual{};
ASSERT_RESULT( val.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once)
if (test_twice) {
ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
}
count++;
}
ASSERT_EQUAL(count, 1);
return true;
}));
}
TEST_SUCCEED();
}
bool string_value() {
TEST_START();
// We can't retrieve a small string twice because it will blow out the string buffer
if (!test_scalar_value(R"("hi")"_padded, std::string_view("hi"), false)) { return false; }
// ... unless the document is big enough to have a big string buffer :)
if (!test_scalar_value(R"("hi" )"_padded, std::string_view("hi"))) { return false; }
TEST_SUCCEED();
}
bool numeric_values() {
TEST_START();
if (!test_scalar_value<int64_t> ("0"_padded, 0)) { return false; }
if (!test_scalar_value<uint64_t>("0"_padded, 0)) { return false; }
if (!test_scalar_value<double> ("0"_padded, 0)) { return false; }
if (!test_scalar_value<int64_t> ("1"_padded, 1)) { return false; }
if (!test_scalar_value<uint64_t>("1"_padded, 1)) { return false; }
if (!test_scalar_value<double> ("1"_padded, 1)) { return false; }
if (!test_scalar_value<int64_t> ("-1"_padded, -1)) { return false; }
if (!test_scalar_value<double> ("-1"_padded, -1)) { return false; }
if (!test_scalar_value<double> ("1.1"_padded, 1.1)) { return false; }
TEST_SUCCEED();
}
bool boolean_values() {
TEST_START();
if (!test_scalar_value<bool> ("true"_padded, true)) { return false; }
if (!test_scalar_value<bool> ("false"_padded, false)) { return false; }
TEST_SUCCEED();
}
bool null_value() {
TEST_START();
auto json = "null"_padded;
SUBTEST("singlestage::document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
bool is_null_value;
ASSERT_SUCCESS( doc.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
return true;
}));
SUBTEST("simdjson_result<singlestage::document>", test_singlestage_doc(json, [&](auto doc_result) {
bool is_null_value;
ASSERT_SUCCESS( doc_result.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
return true;
}));
json = "[null]"_padded;
SUBTEST("singlestage::value", test_singlestage_doc(json, [&](auto doc_result) {
int count = 0;
for (auto value_result : doc_result) {
singlestage::value value;
ASSERT_SUCCESS( value_result.get(value) );
bool is_null_value;
ASSERT_SUCCESS( value.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
count++;
}
ASSERT_EQUAL( count, 1 );
return true;
}));
SUBTEST("simdjson_result<singlestage::value>", test_singlestage_doc(json, [&](auto doc_result) {
int count = 0;
for (auto value_result : doc_result) {
bool is_null_value;
ASSERT_SUCCESS( value_result.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
count++;
}
ASSERT_EQUAL( count, 1 );
return true;
}));
return true;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
bool test_scalar_value_exception(const padded_string &json, const T &expected) {
std::cout << "- JSON: " << json << endl;
SUBTEST( "document", test_singlestage_doc(json, [&](auto doc_result) {
singlestage::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( expected, T(doc) );
return true;
}));
padded_string array_json = std::string("[") + std::string(json) + "]";
std::cout << "- JSON: " << array_json << endl;
SUBTEST( "value", test_singlestage_doc(array_json, [&](auto doc_result) {
int count = 0;
for (T actual : doc_result) {
ASSERT_EQUAL( actual, expected );
count++;
}
ASSERT_EQUAL(count, 1);
return true;
}));
TEST_SUCCEED();
}
bool string_value_exception() {
TEST_START();
return test_scalar_value_exception(R"("hi")"_padded, std::string_view("hi"));
}
bool numeric_values_exception() {
TEST_START();
if (!test_scalar_value_exception<int64_t> ("0"_padded, 0)) { return false; }
if (!test_scalar_value_exception<uint64_t>("0"_padded, 0)) { return false; }
if (!test_scalar_value_exception<double> ("0"_padded, 0)) { return false; }
if (!test_scalar_value_exception<int64_t> ("1"_padded, 1)) { return false; }
if (!test_scalar_value_exception<uint64_t>("1"_padded, 1)) { return false; }
if (!test_scalar_value_exception<double> ("1"_padded, 1)) { return false; }
if (!test_scalar_value_exception<int64_t> ("-1"_padded, -1)) { return false; }
if (!test_scalar_value_exception<double> ("-1"_padded, -1)) { return false; }
if (!test_scalar_value_exception<double> ("1.1"_padded, 1.1)) { return false; }
TEST_SUCCEED();
}
bool boolean_values_exception() {
TEST_START();
if (!test_scalar_value_exception<bool> ("true"_padded, true)) { return false; }
if (!test_scalar_value_exception<bool> ("false"_padded, false)) { return false; }
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool string_with_trailing() {
TEST_START();
auto json = R"( "a string" badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR(doc.get_string().get(view), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool uint64_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
uint64_t x;
ASSERT_ERROR(doc.get_uint64().get(x), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool int64_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
int64_t x;
ASSERT_ERROR(doc.get_int64().get(x), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool double_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
double x;
ASSERT_ERROR(doc.get_double().get(x), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool bool_with_trailing() {
TEST_START();
auto json = R"( true badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
bool x;
ASSERT_ERROR(doc.get_bool().get(x), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool null_with_trailing() {
TEST_START();
auto json = R"( null badstuff )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.is_null(), TRAILING_CONTENT);
TEST_SUCCEED();
}
bool nully() {
TEST_START();
auto json = R"( nully )"_padded;
singlestage::parser parser;
singlestage::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
bool x;
ASSERT_SUCCESS(doc.is_null().get(x));
ASSERT_TRUE(!x);
TEST_SUCCEED();
}
bool run() {
return
nully() &&
string_with_trailing() &&
uint64_with_trailing() &&
int64_with_trailing() &&
bool_with_trailing() &&
null_with_trailing() &&
string_value() &&
numeric_values() &&
boolean_values() &&
null_value() &&
#if SIMDJSON_EXCEPTIONS
string_value_exception() &&
numeric_values_exception() &&
boolean_values_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace scalar_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, scalar_tests::run);
}
@@ -0,0 +1,465 @@
#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include <unistd.h>
#include <utility>
#include <vector>
#include "simdjson.h"
/**
* What follows is merely a compilation test to verify
* https://github.com/simdjson/simdjson/issues/1768
* It deliberately resides before any namespace declaration
* within this file.
*/
namespace issue1768 {
template <typename T> void print(const T &a) { std::cout << a; }
} // namespace NA
void issue1768_test() {
simdjson::singlestage::object obj;
issue1768::print(obj);
}
/**
* End of 1768 compilation check.
*/
using namespace simdjson;
#include "test_singlestage.h"
namespace tostring_tests {
const char *test_files[] = {
TWITTER_JSON, TWITTER_TIMELINE_JSON, REPEAT_JSON, CANADA_JSON,
MESH_JSON, APACHE_JSON, GSOC_JSON};
#if SIMDJSON_EXCEPTIONS
bool issue1607() {
TEST_START();
auto silly_json = R"( { "test": "result" } )"_padded;
singlestage::parser parser;
singlestage::document doc = parser.iterate(silly_json);
std::string_view expected = R"("result")";
std::string_view result = simdjson::to_json_string(doc["test"]);
std::cout << "'"<< result << "'" << std::endl;
ASSERT_EQUAL(result, expected);
TEST_SUCCEED();
}
bool minify_demo() {
TEST_START();
singlestage::parser parser;
auto silly_json = R"( { "test": "result" } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(silly_json).get(doc) );
std::cout << simdjson::to_json_string(doc["test"]) << std::endl;
TEST_SUCCEED();
}
bool minify_demo2() {
TEST_START();
singlestage::parser parser;
auto silly_json = R"( { "test": "result" } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(silly_json).get(doc) );
std::cout << std::string_view(doc["test"]) << std::endl;
TEST_SUCCEED();
}
bool car_example() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
std::vector<std::string_view> arrays;
// We are going to collect string_view instances which point inside the `cars_json` string
// and are therefore valid as long as `cars_json` remains in scope.
{
singlestage::parser parser;
for (singlestage::object car : parser.iterate(cars_json)) {
if(uint64_t(car["year"]) > 2000) { // Pick the recent cars only!
arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
}
}
}
// We can now convert to a JSON string:
std::ostringstream oss;
oss << "[";
for(size_t i = 0; i < arrays.size(); i++) {
if(i>0) { oss << ","; }
oss << arrays[i];
}
oss << "]";
auto json_string = oss.str();
ASSERT_EQUAL(json_string, "[[ 40.1, 39.9, 37.7, 40.4 ],[ 30.1, 31.0, 28.6, 28.7 ]]");
TEST_SUCCEED();
}
/**
* The general idea of these tests if that if you take a JSON file,
* load it, then convert it into a string, then parse that, and
* convert it again into a second string, then the two strings should
* be identifical. If not, then something was lost or added in the
* process.
*/
bool load_to_string(const char *filename) {
simdjson::singlestage::parser parser;
std::cout << "Loading " << filename << std::endl;
simdjson::padded_string docdata;
auto error = simdjson::padded_string::load(filename).get(docdata);
if (error) {
std::cerr << "could not load " << filename << " got " << error << std::endl;
return false;
}
std::cout << "file loaded: " << docdata.size() << " bytes." << std::endl;
simdjson::singlestage::document doc;
auto silly_json = R"( { "test": "result" } )"_padded;
error = parser.iterate(silly_json).get(doc);
if (error) {
std::cerr << error << std::endl;
return false;
}
std::cout << "serializing once." << std::endl;
std::string_view serial1 = simdjson::to_json_string(doc);
error = parser.iterate(serial1, serial1.size() + simdjson::SIMDJSON_PADDING).get(doc);
if (error) {
std::cerr << error << std::endl;
return false;
}
std::cout << "serializing twice." << std::endl;
std::string_view serial2 = simdjson::to_json_string(doc);
bool match = (serial1 == serial2);
if (match) {
std::cout << "Parsing to_string and calling to_string again results in the "
"same content. "
<< "Got " << serial1.size() << " bytes." << std::endl;
}
return match;
}
bool minify_test() {
TEST_START();
for (size_t i = 0; i < sizeof(test_files) / sizeof(test_files[0]); i++) {
bool ok = load_to_string(test_files[i]);
if (!ok) {
return false;
}
}
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool load_to_string_exceptionless(const char *filename) {
simdjson::singlestage::parser parser;
std::cout << "Loading " << filename << std::endl;
simdjson::padded_string docdata;
auto error = simdjson::padded_string::load(filename).get(docdata);
if (error) {
std::cerr << "could not load " << filename << " got " << error << std::endl;
return false;
}
std::cout << "file loaded: " << docdata.size() << " bytes." << std::endl;
simdjson::singlestage::document doc;
error = parser.iterate(docdata).get(doc);
if (error) {
std::cerr << error << std::endl;
return false;
}
std::cout << "serializing once." << std::endl;
std::string_view serial1;
error = simdjson::to_json_string(doc).get(serial1);
if (error) {
std::cerr << error << std::endl;
return false;
}
error = parser.iterate(serial1, serial1.size() + simdjson::SIMDJSON_PADDING).get(doc);
if (error) {
std::cerr << error << std::endl;
return false;
}
std::cout << "serializing twice." << std::endl;
std::string_view serial2;
error = simdjson::to_json_string(doc).get(serial2);
if (error) {
std::cerr << error << std::endl;
return false;
}
bool match = (serial1 == serial2);
if (match) {
std::cout << "Parsing to_string and calling to_string again results in the "
"same content. "
<< "Got " << serial1.size() << " bytes." << std::endl;
}
return match;
}
bool minify_exceptionless_test() {
TEST_START();
for (size_t i = 0; i < sizeof(test_files) / sizeof(test_files[0]); i++) {
bool ok = load_to_string_exceptionless(test_files[i]);
if (!ok) {
return false;
}
}
return true;
}
bool empty_object() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"({})"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc));
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"({})");
TEST_SUCCEED();
}
bool empty_array() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"([])"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc));
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"([])");
TEST_SUCCEED();
}
bool single_digit_document() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"(9)"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"(9)");
TEST_SUCCEED();
}
bool single_string_document() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"("")"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"("")");
TEST_SUCCEED();
}
bool at_start_array() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( [111,2,3,5] )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::array array;
ASSERT_SUCCESS( doc.get_array().get(array) );
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(array).get(serial));
ASSERT_EQUAL(serial, "[111,2,3,5]");
TEST_SUCCEED();
}
bool at_start_object() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( {"a":1, "b":2, "c": 3 } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::object object;
ASSERT_SUCCESS( doc.get_object().get(object) );
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
TEST_SUCCEED();
}
bool in_middle_array() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( [111,{"a":1},3,5] )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::array array;
ASSERT_SUCCESS( doc.get_array().get(array) );
auto i = array.begin();
int64_t x;
ASSERT_SUCCESS( (*i).get_int64().get(x) );
ASSERT_EQUAL(x, 111);
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(array).get(serial));
ASSERT_EQUAL(serial, "[111,{\"a\":1},3,5]");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, "[111,{\"a\":1},3,5]");
TEST_SUCCEED();
}
bool at_middle_object() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( {"a":1, "b":2, "c": 3 } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::object object;
ASSERT_SUCCESS( doc.get_object().get(object) );
int64_t x;
ASSERT_SUCCESS(object["b"].get_int64().get(x));
ASSERT_EQUAL(x,2);
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
TEST_SUCCEED();
}
bool at_middle_object_just_key() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( {"a":1, "b":2, "c": 3 } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::object object;
ASSERT_SUCCESS( doc.get_object().get(object) );
singlestage::value x;
ASSERT_SUCCESS(object["b"].get(x));
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
TEST_SUCCEED();
}
bool at_end_object() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( {"a":1, "b":2, "c": 3 } )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::object object;
ASSERT_SUCCESS( doc.get_object().get(object) );
int64_t x;
ASSERT_ERROR(object["bcc"].get_int64().get(x), NO_SUCH_FIELD);
std::string_view serial;
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"({"a":1, "b":2, "c": 3 })");
TEST_SUCCEED();
}
bool at_array_end() {
TEST_START();
singlestage::parser parser;
std::string_view serial;
auto arr_json = R"( [111,2,3,5] )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::array array;
ASSERT_SUCCESS( doc.get_array().get(array) );
auto i = array.begin();
int64_t x;
ASSERT_SUCCESS( (*i).get_int64().get(x) );
ASSERT_EQUAL(x, 111);
++i;
ASSERT_SUCCESS( (*i).get_int64().get(x) );
ASSERT_EQUAL(x, 2);
++i;
ASSERT_SUCCESS( (*i).get_int64().get(x) );
ASSERT_EQUAL(x, 3);
++i;
ASSERT_SUCCESS( (*i).get_int64().get(x) );
ASSERT_EQUAL(x, 5);
ASSERT_SUCCESS( simdjson::to_json_string(array).get(serial));
ASSERT_EQUAL(serial, "[111,2,3,5]");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, "[111,2,3,5]");
TEST_SUCCEED();
}
bool complex_case() {
TEST_START();
singlestage::parser parser;
auto arr_json = R"( {"array":[1,2,3], "objects":[{"id":1}, {"id":2}, {"id":3}]} )"_padded;
singlestage::document doc;
ASSERT_SUCCESS( parser.iterate(arr_json).get(doc) );
singlestage::object obj;
ASSERT_SUCCESS( doc.get_object().get(obj) );
singlestage::array array;
ASSERT_SUCCESS( obj["objects"].get_array().get(array) );
std::string_view serial;
for(auto v : array) {
singlestage::object object;
ASSERT_SUCCESS( v.get_object().get(object) );
int64_t x;
ASSERT_SUCCESS(object["id"].get_int64().get(x));
if(x / 2 * 2 != x) {
ASSERT_SUCCESS( simdjson::to_json_string(object).get(serial));
ASSERT_EQUAL(serial, "{\"id\":"+std::to_string(x)+"}");
}
}
ASSERT_SUCCESS( simdjson::to_json_string(array).get(serial));
ASSERT_EQUAL(serial, R"([{"id":1}, {"id":2}, {"id":3}])");
ASSERT_SUCCESS( simdjson::to_json_string(obj).get(serial));
ASSERT_EQUAL(serial, R"({"array":[1,2,3], "objects":[{"id":1}, {"id":2}, {"id":3}]})");
ASSERT_SUCCESS( simdjson::to_json_string(doc).get(serial));
ASSERT_EQUAL(serial, R"({"array":[1,2,3], "objects":[{"id":1}, {"id":2}, {"id":3}]})");
TEST_SUCCEED();
}
bool run() {
return
empty_object() &&
empty_array() &&
single_digit_document() &&
single_string_document() &&
complex_case() &&
at_start_object() &&
at_middle_object() &&
at_middle_object_just_key() &&
at_end_object() &&
at_start_array() &&
in_middle_array() &&
at_array_end() &&
#if SIMDJSON_EXCEPTIONS
issue1607() &&
minify_demo() &&
minify_demo2() &&
minify_test() &&
car_example() &&
#endif // SIMDJSON_EXCEPTIONS
minify_exceptionless_test() &&
true;
}
} // namespace tostring_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, tostring_tests::run);
}
@@ -0,0 +1,197 @@
#include <set>
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace twitter_tests {
using namespace std;
bool twitter_count() {
TEST_START();
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
uint64_t count;
ASSERT_SUCCESS( doc_result["search_metadata"]["count"].get(count) );
ASSERT_EQUAL( count, 100 );
return true;
}));
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool twitter_example() {
TEST_START();
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
singlestage::parser parser;
auto doc = parser.iterate(json);
for (singlestage::object tweet : doc["statuses"]) {
uint64_t id = tweet["id"];
std::string_view text = tweet["text"];
std::string_view screen_name = tweet["user"]["screen_name"];
uint64_t retweets = tweet["retweet_count"];
uint64_t favorites = tweet["favorite_count"];
(void) id;
(void) text;
(void) retweets;
(void) favorites;
(void) screen_name;
}
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool twitter_default_profile() {
TEST_START();
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
// Print users with a default profile.
set<string_view> default_users;
for (auto tweet : doc_result["statuses"]) {
auto user = tweet["user"].get_object();
// We have to get the screen name before default_profile because it appears first
std::string_view screen_name;
ASSERT_SUCCESS( user["screen_name"].get(screen_name) );
bool default_profile{};
ASSERT_SUCCESS( user["default_profile"].get(default_profile) );
if (default_profile) {
default_users.insert(screen_name);
}
}
ASSERT_EQUAL( default_users.size(), 86 );
return true;
}));
TEST_SUCCEED();
}
bool twitter_image_sizes() {
TEST_START();
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
// Print image names and sizes
set<pair<uint64_t, uint64_t>> image_sizes;
for (auto tweet : doc_result["statuses"]) {
auto media = tweet["entities"]["media"];
if (!media.error()) {
for (auto image : media) {
uint64_t id_val;
std::string_view id_string;
ASSERT_SUCCESS( image["id"].get(id_val) );
ASSERT_SUCCESS( image["id_str"].get(id_string) );
std::cout << "id = " << id_val << std::endl;
std::cout << "id_string = " << id_string << std::endl;
for (auto size : image["sizes"].get_object()) {
std::string_view size_key;
ASSERT_SUCCESS( size.unescaped_key().get(size_key) );
std::cout << "Type of image size = " << size_key << std::endl;
uint64_t width, height;
ASSERT_SUCCESS( size.value()["w"].get(width) );
ASSERT_SUCCESS( size.value()["h"].get(height) );
image_sizes.insert(make_pair(width, height));
}
}
}
}
ASSERT_EQUAL( image_sizes.size(), 15 );
return true;
}));
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool twitter_count_exception() {
TEST_START();
padded_string json;
ASSERT_SUCCESS( padded_string::load(TWITTER_JSON).get(json) );
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
uint64_t count = doc_result["search_metadata"]["count"];
ASSERT_EQUAL( count, 100 );
return true;
}));
TEST_SUCCEED();
}
bool twitter_default_profile_exception() {
TEST_START();
padded_string json = padded_string::load(TWITTER_JSON);
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
// Print users with a default profile.
set<string_view> default_users;
for (auto tweet : doc_result["statuses"]) {
singlestage::object user = tweet["user"];
// We have to get the screen name before default_profile because it appears first
std::string_view screen_name = user["screen_name"];
if (user["default_profile"]) {
default_users.insert(screen_name);
}
}
ASSERT_EQUAL( default_users.size(), 86 );
return true;
}));
TEST_SUCCEED();
}
/*
* Fun fact: id and id_str can differ:
* 505866668485386240 and 505866668485386241.
* Presumably, it is because doubles are used
* at some point in the process and the number
* 505866668485386241 cannot be represented as a double.
* (not our fault)
*/
bool twitter_image_sizes_exception() {
TEST_START();
padded_string json = padded_string::load(TWITTER_JSON);
ASSERT_TRUE(test_singlestage_doc(json, [&](auto doc_result) {
// Print image names and sizes
set<pair<uint64_t, uint64_t>> image_sizes;
for (auto tweet : doc_result["statuses"]) {
auto media = tweet["entities"]["media"];
if (!media.error()) {
for (auto image : media) {
std::cout << "id = " << uint64_t(image["id"]) << std::endl;
std::cout << "id_string = " << std::string_view(image["id_str"]) << std::endl;
for (auto size : image["sizes"].get_object()) {
std::cout << "Type of image size = " << std::string_view(size.unescaped_key()) << std::endl;
// NOTE: the uint64_t is required so that each value is actually parsed before the pair is created
image_sizes.insert(make_pair<uint64_t,uint64_t>(size.value()["w"], size.value()["h"]));
}
}
}
}
ASSERT_EQUAL( image_sizes.size(), 15 );
return true;
}));
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
twitter_count() &&
twitter_default_profile() &&
twitter_image_sizes() &&
#if SIMDJSON_EXCEPTIONS
twitter_count_exception() &&
twitter_example() &&
twitter_default_profile_exception() &&
twitter_image_sizes_exception() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace twitter_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, twitter_tests::run);
}
@@ -0,0 +1,176 @@
#include "simdjson.h"
#include "test_singlestage.h"
using namespace simdjson;
namespace wrong_type_error_tests {
using namespace std;
#define TEST_CAST_ERROR(JSON, TYPE, ERROR) \
std::cout << "- Subtest: get_" << (#TYPE) << "() - JSON: " << (JSON) << std::endl; \
if (!test_singlestage_doc((JSON##_padded), [&](auto doc_result) { \
ASSERT_ERROR( doc_result.get_##TYPE(), (ERROR) ); \
return true; \
})) { \
return false; \
} \
{ \
padded_string a_json(std::string(R"({ "a": )") + JSON + " }"); \
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
if (!test_singlestage_doc(a_json, [&](auto doc_result) { \
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \
return true; \
})) { \
return false; \
}; \
}
bool wrong_type_array() {
TEST_START();
TEST_CAST_ERROR("[]", object, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", double, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", string, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_object() {
TEST_START();
TEST_CAST_ERROR("{}", array, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", double, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", string, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_true() {
TEST_START();
TEST_CAST_ERROR("true", array, INCORRECT_TYPE);
TEST_CAST_ERROR("true", object, INCORRECT_TYPE);
TEST_CAST_ERROR("true", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("true", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("true", double, INCORRECT_TYPE);
TEST_CAST_ERROR("true", string, INCORRECT_TYPE);
TEST_CAST_ERROR("true", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_false() {
TEST_START();
TEST_CAST_ERROR("false", array, INCORRECT_TYPE);
TEST_CAST_ERROR("false", object, INCORRECT_TYPE);
TEST_CAST_ERROR("false", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("false", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("false", double, INCORRECT_TYPE);
TEST_CAST_ERROR("false", string, INCORRECT_TYPE);
TEST_CAST_ERROR("false", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_null() {
TEST_START();
TEST_CAST_ERROR("null", array, INCORRECT_TYPE);
TEST_CAST_ERROR("null", object, INCORRECT_TYPE);
TEST_CAST_ERROR("null", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("null", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("null", double, INCORRECT_TYPE);
TEST_CAST_ERROR("null", string, INCORRECT_TYPE);
TEST_CAST_ERROR("null", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_1() {
TEST_START();
TEST_CAST_ERROR("1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_negative_1() {
TEST_START();
TEST_CAST_ERROR("-1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_float() {
TEST_START();
TEST_CAST_ERROR("1.1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_negative_int64_overflow() {
TEST_START();
TEST_CAST_ERROR("-9223372036854775809", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_int64_overflow() {
TEST_START();
TEST_CAST_ERROR("9223372036854775808", array, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", object, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", string, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool wrong_type_uint64_overflow() {
TEST_START();
TEST_CAST_ERROR("18446744073709551616", array, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", object, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", int64, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", uint64, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", string, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
bool run() {
return
wrong_type_1() &&
wrong_type_array() &&
wrong_type_false() &&
wrong_type_float() &&
wrong_type_int64_overflow() &&
wrong_type_negative_1() &&
wrong_type_negative_int64_overflow() &&
wrong_type_null() &&
wrong_type_object() &&
wrong_type_true() &&
wrong_type_uint64_overflow() &&
true;
}
} // namespace wrong_type_error_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, wrong_type_error_tests::run);
}
+87
View File
@@ -0,0 +1,87 @@
#ifndef SINGLESTAGE_TEST_SINGLESTAGE_H
#define SINGLESTAGE_TEST_SINGLESTAGE_H
#include <iostream>
#include <unistd.h>
#include "simdjson.h"
#include "cast_tester.h"
#include "test_macros.h"
template<typename T, typename F>
bool test_singlestage(simdjson::singlestage::parser &parser, const simdjson::padded_string &json, const F& f) {
auto doc = parser.iterate(json);
T val{};
ASSERT_SUCCESS( doc.get(val) );
return f(val);
}
template<typename T, typename F>
bool test_singlestage(const simdjson::padded_string &json, const F& f) {
simdjson::singlestage::parser parser;
return test_singlestage<T, F>(parser, json, f);
}
template<typename F>
bool test_singlestage_doc(simdjson::singlestage::parser &parser, const simdjson::padded_string &json, const F& f) {
return f(parser.iterate(json));
}
template<typename F>
bool test_singlestage_doc(const simdjson::padded_string &json, const F& f) {
simdjson::singlestage::parser parser;
return test_singlestage_doc(parser, json, f);
}
#define SINGLESTAGE_SUBTEST(NAME, JSON, TEST) \
{ \
std::cout << "- Subtest " << NAME << " - JSON: " << (JSON) << " ..." << std::endl; \
if (!test_singlestage_doc(JSON##_padded, [&](auto doc) { \
return (TEST); \
})) { \
return false; \
} \
}
const size_t AMAZON_CELLPHONES_NDJSON_DOC_COUNT = 793;
#define SIMDJSON_SHOW_DEFINE(x) printf("%s=%s\n", #x, SIMDJSON_STRINGIFY(x))
template<typename F>
int test_main(int argc, char *argv[], const F& test_function) {
std::cout << std::unitbuf;
int c;
while ((c = getopt(argc, argv, "a:")) != -1) {
switch (c) {
case 'a': {
const simdjson::implementation *impl = simdjson::get_available_implementations()[optarg];
if (!impl) {
std::fprintf(stderr, "Unsupported architecture value -a %s\n", optarg);
return EXIT_FAILURE;
}
simdjson::get_active_implementation() = impl;
break;
}
default:
std::fprintf(stderr, "Unexpected argument %c\n", c);
return EXIT_FAILURE;
}
}
// this is put here deliberately to check that the documentation is correct (README),
// should this fail to compile, you should update the documentation:
if (simdjson::get_active_implementation()->name() == "unsupported") {
std::printf("unsupported CPU\n");
std::abort();
}
// We want to know what we are testing.
std::cout << "builtin_implementation -- " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "------------------------------------------------------------" << std::endl;
std::cout << "Running tests." << std::endl;
if (test_function()) {
std::cout << "Success!" << std::endl;
return EXIT_SUCCESS;
} else {
std::cerr << "FAILED." << std::endl;
return EXIT_FAILURE;
}
}
#endif // SINGLESTAGE_TEST_SINGLESTAGE_H
+4
View File
@@ -37,6 +37,10 @@ template<>
simdjson_inline bool equals_expected<simdjson::ondemand::raw_json_string, const char *>(simdjson::ondemand::raw_json_string actual, const char * expected) {
return actual == expected;
}
template<>
simdjson_inline bool equals_expected<simdjson::singlestage::raw_json_string, const char *>(simdjson::singlestage::raw_json_string actual, const char * expected) {
return actual == expected;
}
simdjson_inline simdjson::error_code to_error_code(simdjson::error_code error) {
return error;