mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
f64c004cb7
* making it easier to convert to std::string * typo * guarding C++20 * bad semi-colon
1394 lines
47 KiB
C++
1394 lines
47 KiB
C++
#include "simdjson.h"
|
|
#include "test_ondemand.h"
|
|
|
|
using namespace simdjson;
|
|
|
|
namespace object_tests {
|
|
using namespace std;
|
|
using simdjson::ondemand::json_type;
|
|
|
|
bool issue1979() {
|
|
TEST_START();
|
|
auto json = R"({
|
|
"@avito-core/toggles:6.1.18": {
|
|
"add_model_review_from": true
|
|
}
|
|
})"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS(doc.get_object().get(object));
|
|
ASSERT_SUCCESS(object["@avito-core/toggles:6.1.18"].get_object().error());
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
|
|
bool testing_to_string() {
|
|
TEST_START();
|
|
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS(doc.get_object().get(object));
|
|
for (auto field : object) {
|
|
std::string key;
|
|
ASSERT_SUCCESS(field.unescaped_key().get(key));
|
|
ASSERT_EQUAL(key, "bebe");
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
|
|
bool issue1977() {
|
|
TEST_START();
|
|
auto json = R"({"1": 2} foo })"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS(doc.get_object().get(object));
|
|
for (auto values : object) {
|
|
ASSERT_SUCCESS(values);
|
|
}
|
|
ASSERT_FALSE(doc.at_end());
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1745() {
|
|
TEST_START();
|
|
auto json = R"({
|
|
"keys": [
|
|
{
|
|
"kid": "bbd2ac7c4c5eb8adc8eeffbc8f5a2dd6cf7545e4",
|
|
"e": "AQAB",
|
|
"alg": "RS256",
|
|
"use": "sig",
|
|
"n": "y930dtGTeMG52IPsKmMuEpPHLaxuYQlduZd6BqFVjc2-UFZR8fNqtnYzAjbXWJD_Tqxgdlj_MW4vogvX4sHwVpZONvdyeGoIyDQtis6iuGQhQamV85F_JbrEUnEw3QCO87Liz5UXG6BK2HRyPhDfMex1_tO0ROmySLFdCTS17D0wah71Ibpi0gI8LUi6kzVRjYDIC1oE-iK3Y9s88Bi4ZGYJxXAbnNwbwVkGOKCXja9k0jjBGRxZD-4KDuf493lFOOEGSLDA2Qp9rDqrURP12XYgvf_zJx_kSDipnr0gL6Vz2n3H4-XN4tA45zuzRkHoE7-XexPq-tv7kQ8pSjY2uQ",
|
|
"kty": "RSA"
|
|
},
|
|
{
|
|
"kty": "RSA",
|
|
"n": "zMHxWuxztMKXdBhv3rImlUvW_yp6nO03cVXPyA0Vyq0-M7LfOJJIF-OdNoRGdsFPHVKCFoo6qGhR8rBCmMxA4fM-Ubk5qKuUqCN9eP3yZJq8Cw9tUrt_qh7uW-qfMr0upcyeSHhC_zW1lTGs5sowDorKN_jQ1Sfh9hfBxfc8T7dQAAgEqqMcE3u-2J701jyhJz0pvurCfziiB3buY6SGREhBQwNwpnQjt_lE2U4km8FS0woPzt0ccE3zsGL2qM-LWZbOm9aXquSnqNJLt3tGVvShnev-GiJ1XfQ3EWm0f4w0TX9fTOkxstl0vo_vW_FjGQ0D1pXSjqb7n-hAdXwc9w",
|
|
"alg": "RS256",
|
|
"kid": "85828c59284a69b54b27483e487c3bd46cd2a2b3",
|
|
"e": "AQAB",
|
|
"use": "sig"
|
|
}
|
|
]
|
|
}
|
|
|
|
})"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
for (auto items_instance : doc["keys"]) {
|
|
ondemand::object items;
|
|
ASSERT_SUCCESS(items_instance.get_object().get(items));
|
|
std::string_view kty_value;
|
|
ASSERT_SUCCESS(items["kty"].get_string().get(kty_value));
|
|
std::string_view use_value;
|
|
ASSERT_SUCCESS(items["use"].get_string().get(use_value));
|
|
if (kty_value != "RSA" && use_value != "sig") { continue; }
|
|
std::string_view str;
|
|
ASSERT_SUCCESS(items["alg"].get_string().get(str));
|
|
ASSERT_EQUAL(str, "RS256");
|
|
ASSERT_EQUAL(use_value, "sig");
|
|
ASSERT_EQUAL(kty_value, "RSA");
|
|
ASSERT_SUCCESS(items["n"].get_string().get(str));
|
|
ASSERT_EQUAL(str.size(), 342);
|
|
ASSERT_SUCCESS(items["e"].get_string().get(str));
|
|
ASSERT_EQUAL(str, "AQAB");
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1723() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = R"({
|
|
"contents":
|
|
{
|
|
"bids":[
|
|
{"offset":"1", "price":"1.1", "size":"4.1"}
|
|
],
|
|
"asks":[
|
|
{"offset":"1111", "price":"1.100", "size":"114.1"}
|
|
]
|
|
|
|
}
|
|
})"_padded;
|
|
simdjson::ondemand::document doc;
|
|
auto error = parser.iterate(docdata).get(doc);
|
|
if(error) { return false; }
|
|
ondemand::object content;
|
|
error = doc.get_object().find_field("contents").get(content);
|
|
if(error) { return false; }
|
|
ondemand::array bids;
|
|
error = content["bids"].get_array().get(bids);
|
|
if(error) { return false; }
|
|
for (auto aux : bids) {
|
|
uint64_t id;
|
|
error = aux["offset"].get_uint64_in_string().get(id);
|
|
if(error) { return false; }
|
|
double price;
|
|
error = aux["price"].get_double_in_string().get(price);
|
|
if(error) { return false; }
|
|
double size;
|
|
error = aux["size"].get_double_in_string().get(size);
|
|
if(error) { return false; }
|
|
std::cout << id << " " << price << " " << size << std::endl;
|
|
}
|
|
ondemand::array asks;
|
|
error = content["asks"].get_array().get(asks);
|
|
if(error) { return false; }
|
|
for (auto aux : asks) {
|
|
uint64_t id;
|
|
error = aux["offset"].get_uint64_in_string().get(id);
|
|
if(error) { return false; }
|
|
double price;
|
|
error = aux["price"].get_double_in_string().get(price);
|
|
if(error) { return false; }
|
|
double size;
|
|
error = aux["size"].get_double_in_string().get(size);
|
|
if(error) { return false; }
|
|
std::cout << id << " " << price << " " << size << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// In this test, no non-trivial object in an array have a missing key
|
|
bool no_missing_keys() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = R"([{"a":"a"},{}])"_padded;
|
|
simdjson::ondemand::document doc;
|
|
auto error = parser.iterate(docdata).get(doc);
|
|
if(error != simdjson::SUCCESS) { return false; }
|
|
simdjson::ondemand::array a;
|
|
error = doc.get_array().get(a);
|
|
if(error != simdjson::SUCCESS) { return false; }
|
|
size_t counter{0};
|
|
for(auto elem : a) {
|
|
error = elem.find_field_unordered("a").error();
|
|
if(counter == 0) {
|
|
ASSERT_EQUAL( error, simdjson::SUCCESS);
|
|
} else {
|
|
ASSERT_EQUAL( error, simdjson::NO_SUCH_FIELD);
|
|
}
|
|
counter++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool missing_key_continue() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = R"({"a":0, "b":1, "c":2})"_padded;
|
|
simdjson::ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
|
int64_t num;
|
|
ASSERT_SUCCESS(doc["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
ASSERT_SUCCESS(doc["b"].get(num));
|
|
ASSERT_EQUAL(num, 1);
|
|
ASSERT_SUCCESS(doc["c"].get(num));
|
|
ASSERT_EQUAL(num, 2);
|
|
// Start again, but omit a key
|
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
|
ASSERT_SUCCESS(doc["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
ASSERT_SUCCESS(doc["c"].get(num));
|
|
ASSERT_EQUAL(num, 2);
|
|
// Start again, but request a missing key
|
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
|
simdjson::ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
|
|
ASSERT_SUCCESS(obj["c"].get(num));
|
|
ASSERT_EQUAL(num, 2);
|
|
// Start again, but request a missing key first
|
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
// Start again, but request a missing key twice
|
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
|
|
assert_error(obj["z"].get(num), NO_SUCH_FIELD);
|
|
// Because we do a full circle, you can query the same
|
|
// key twice!!!
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
ASSERT_SUCCESS(obj["b"].get(num));
|
|
ASSERT_EQUAL(num, 1);
|
|
ASSERT_SUCCESS(obj["b"].get(num));
|
|
ASSERT_EQUAL(num, 1);
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
assert_error(obj["d"].get(num), NO_SUCH_FIELD);
|
|
ASSERT_SUCCESS(obj["a"].get(num));
|
|
ASSERT_EQUAL(num, 0);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool missing_keys() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = R"([{"a":"a"},{}])"_padded;
|
|
simdjson::ondemand::document doc;
|
|
auto error = parser.iterate(docdata).get(doc);
|
|
if(error != simdjson::SUCCESS) { return false; }
|
|
simdjson::ondemand::array a;
|
|
error = doc.get_array().get(a);
|
|
if(error != simdjson::SUCCESS) { return false; }
|
|
for(auto elem : a) {
|
|
error = elem.find_field_unordered("keynotfound").error();
|
|
if(error != simdjson::NO_SUCH_FIELD) {
|
|
std::cout << error << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool missing_keys_for_empty_top_level_object() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = "{}"_padded;
|
|
simdjson::ondemand::document doc;
|
|
auto error = parser.iterate(docdata).get(doc);
|
|
if(error != simdjson::SUCCESS) { return false; }
|
|
error = doc.find_field_unordered("keynotfound").error();
|
|
if(error != simdjson::NO_SUCH_FIELD) {
|
|
std::cout << error << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if SIMDJSON_EXCEPTIONS
|
|
|
|
bool testing_to_string_exception() {
|
|
TEST_START();
|
|
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc = parser.iterate(json);
|
|
ondemand::object object = doc.get_object();
|
|
for (auto field : object) {
|
|
std::string key;
|
|
ASSERT_SUCCESS(field.unescaped_key().get(key));
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1965() {
|
|
TEST_START();
|
|
std::string str = "{\"query\":\"ah\"}";
|
|
std::unique_ptr<char[]> buffer(new char[str.size() + simdjson::SIMDJSON_PADDING]);
|
|
memcpy(buffer.get(), str.data(), str.size());
|
|
simdjson::padded_string_view view(buffer.get(), str.size(), str.size() + simdjson::SIMDJSON_PADDING);
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::ondemand::document doc = parser.iterate(view);
|
|
simdjson::ondemand::object root = doc.get_object();
|
|
simdjson::ondemand::value query = root.find_field("query");
|
|
simdjson::ondemand::raw_json_string raw = query.get_raw_json_string();
|
|
std::unique_ptr<uint8_t[]> dst_buffer(new uint8_t[3 + simdjson::SIMDJSON_PADDING]);
|
|
uint8_t * dst = dst_buffer.get();
|
|
std::string_view fieldstring = parser.unescape(raw, dst);
|
|
std::cout << fieldstring << std::endl;
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1745_with_exceptions() {
|
|
TEST_START();
|
|
auto json = R"({
|
|
"keys": [
|
|
{
|
|
"kid": "bbd2ac7c4c5eb8adc8eeffbc8f5a2dd6cf7545e4",
|
|
"e": "AQAB",
|
|
"alg": "RS256",
|
|
"use": "sig",
|
|
"n": "y930dtGTeMG52IPsKmMuEpPHLaxuYQlduZd6BqFVjc2-UFZR8fNqtnYzAjbXWJD_Tqxgdlj_MW4vogvX4sHwVpZONvdyeGoIyDQtis6iuGQhQamV85F_JbrEUnEw3QCO87Liz5UXG6BK2HRyPhDfMex1_tO0ROmySLFdCTS17D0wah71Ibpi0gI8LUi6kzVRjYDIC1oE-iK3Y9s88Bi4ZGYJxXAbnNwbwVkGOKCXja9k0jjBGRxZD-4KDuf493lFOOEGSLDA2Qp9rDqrURP12XYgvf_zJx_kSDipnr0gL6Vz2n3H4-XN4tA45zuzRkHoE7-XexPq-tv7kQ8pSjY2uQ",
|
|
"kty": "RSA"
|
|
},
|
|
{
|
|
"kty": "RSA",
|
|
"n": "zMHxWuxztMKXdBhv3rImlUvW_yp6nO03cVXPyA0Vyq0-M7LfOJJIF-OdNoRGdsFPHVKCFoo6qGhR8rBCmMxA4fM-Ubk5qKuUqCN9eP3yZJq8Cw9tUrt_qh7uW-qfMr0upcyeSHhC_zW1lTGs5sowDorKN_jQ1Sfh9hfBxfc8T7dQAAgEqqMcE3u-2J701jyhJz0pvurCfziiB3buY6SGREhBQwNwpnQjt_lE2U4km8FS0woPzt0ccE3zsGL2qM-LWZbOm9aXquSnqNJLt3tGVvShnev-GiJ1XfQ3EWm0f4w0TX9fTOkxstl0vo_vW_FjGQ0D1pXSjqb7n-hAdXwc9w",
|
|
"alg": "RS256",
|
|
"kid": "85828c59284a69b54b27483e487c3bd46cd2a2b3",
|
|
"e": "AQAB",
|
|
"use": "sig"
|
|
}
|
|
]
|
|
}
|
|
|
|
})"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc = parser.iterate(json);
|
|
for (auto items : doc["keys"]) {
|
|
std::string_view kty_value = items["kty"].get_string();
|
|
std::string_view use_value = items["use"].get_string();
|
|
if (kty_value != "RSA" && use_value != "sig") { continue; }
|
|
std::string_view str = items["alg"].get_string();
|
|
if(str != "RS256") { return false; }
|
|
if(use_value != "sig") { return false; }
|
|
if(kty_value != "RSA") { return false; }
|
|
str = items["n"].get_string();
|
|
if(str.size() != 342) { return false; }
|
|
str = items["e"].get_string();
|
|
if(str != "AQAB") { return false; }
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1723_except() {
|
|
TEST_START();
|
|
simdjson::ondemand::parser parser;
|
|
simdjson::padded_string docdata = R"({
|
|
"contents":
|
|
{
|
|
"bids":[
|
|
{"offset":"1", "price":"1.1", "size":"4.1"}
|
|
],
|
|
"asks":[
|
|
{"offset":"1111", "price":"1.100", "size":"114.1"}
|
|
]
|
|
|
|
}
|
|
})"_padded;
|
|
simdjson::ondemand::document doc = parser.iterate(docdata);
|
|
ondemand::object content = doc.get_object().find_field("contents");
|
|
ondemand::array bids = content["bids"].get_array();
|
|
std::cout << "bids:" << std::endl;
|
|
for (auto aux : bids) {
|
|
uint64_t id = aux["offset"].get_uint64_in_string();
|
|
double price = aux["price"].get_double_in_string();
|
|
double size = aux["size"].get_double_in_string();
|
|
std::cout << id << " " << price << " " << size << std::endl;
|
|
}
|
|
ondemand::array asks = content["asks"].get_array();
|
|
std::cout << "asks:" << std::endl;
|
|
for (auto aux : asks) {
|
|
uint64_t id = aux["offset"].get_uint64_in_string();
|
|
double price = aux["price"].get_double_in_string();
|
|
double size = aux["size"].get_double_in_string();
|
|
std::cout << id << " " << price << " " << size << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
// used in issue_1521
|
|
// difficult to use as a lambda because it is recursive.
|
|
void broken_descend(ondemand::object node) {
|
|
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type.get_string().value() == "child") {
|
|
auto n = node.find_field_unordered("name");
|
|
if(n.error() == simdjson::SUCCESS) {
|
|
std::cout << std::string_view(n) << std::endl;
|
|
}
|
|
} else {
|
|
for (ondemand::object child_node : node["nodes"]) { broken_descend(child_node); }
|
|
}
|
|
}
|
|
|
|
bool broken_issue_1521() {
|
|
TEST_START();
|
|
ondemand::parser parser;
|
|
padded_string json = R"({"type":"root","nodes":[{"type":"child","nodes":[]},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
|
|
ondemand::document file_tree = parser.iterate(json);
|
|
try {
|
|
broken_descend(file_tree);
|
|
} catch(simdjson::simdjson_error& e) {
|
|
std::cout << "The document is valid JSON: " << json << std::endl;
|
|
TEST_FAIL(e.error());
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool fixed_broken_issue_1521() {
|
|
TEST_START();
|
|
ondemand::parser parser;
|
|
// We omit the ',"nodes":[]'
|
|
padded_string json = R"({"type":"root","nodes":[{"type":"child"},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
|
|
ondemand::document file_tree = parser.iterate(json);
|
|
try {
|
|
broken_descend(file_tree);
|
|
} catch(simdjson::simdjson_error& e) {
|
|
std::cout << "The document is valid JSON: " << json << std::endl;
|
|
TEST_FAIL(e.error());
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
// used in issue_1521
|
|
// difficult to use as a lambda because it is recursive.
|
|
void descend(ondemand::object node) {
|
|
auto n = node.find_field_unordered("name");
|
|
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type.get_string().value() == "child") {
|
|
if(n.error() == simdjson::SUCCESS) {
|
|
std::cout << std::string_view(n) << std::endl;
|
|
}
|
|
} else {
|
|
for (ondemand::object child_node : node["nodes"]) { descend(child_node); }
|
|
}
|
|
}
|
|
|
|
bool issue_1521() {
|
|
TEST_START();
|
|
ondemand::parser parser;
|
|
padded_string json = R"({"type":"root","nodes":[{"type":"child","nodes":[]},{"type":"child","name":"child-name","nodes":[]}]})"_padded;
|
|
ondemand::document file_tree = parser.iterate(json);
|
|
try {
|
|
descend(file_tree);
|
|
} catch(simdjson::simdjson_error& e) {
|
|
std::cout << "The document is valid JSON: " << json << std::endl;
|
|
TEST_FAIL(e.error());
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
#endif
|
|
|
|
bool iterate_object() {
|
|
TEST_START();
|
|
auto json = R"({ "a": 1, "b": 2, "c": 3 })"_padded;
|
|
const char* expected_key[] = { "a", "b", "c" };
|
|
const uint64_t expected_value[] = { 1, 2, 3 };
|
|
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
ASSERT_SUCCESS( field.key().error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::object-document-rewind", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
ASSERT_SUCCESS( field.error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
doc_result.rewind();
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
i = 0;
|
|
for (auto field : object) {
|
|
ASSERT_SUCCESS( field.error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::object-rewind", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
ASSERT_SUCCESS( field.error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
object.reset();
|
|
i = 0;
|
|
for (auto field : object) {
|
|
ASSERT_SUCCESS( field.error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
return true;
|
|
}));
|
|
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
|
|
simdjson_result<ondemand::object> object_result = doc_result.get_object();
|
|
size_t i = 0;
|
|
for (auto field : object_result) {
|
|
ASSERT_SUCCESS( field.error() );
|
|
ASSERT_EQUAL( field.key().value_unsafe(), expected_key[i]);
|
|
ASSERT_EQUAL( field.value().get_uint64().value_unsafe(), expected_value[i] );
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
|
return true;
|
|
}));
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_object_partial_children() {
|
|
TEST_START();
|
|
auto json = R"(
|
|
{
|
|
"scalar_ignore": 0,
|
|
"empty_array_ignore": [],
|
|
"empty_object_ignore": {},
|
|
"object_break": { "x": 3, "y": 33 },
|
|
"object_break_unused": { "x": 4, "y": 44 },
|
|
"object_index": { "x": 5, "y": 55 },
|
|
"object_index_unused": { "x": 6, "y": 66 },
|
|
"array_break": [ 7, 77, 777 ],
|
|
"array_break_unused": [ 8, 88, 888 ],
|
|
"quadruple_nested_break": { "a": [ { "b": [ 9, 99 ], "c": 999 }, 9999 ], "d": 99999 },
|
|
"actual_value": 10
|
|
}
|
|
)"_padded;
|
|
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
ondemand::raw_json_string key;
|
|
ASSERT_SUCCESS( field.key().get(key) );
|
|
|
|
switch (i) {
|
|
case 0: {
|
|
ASSERT_EQUAL(key, "scalar_ignore");
|
|
std::cout << " - After ignoring empty scalar ..." << std::endl;
|
|
break;
|
|
}
|
|
case 1: {
|
|
ASSERT_EQUAL(key, "empty_array_ignore");
|
|
std::cout << " - After ignoring empty array ..." << std::endl;
|
|
break;
|
|
}
|
|
case 2: {
|
|
ASSERT_EQUAL(key, "empty_object_ignore");
|
|
std::cout << " - After ignoring empty object ..." << std::endl;
|
|
break;
|
|
}
|
|
// Break after using first value in child object
|
|
case 3: {
|
|
ASSERT_EQUAL(key, "object_break");
|
|
|
|
for (auto child_field : field.value().get_object()) {
|
|
ASSERT_SUCCESS( child_field.error() );
|
|
ASSERT_EQUAL(child_field.key().value_unsafe(), "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: {
|
|
ASSERT_EQUAL(key, "object_break_unused");
|
|
|
|
for (auto child_field : field.value().get_object()) {
|
|
ASSERT_SUCCESS(child_field.error());
|
|
ASSERT_EQUAL(child_field.key().value_unsafe(), "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: {
|
|
ASSERT_EQUAL(key, "object_index");
|
|
|
|
uint64_t x;
|
|
ASSERT_SUCCESS( field.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_EQUAL(key, "object_index_unused");
|
|
|
|
ASSERT_SUCCESS( field.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: {
|
|
ASSERT_EQUAL(key, "array_break");
|
|
for (auto child_value : field.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: {
|
|
ASSERT_EQUAL(key, "array_break_unused");
|
|
for (auto child_value : field.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: {
|
|
ASSERT_EQUAL(key, "quadruple_nested_break");
|
|
for (auto child1 : field.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: {
|
|
ASSERT_EQUAL(key, "actual_value");
|
|
uint64_t actual_value;
|
|
ASSERT_SUCCESS( field.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_object() {
|
|
TEST_START();
|
|
auto json = R"({})"_padded;
|
|
|
|
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
for (simdjson_unused auto field : object) {
|
|
TEST_FAIL("Unexpected field");
|
|
}
|
|
return true;
|
|
}));
|
|
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
|
|
simdjson_result<ondemand::object> object_result = doc_result.get_object();
|
|
for (simdjson_unused auto field : object_result) {
|
|
TEST_FAIL("Unexpected field");
|
|
}
|
|
return true;
|
|
}));
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool value_search_unescaped_key() {
|
|
TEST_START();
|
|
auto json = R"({"k\u0065y": 1})"_padded;
|
|
SUBTEST("ondemand::unescapedkey", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
bool got_key = false;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
for (auto field : object) {
|
|
std::string_view keyv;
|
|
ASSERT_SUCCESS( field.unescaped_key().get(keyv) );
|
|
if(keyv == "key") {
|
|
int64_t value;
|
|
ASSERT_SUCCESS( field.value().get(value) );
|
|
ASSERT_EQUAL( value, 1);
|
|
got_key = true;
|
|
}
|
|
}
|
|
return got_key;
|
|
}));
|
|
SUBTEST("ondemand::unescapedkey(std)", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
bool got_key = false;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
for (auto field : object) {
|
|
std::string keyv;
|
|
ASSERT_SUCCESS( field.unescaped_key(keyv) );
|
|
if(keyv == "key") {
|
|
int64_t value;
|
|
ASSERT_SUCCESS( field.value().get(value) );
|
|
ASSERT_EQUAL( value, 1);
|
|
got_key = true;
|
|
}
|
|
}
|
|
return got_key;
|
|
}));
|
|
SUBTEST("ondemand::rawkey", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
bool got_key = false;
|
|
for (auto field : object) {
|
|
ondemand::raw_json_string keyv;
|
|
ASSERT_SUCCESS( field.key().get(keyv) );
|
|
if(keyv == R"(k\u0065y)") {
|
|
int64_t value;
|
|
ASSERT_SUCCESS( field.value().get(value) );
|
|
ASSERT_EQUAL( value, 1);
|
|
got_key = true;
|
|
}
|
|
}
|
|
return got_key;
|
|
}));
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue_1480() {
|
|
TEST_START();
|
|
auto json = R"({ "name" : "something", "version": "0.13.2", "version_major": 0})"_padded;
|
|
|
|
SUBTEST("ondemand::issue_1480::object", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
ASSERT_EQUAL( object.find_field_unordered("version_major").get_uint64().value_unsafe(), 0 );
|
|
ASSERT_EQUAL( object.find_field_unordered("version").get_string().value_unsafe(), "0.13.2");
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::issue_1480::big-key", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
std::vector<char> large_buf(512,' ');
|
|
std::string_view lb{large_buf.data(), large_buf.size()};
|
|
ASSERT_ERROR( object.find_field(lb), NO_SUCH_FIELD );
|
|
return true;
|
|
}));
|
|
|
|
#if SIMDJSON_EXCEPTIONS
|
|
SUBTEST("ondemand::issue_1480::object-iteration", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t counter_version{0};
|
|
size_t counter_version_major{0};
|
|
for(auto field : object) {
|
|
std::string_view keyv = field.unescaped_key();
|
|
if(keyv == "version") { counter_version++; }
|
|
if(keyv == "version_major") { counter_version_major++; }
|
|
}
|
|
ASSERT_EQUAL( counter_version, 1 );
|
|
ASSERT_EQUAL( counter_version_major, 1 );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::issue_1480::object-iteration-string-view", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t counter_version{0};
|
|
size_t counter_version_major{0};
|
|
const char * bufv = "version";
|
|
const char * bufvm = "version_major";
|
|
|
|
std::string_view v{bufv};
|
|
std::string_view vm{bufvm};
|
|
|
|
for(auto field : object) {
|
|
std::string_view keyv = field.unescaped_key();
|
|
if(keyv == v) { counter_version++; }
|
|
if(keyv == vm) { counter_version_major++; }
|
|
}
|
|
ASSERT_EQUAL( counter_version, 1 );
|
|
ASSERT_EQUAL( counter_version_major, 1 );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::issue_1480::original", test_ondemand_doc(json, [&](auto doc_result) {
|
|
size_t counter{0};
|
|
for (auto field : doc_result.get_object()) {
|
|
auto key = field.key().value();
|
|
if (key == "version") { counter++; }
|
|
}
|
|
ASSERT_EQUAL( counter, 1 );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::issue_1480::object-operator", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
ASSERT_EQUAL( std::string_view(object["version"]), "0.13.2" );
|
|
ASSERT_EQUAL( uint64_t(object["version_major"]), 0 );
|
|
return true;
|
|
}));
|
|
#endif
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
#if SIMDJSON_EXCEPTIONS
|
|
|
|
bool iterate_object_exception() {
|
|
TEST_START();
|
|
auto json = R"({ "a": 1, "b": 2, "c": 3 })"_padded;
|
|
const char* expected_key[] = { "a", "b", "c" };
|
|
const uint64_t expected_value[] = { 1, 2, 3 };
|
|
ASSERT_TRUE(test_ondemand_doc(json, [&](auto doc_result) {
|
|
size_t i = 0;
|
|
for (ondemand::field field : doc_result.get_object()) {
|
|
ASSERT_EQUAL( field.key(), expected_key[i] );
|
|
ASSERT_EQUAL( uint64_t(field.value()), 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_ondemand_doc(json, [&](auto doc_result) {
|
|
for (simdjson_unused ondemand::field field : doc_result.get_object()) {
|
|
TEST_FAIL("Unexpected field");
|
|
}
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::object-document-rewind", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
doc_result.rewind();
|
|
i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::object-rewind", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
object.reset();
|
|
i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
return true;
|
|
}));
|
|
SUBTEST("ondemand::object-rewind", test_ondemand_doc(json, [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
size_t i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
object.reset();
|
|
i = 0;
|
|
for (auto field : object) {
|
|
(void)field;
|
|
i++;
|
|
}
|
|
ASSERT_EQUAL( i, 0 );
|
|
return true;
|
|
}));
|
|
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
#endif // SIMDJSON_EXCEPTIONS
|
|
|
|
bool empty(simdjson::ondemand::object obj) {
|
|
bool is_empty;
|
|
ASSERT_SUCCESS(obj.is_empty().get(is_empty));
|
|
ASSERT_TRUE(is_empty);
|
|
return true;
|
|
}
|
|
bool value_to_object(simdjson::ondemand::value val) {
|
|
ondemand::json_type t;
|
|
ASSERT_SUCCESS(val.type().get(t));
|
|
ASSERT_EQUAL(t, ondemand::json_type::object);
|
|
simdjson::ondemand::object obj;
|
|
ASSERT_SUCCESS(val.get_object().get(obj));
|
|
if(empty(obj) != true) { return false; }
|
|
return true;
|
|
}
|
|
bool empty_rewind_convoluted() {
|
|
TEST_START();
|
|
const auto json = R"( {} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::value val;
|
|
ASSERT_SUCCESS(doc.get_value().get(val));
|
|
if(!value_to_object(val)) { return false; }
|
|
TEST_SUCCEED();
|
|
}
|
|
#if SIMDJSON_EXCEPTIONS
|
|
bool value_to_object_except(simdjson::ondemand::value val) {
|
|
ondemand::json_type t = val.type();
|
|
ASSERT_EQUAL(t, ondemand::json_type::object);
|
|
if(empty(simdjson::ondemand::object(val)) != true) { return false; }
|
|
return true;
|
|
}
|
|
bool empty_rewind_convoluted_with_exceptions() {
|
|
TEST_START();
|
|
const auto json = R"( {} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::value val;
|
|
ASSERT_SUCCESS(doc.get_value().get(val));
|
|
if(value_to_object_except(val) != true) { return false; }
|
|
TEST_SUCCEED();
|
|
}
|
|
#endif
|
|
|
|
bool iterate_empty_object_count() {
|
|
TEST_START();
|
|
auto json = R"( {} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
size_t count;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
ASSERT_SUCCESS(obj.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 0);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1876a() {
|
|
TEST_START();
|
|
auto json = R"( {} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
size_t count;
|
|
ASSERT_SUCCESS(obj.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 0);
|
|
for (auto field : obj) {
|
|
std::string_view key;
|
|
ASSERT_SUCCESS(field.unescaped_key().get(key));
|
|
double value;
|
|
ASSERT_SUCCESS(field.value().get_double().get(value));
|
|
std::cout << key << " " << value << std::endl;
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1876() {
|
|
TEST_START();
|
|
auto json = R"( {} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
size_t count;
|
|
ASSERT_SUCCESS(doc.count_fields().get(count));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
|
|
ASSERT_EQUAL(count, 0);
|
|
for (auto field : obj) {
|
|
std::string_view key;
|
|
ASSERT_SUCCESS(field.unescaped_key().get(key));
|
|
double value;
|
|
ASSERT_SUCCESS(field.value().get_double().get(value));
|
|
std::cout << key << " " << value << std::endl;
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1742() {
|
|
TEST_START();
|
|
auto json = R"( {
|
|
"code": 0,
|
|
"method": "subscribe",
|
|
"result": {
|
|
"instrument_name": "DAI_USDC",
|
|
"subscription": "trade.DAI_USDC",
|
|
"channel": "trade",
|
|
"data": [
|
|
{
|
|
"dataTime": 1635315736764,
|
|
"d": 1928673075051012900,
|
|
"s": "SELL",
|
|
"p": 1.0004,
|
|
"q": 0.01,
|
|
"t": 1635315736763,
|
|
"i": "DAI_USDC"
|
|
}
|
|
]
|
|
}
|
|
} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
simdjson::ondemand::array data;
|
|
ASSERT_SUCCESS(doc["result"]["data"].get_array().get(data));
|
|
for (auto d : data) {
|
|
simdjson::ondemand::object obj;
|
|
ASSERT_SUCCESS(d.get_object().get(obj));
|
|
size_t count;
|
|
ASSERT_SUCCESS(obj.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 7);
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
|
|
bool issue1742_value() {
|
|
TEST_START();
|
|
auto json = R"( {
|
|
"code": 0,
|
|
"method": "subscribe",
|
|
"result": {
|
|
"instrument_name": "DAI_USDC",
|
|
"subscription": "trade.DAI_USDC",
|
|
"channel": "trade",
|
|
"data": [
|
|
{
|
|
"dataTime": 1635315736764,
|
|
"d": 1928673075051012900,
|
|
"s": "SELL",
|
|
"p": 1.0004,
|
|
"q": 0.01,
|
|
"t": 1635315736763,
|
|
"i": "DAI_USDC"
|
|
}
|
|
]
|
|
}
|
|
} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
simdjson::ondemand::array data;
|
|
ASSERT_SUCCESS(doc["result"]["data"].get_array().get(data));
|
|
for (auto d : data) {
|
|
size_t count;
|
|
ASSERT_SUCCESS(d.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 7);
|
|
}
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_basic_object_count() {
|
|
TEST_START();
|
|
auto json = R"( {"a":-55, "b":3.23, "c":100000000000000000000, "d":true, "e":null} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
size_t count;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
ASSERT_SUCCESS(obj.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 5);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_complex_object_count() {
|
|
TEST_START();
|
|
auto json = R"( {
|
|
"first": {"a":1, "b":[1,2,3], "c":{}},
|
|
"second":[true, false, null, {"1":[4,5,6], "2":3.14}]
|
|
} )"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj1;
|
|
size_t count{0};
|
|
ASSERT_SUCCESS(doc.get_object().get(obj1));
|
|
ASSERT_SUCCESS(obj1.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 2);
|
|
count = 0;
|
|
ondemand::object obj2;
|
|
ASSERT_SUCCESS(doc.find_field("first").get_object().get(obj2));
|
|
ASSERT_SUCCESS(obj2.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 3);
|
|
count = 0;
|
|
ondemand::object obj3;
|
|
ASSERT_SUCCESS(obj2.find_field("c").get_object().get(obj3));
|
|
ASSERT_SUCCESS(obj3.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 0);
|
|
count = 0;
|
|
ondemand::object obj4;
|
|
ASSERT_SUCCESS(doc.at_pointer("/second/3").get_object().get(obj4));
|
|
ASSERT_SUCCESS(obj4.count_fields().get(count));
|
|
ASSERT_EQUAL(count, 2);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_bad_object_count() {
|
|
TEST_START();
|
|
padded_string bad_jsons[3] = {R"( {"a":5 "b":3} )"_padded, R"( {"a":5, 3} )"_padded, R"( {"a":5, "b": } )"_padded};
|
|
std::string names[3] = {"missing_comma", "missing_key", "missing_value"};
|
|
size_t count{0};
|
|
|
|
for (auto name : names) {
|
|
SUBTEST("ondemand::" + name, test_ondemand_doc(bad_jsons[count++], [&](auto doc_result) {
|
|
ondemand::object object;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.get(object) );
|
|
ASSERT_ERROR(object.count_fields(), TAPE_ERROR);
|
|
return true;
|
|
}));
|
|
}
|
|
ASSERT_EQUAL(count, 3);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_doc_object_count() {
|
|
TEST_START();
|
|
auto empty = R"( {} )"_padded;
|
|
SUBTEST("ondemand::empty_doc_object", test_ondemand_doc(empty, [&](auto doc_result) {
|
|
size_t count;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.count_fields().get(count) );
|
|
ASSERT_EQUAL( count, 0 );
|
|
return true;
|
|
}));
|
|
auto basic = R"( {"a":-1.234, "b":false, "c":null, "d":[1000.1,-2000.2,3000.3], "e":{"a":true, "b":false}} )"_padded;
|
|
SUBTEST("ondemand::basic_doc_object", test_ondemand_doc(basic, [&](auto doc_result) {
|
|
size_t count;
|
|
ASSERT_RESULT( doc_result.type(), json_type::object );
|
|
ASSERT_SUCCESS( doc_result.count_fields().get(count) );
|
|
ASSERT_EQUAL( count, 5 );
|
|
return true;
|
|
}));
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
|
|
bool issue1974a() {
|
|
TEST_START();
|
|
padded_string bad_json = R"({"key":111)"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(bad_json).get(doc));
|
|
ondemand::object object;
|
|
ASSERT_ERROR(doc.get_object().get(object), INCOMPLETE_ARRAY_OR_OBJECT);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool issue1974b() {
|
|
TEST_START();
|
|
padded_string bad_json = R"({"key":111)"_padded;
|
|
ondemand::parser parser;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(bad_json).get(doc));
|
|
ondemand::value val;
|
|
ASSERT_ERROR(doc.get_value().get(val), INCOMPLETE_ARRAY_OR_OBJECT);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool iterate_bad_doc_object_count() {
|
|
TEST_START();
|
|
padded_string bad_jsons[4] = {R"( {"a":5 "b":3} )"_padded, R"( {"a":5, 3} )"_padded, R"( {"a":5, "b": } )"_padded, R"( {"a":5, "b":3 )"_padded};
|
|
std::string names[4] = {"missing_comma", "missing_key", "missing_value", "missing_bracket"};
|
|
simdjson::error_code errors[4] = {TAPE_ERROR, TAPE_ERROR, TAPE_ERROR, INCOMPLETE_ARRAY_OR_OBJECT};
|
|
size_t count{0};
|
|
|
|
for (auto name : names) {
|
|
SUBTEST("ondemand::" + name, test_ondemand_doc(bad_jsons[count], [&](auto doc_result) {
|
|
ASSERT_RESULT(doc_result.type(), json_type::object );
|
|
ASSERT_ERROR(doc_result.count_fields(), errors[count]);
|
|
return true;
|
|
}));
|
|
count++;
|
|
}
|
|
ASSERT_EQUAL(count, 4);
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
|
|
bool test_strager() {
|
|
// Find exactly one of a/myint or b/myint, if both or none are present,
|
|
// return false.
|
|
auto process = [](ondemand::object root) -> std::pair<bool,uint64_t> {
|
|
///////////////////////////////////////////////////////////
|
|
// The core idea with On-Demand is that you should visit everything only
|
|
// once.
|
|
//
|
|
// Thus, with On-Demand, you must consume a field once you have accessed it.
|
|
// Thus if you do root["a"], you must full consume the corresponding
|
|
// value (if present) before you move to another field (e.g., root["a"]).
|
|
//
|
|
// If, somehow, you only want the value corresponding to "a" if some
|
|
// other key is not present, then you must still parse/consume the value
|
|
// corresponding to "a", and later throw the result away.
|
|
///////////////////////////////////////////////////////////
|
|
uint64_t myint;
|
|
// Notice in the line below that I use chaining... I do not
|
|
// first convert root["a"] to an object and then seek
|
|
// "myint" into. Chaining works in this instance because
|
|
// I only need to access the object 'root["a"]' for a single
|
|
// key, if I needed to do more work on root["a"], then I should
|
|
// create an object.
|
|
if(root["a"]["myint"].get(myint) == SUCCESS) {
|
|
// We want myint to be discarded if "b" is also present.
|
|
if(! root["b"].error() ) {
|
|
// Oops, the other key is also present!!! We must throw
|
|
// away 'myint' and return false.
|
|
// Notice how I do not parse the value corresponding to
|
|
// '"b"', as it is unneeded.
|
|
return std::make_pair(false, 0);
|
|
}
|
|
// All is good,
|
|
return std::make_pair(true,myint);
|
|
}
|
|
// Now we know that "a" is not present, so seek "b".
|
|
if(root["b"]["myint"].get(myint) == SUCCESS) {
|
|
return std::make_pair(true, myint);
|
|
}
|
|
// None are present.
|
|
return std::make_pair(false, 0);
|
|
};
|
|
|
|
TEST_START();
|
|
|
|
{
|
|
ondemand::parser parser;
|
|
padded_string json = R"({ "a": { "myint": 42 } })"_padded;
|
|
std::cout << " - Subtest 'a' - JSON: " << (json) << " ..." << std::endl;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
std::pair<bool,uint64_t> ret = process(obj);
|
|
ASSERT_TRUE(ret.first);
|
|
ASSERT_EQUAL(ret.second, 42);
|
|
}
|
|
|
|
{
|
|
ondemand::parser parser;
|
|
padded_string json = R"({ "b": { "myint": 42 } })"_padded;
|
|
std::cout << " - Subtest 'b' - JSON: " << (json) << " ..." << std::endl;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
std::pair<bool,uint64_t> ret = process(obj);
|
|
ASSERT_TRUE(ret.first);
|
|
ASSERT_EQUAL(ret.second, 42);
|
|
}
|
|
|
|
|
|
{
|
|
ondemand::parser parser;
|
|
padded_string json = R"({ "b": { "myint": 42 }, "a": { "myint": 42 } })"_padded;
|
|
std::cout << " - Subtest 'b&a' - JSON: " << (json) << " ..." << std::endl;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
std::pair<bool,uint64_t> ret = process(obj);
|
|
ASSERT_FALSE(ret.first);
|
|
ASSERT_EQUAL(ret.second, 0);
|
|
}
|
|
|
|
{
|
|
ondemand::parser parser;
|
|
padded_string json = R"({ "z": { "myint": 42 }, "y": { "myint": 42 } })"_padded;
|
|
std::cout << " - Subtest 'none' - JSON: " << (json) << " ..." << std::endl;
|
|
ondemand::document doc;
|
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
|
ondemand::object obj;
|
|
ASSERT_SUCCESS(doc.get_object().get(obj));
|
|
std::pair<bool,uint64_t> ret = process(obj);
|
|
ASSERT_FALSE(ret.first);
|
|
ASSERT_EQUAL(ret.second, 0);
|
|
}
|
|
|
|
TEST_SUCCEED();
|
|
}
|
|
|
|
bool run() {
|
|
return testing_to_string() &&
|
|
issue1979() &&
|
|
issue1977() &&
|
|
#if SIMDJSON_EXCEPTIONS
|
|
testing_to_string_exception() &&
|
|
issue1965() &&
|
|
#endif
|
|
issue1974a() &&
|
|
issue1974b() &&
|
|
issue1876a() &&
|
|
issue1876() &&
|
|
test_strager() &&
|
|
issue1745() &&
|
|
issue1742() &&
|
|
issue1742_value() &&
|
|
issue1723() &&
|
|
value_search_unescaped_key() &&
|
|
missing_key_continue() &&
|
|
no_missing_keys() &&
|
|
missing_keys() &&
|
|
missing_keys_for_empty_top_level_object() &&
|
|
#if SIMDJSON_EXCEPTIONS
|
|
issue1745_with_exceptions() &&
|
|
issue1723_except() &&
|
|
fixed_broken_issue_1521() &&
|
|
issue_1521() &&
|
|
broken_issue_1521() &&
|
|
#endif
|
|
iterate_object() &&
|
|
iterate_empty_object() &&
|
|
iterate_object_partial_children() &&
|
|
issue_1480() &&
|
|
empty_rewind_convoluted() &&
|
|
#if SIMDJSON_EXCEPTIONS
|
|
iterate_object_exception() &&
|
|
empty_rewind_convoluted_with_exceptions() &&
|
|
#endif // SIMDJSON_EXCEPTIONS
|
|
iterate_empty_object_count() &&
|
|
iterate_basic_object_count() &&
|
|
iterate_complex_object_count() &&
|
|
iterate_bad_object_count() &&
|
|
iterate_bad_doc_object_count() &&
|
|
true;
|
|
}
|
|
|
|
} // namespace object_tests
|
|
|
|
int main(int argc, char *argv[]) {
|
|
return test_main(argc, argv, object_tests::run);
|
|
}
|