Compare commits

...

8 Commits

Author SHA1 Message Date
John Keiser 7edd32dc6b Remove document_stream from singlestage 2023-07-17 16:31:37 -07:00
John Keiser 91b74c593c Update powerpolicy to include singlestage (whatever that is?) 2023-07-17 16:23:03 -07:00
John Keiser fa629bd783 Make trim a common file 2023-07-17 16:23:03 -07:00
John Keiser 7784aa86c2 Add (non-working) fuzzer for singlestage (copy of ondemand) 2023-07-17 16:23:03 -07:00
John Keiser a47b64e0ff Add quickstart for singlestage (copy of ondemand) 2023-07-17 16:23:03 -07:00
John Keiser d92bc82b37 Add benchmarks for singlestage (copy of ondemand) 2023-07-17 16:23:03 -07:00
John Keiser 5b02e117bb Add tests for singlestage (copy of ondemand) 2023-07-17 16:23:03 -07:00
John Keiser ef6fc77861 Add initial singlestage implementation 2023-07-17 16:22:59 -07:00
103 changed files with 20513 additions and 32 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
# fuzzers that change behaviour with SIMDJSON_FORCE_IMPLEMENTATION
defaultimplfuzzers: atpointer dump dump_raw_tape element minify parser print_json
# fuzzers that loop over the implementations themselves, or don't need to switch.
implfuzzers: implementations minifyimpl ndjson ondemand padded utf8
implfuzzers: implementations minifyimpl ndjson ondemand padded utf8 # TODO add singlestage
implementations: haswell westmere fallback
UBSAN_OPTIONS: halt_on_error=1
MAXLEN: -max_len=4000
@@ -0,0 +1,72 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "amazon_cellphones.h"
namespace amazon_cellphones {
using namespace simdjson;
template<bool threaded>
struct simdjson_singlestage {
using StringType = std::string;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
#ifdef SIMDJSON_THREADS_ENABLED
parser.threaded = threaded;
#endif
singlestage::document_stream stream = parser.iterate_many(json);
singlestage::document_stream::iterator i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto doc = *i;
size_t index{0};
StringType copy;
double rating;
uint64_t reviews;
for ( auto value : doc ) {
switch (index)
{
case 1:
copy = StringType(std::string_view(value));
break;
case 5:
rating = double(value);
break;
case 7:
reviews = uint64_t(value);
break;
default:
break;
}
index++;
}
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, amazon_cellphones::brand{
rating * reviews,
reviews
});
} else { // Otherwise, update key data
x->second.cumulative_rating += rating * reviews;
x->second.reviews_count += reviews;
}
}
return true;
}
};
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_singlestage<UNTHREADED>)->UseManualTime();
#ifdef SIMDJSON_THREADS_ENABLED
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_singlestage<THREADED>)->UseManualTime();
#endif
} // namespace amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
+13
View File
@@ -26,6 +26,7 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
SIMDJSON_POP_DISABLE_WARNINGS
#include "json2msgpack/simdjson_ondemand.h"
#include "json2msgpack/simdjson_singlestage.h"
#include "json2msgpack/simdjson_dom.h"
#include "json2msgpack/yyjson.h"
#include "json2msgpack/rapidjson.h"
@@ -35,6 +36,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
#include "json2msgpack/nlohmann_json.h"
#include "partial_tweets/simdjson_ondemand.h"
#include "partial_tweets/simdjson_singlestage.h"
#include "partial_tweets/simdjson_dom.h"
#include "partial_tweets/yyjson.h"
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
@@ -52,6 +54,8 @@ SIMDJSON_POP_DISABLE_WARNINGS
#include "distinct_user_id/simdjson_ondemand.h"
#include "distinct_user_id/simdjson_ondemand_json_pointer.h"
#include "distinct_user_id/simdjson_singlestage.h"
#include "distinct_user_id/simdjson_singlestage_json_pointer.h"
#include "distinct_user_id/simdjson_dom.h"
#include "distinct_user_id/simdjson_dom_json_pointer.h"
#include "distinct_user_id/yyjson.h"
@@ -68,6 +72,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_COMPETITION_SAX
#include "find_tweet/simdjson_ondemand.h"
#include "find_tweet/simdjson_singlestage.h"
#include "find_tweet/simdjson_dom.h"
#include "find_tweet/yyjson.h"
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
@@ -83,6 +88,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_COMPETITION_SAX
#include "top_tweet/simdjson_ondemand.h"
#include "top_tweet/simdjson_singlestage.h"
#include "top_tweet/simdjson_dom.h"
#include "top_tweet/yyjson.h"
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
@@ -99,6 +105,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
#include "kostya/simdjson_ondemand.h"
#include "kostya/simdjson_singlestage.h"
#include "kostya/simdjson_dom.h"
#include "kostya/yyjson.h"
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
@@ -117,6 +124,10 @@ SIMDJSON_POP_DISABLE_WARNINGS
#if SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
#include "large_random/simdjson_ondemand_unordered.h"
#endif // SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
#include "large_random/simdjson_singlestage.h"
#if SIMDJSON_COMPETITION_SINGLESTAGE_UNORDERED
#include "large_random/simdjson_singlestage_unordered.h"
#endif // SIMDJSON_COMPETITION_SINGLESTAGE_UNORDERED
#include "large_random/simdjson_dom.h"
#include "large_random/yyjson.h"
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
@@ -133,8 +144,10 @@ SIMDJSON_POP_DISABLE_WARNINGS
#include "amazon_cellphones/simdjson_dom.h"
#include "amazon_cellphones/simdjson_ondemand.h"
#include "amazon_cellphones/simdjson_singlestage.h"
#include "large_amazon_cellphones/simdjson_dom.h"
#include "large_amazon_cellphones/simdjson_ondemand.h"
#include "large_amazon_cellphones/simdjson_singlestage.h"
BENCHMARK_MAIN();
@@ -0,0 +1,37 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_singlestage {
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (singlestage::object tweet : doc.find_field("statuses")) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet.find_field("user").find_field("id"));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet = tweet.find_field("retweeted_status");
if (!retweet.error()) {
result.push_back(retweet.find_field("user").find_field("id"));
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_singlestage)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,37 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_singlestage_json_pointer {
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (singlestage::object tweet : doc.find_field("statuses")) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet.at_pointer("/user/id"));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet_id = tweet.at_pointer("/retweeted_status/user/id");
if (retweet_id.error() != NO_SUCH_FIELD) {
result.push_back(retweet_id);
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_singlestage_json_pointer)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,33 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "find_tweet.h"
namespace find_tweet {
using namespace simdjson;
struct simdjson_singlestage {
using StringType=std::string_view;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (auto tweet : doc.find_field("statuses")) {
if (uint64_t(tweet.find_field("id")) == find_id) {
result = tweet.find_field("text");
return true;
}
}
return false;
}
};
BENCHMARK_TEMPLATE(find_tweet, simdjson_singlestage)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_EXCEPTIONS
+14 -14
View File
@@ -8,7 +8,7 @@ namespace json2msgpack {
using namespace simdjson;
/**
* @brief The simdjson2msgpack struct is used to quickly convert
* @brief The simdjsonondemand2msgpack struct is used to quickly convert
* JSON strings to msgpack views. You must provide a pointer to
* a large memory region where the msgpack gets written. The
* buffer should be large enough to store the msgpack output (which
@@ -17,7 +17,7 @@ using namespace simdjson;
*
* Recommended usage:
*
* simdjson2msgpack parser{};
* simdjsonondemand2msgpack parser{};
* simdjson::padded_string json = "[1,2]"_padded; // some JSON
* uint8_t * buffer = new uint8_t[3*json.size() + simdjson::SIMDJSON_PADDING]; // large buffer
*
@@ -26,10 +26,10 @@ using namespace simdjson;
* The result (msgpack) is a string view to a msgpack serialization of the input JSON,
* it points inside the buffer you provided.
*
* You may reuse the simdjson2msgpack instance though you should use
* You may reuse the simdjsonondemand2msgpack instance though you should use
* one per thread.
*/
struct simdjson2msgpack {
struct simdjsonondemand2msgpack {
/**
* @brief Converts the provided JSON into msgpack.
*
@@ -58,7 +58,7 @@ private:
};
std::string_view
simdjson2msgpack::to_msgpack(const simdjson::padded_string &json,
simdjsonondemand2msgpack::to_msgpack(const simdjson::padded_string &json,
uint8_t *buf) {
buff = buf;
ondemand::document doc = parser.iterate(json);
@@ -106,33 +106,33 @@ simdjson2msgpack::to_msgpack(const simdjson::padded_string &json,
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
}
void simdjson2msgpack::write_double(const double d) noexcept {
void simdjsonondemand2msgpack::write_double(const double d) noexcept {
*buff++ = 0xcb;
::memcpy(buff, &d, sizeof(d));
buff += sizeof(d);
}
void simdjson2msgpack::write_byte(const uint8_t b) noexcept {
void simdjsonondemand2msgpack::write_byte(const uint8_t b) noexcept {
*buff = b;
buff++;
}
void simdjson2msgpack::write_uint32(const uint32_t w) noexcept {
void simdjsonondemand2msgpack::write_uint32(const uint32_t w) noexcept {
::memcpy(buff, &w, sizeof(w));
buff += sizeof(w);
}
uint8_t *simdjson2msgpack::skip_uint32() noexcept {
uint8_t *simdjsonondemand2msgpack::skip_uint32() noexcept {
uint8_t *ret = buff;
buff += sizeof(uint32_t);
return ret;
}
void simdjson2msgpack::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
void simdjsonondemand2msgpack::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
::memcpy(p, &w, sizeof(w));
}
void simdjson2msgpack::write_raw_string(
void simdjsonondemand2msgpack::write_raw_string(
simdjson::ondemand::raw_json_string in) {
write_byte(0xdb);
uint8_t *location = skip_uint32();
@@ -140,7 +140,7 @@ void simdjson2msgpack::write_raw_string(
write_uint32_at(uint32_t(v.size()), location);
}
void simdjson2msgpack::recursive_processor(simdjson::ondemand::value element) {
void simdjsonondemand2msgpack::recursive_processor(simdjson::ondemand::value element) {
switch (element.type()) {
case simdjson::ondemand::json_type::array: {
uint32_t counter = 0;
@@ -185,7 +185,7 @@ void simdjson2msgpack::recursive_processor(simdjson::ondemand::value element) {
}
void simdjson2msgpack::recursive_processor_ref(simdjson::ondemand::value& element) {
void simdjsonondemand2msgpack::recursive_processor_ref(simdjson::ondemand::value& element) {
switch (element.type()) {
case simdjson::ondemand::json_type::array: {
uint32_t counter = 0;
@@ -234,7 +234,7 @@ void simdjson2msgpack::recursive_processor_ref(simdjson::ondemand::value& elemen
struct simdjson_ondemand {
using StringType = std::string_view;
simdjson2msgpack parser{};
simdjsonondemand2msgpack parser{};
bool run(simdjson::padded_string &json, char *buffer,
std::string_view &result) {
@@ -0,0 +1,250 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "json2msgpack.h"
namespace json2msgpack {
using namespace simdjson;
/**
* @brief The simdjsonsinglestage2msgpack struct is used to quickly convert
* JSON strings to msgpack views. You must provide a pointer to
* a large memory region where the msgpack gets written. The
* buffer should be large enough to store the msgpack output (which
* can never be 3x larger than the input JSON) with an additional
* simdjson::SIMDJSON_PADDING bytes.
*
* Recommended usage:
*
* simdjsonsinglestage2msgpack parser{};
* simdjson::padded_string json = "[1,2]"_padded; // some JSON
* uint8_t * buffer = new uint8_t[3*json.size() + simdjson::SIMDJSON_PADDING]; // large buffer
*
* std::string_view msgpack = parser.to_msgpack(json, buffer);
*
* The result (msgpack) is a string view to a msgpack serialization of the input JSON,
* it points inside the buffer you provided.
*
* You may reuse the simdjsonsinglestage2msgpack instance though you should use
* one per thread.
*/
struct simdjsonsinglestage2msgpack {
/**
* @brief Converts the provided JSON into msgpack.
*
* @param json JSON input
* @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes
* of padding)
* @return std::string_view msgpack output, writing to the temporary buffer
*/
inline std::string_view to_msgpack(const simdjson::padded_string &json,
uint8_t *buf);
private:
simdjson_inline void write_double(const double d) noexcept;
simdjson_inline void write_byte(const uint8_t b) noexcept;
simdjson_inline void write_uint32(const uint32_t w) noexcept;
simdjson_inline uint8_t *skip_uint32() noexcept;
simdjson_inline void write_uint32_at(const uint32_t w,
uint8_t *p) noexcept;
simdjson_inline void
write_raw_string(simdjson::singlestage::raw_json_string rjs);
inline void recursive_processor(simdjson::singlestage::value element);
inline void recursive_processor_ref(simdjson::singlestage::value& element);
simdjson::singlestage::parser parser;
uint8_t *buff{};
};
std::string_view
simdjsonsinglestage2msgpack::to_msgpack(const simdjson::padded_string &json,
uint8_t *buf) {
buff = buf;
singlestage::document doc = parser.iterate(json);
if (doc.is_scalar()) {
// we have a special case where the JSON document is a single document...
switch (doc.type()) {
case simdjson::singlestage::json_type::number:
write_double(doc.get_double());
break;
case simdjson::singlestage::json_type::string:
write_raw_string(doc.get_raw_json_string());
break;
case simdjson::singlestage::json_type::boolean:
write_byte(0xc2 + doc.get_bool());
break;
case simdjson::singlestage::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(doc.is_null()) {
write_byte(0xc0);
}
break;
case simdjson::singlestage::json_type::array:
case simdjson::singlestage::json_type::object:
default:
// impossible
SIMDJSON_UNREACHABLE();
}
} else {
simdjson::singlestage::value val = doc;
#define SIMDJSON_GCC_COMPILER ((__GNUC__) && !(__clang__) && !(__INTEL_COMPILER))
#if SIMDJSON_GCC_COMPILER
// the GCC compiler does well with by-value passing.
// GCC has superior recursive inlining:
// https://stackoverflow.com/questions/29186186/why-does-gcc-generate-a-faster-program-than-clang-in-this-recursive-fibonacci-co
// https://godbolt.org/z/TeK4doE51
recursive_processor(val);
#else
recursive_processor_ref(val);
#endif
}
if (!doc.at_end()) {
throw "There are unexpectedly tokens after the end of the json in the json2msgpack sample data";
}
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
}
void simdjsonsinglestage2msgpack::write_double(const double d) noexcept {
*buff++ = 0xcb;
::memcpy(buff, &d, sizeof(d));
buff += sizeof(d);
}
void simdjsonsinglestage2msgpack::write_byte(const uint8_t b) noexcept {
*buff = b;
buff++;
}
void simdjsonsinglestage2msgpack::write_uint32(const uint32_t w) noexcept {
::memcpy(buff, &w, sizeof(w));
buff += sizeof(w);
}
uint8_t *simdjsonsinglestage2msgpack::skip_uint32() noexcept {
uint8_t *ret = buff;
buff += sizeof(uint32_t);
return ret;
}
void simdjsonsinglestage2msgpack::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
::memcpy(p, &w, sizeof(w));
}
void simdjsonsinglestage2msgpack::write_raw_string(
simdjson::singlestage::raw_json_string in) {
write_byte(0xdb);
uint8_t *location = skip_uint32();
std::string_view v = parser.unescape(in, buff);
write_uint32_at(uint32_t(v.size()), location);
}
void simdjsonsinglestage2msgpack::recursive_processor(simdjson::singlestage::value element) {
switch (element.type()) {
case simdjson::singlestage::json_type::array: {
uint32_t counter = 0;
write_byte(0xdd);
uint8_t *location = skip_uint32();
for (auto child : element.get_array()) {
counter++;
recursive_processor(child.value());
}
write_uint32_at(counter, location);
} break;
case simdjson::singlestage::json_type::object: {
uint32_t counter = 0;
write_byte(0xdf);
uint8_t *location = skip_uint32();
for (auto field : element.get_object()) {
counter++;
write_raw_string(field.key());
recursive_processor(field.value());
}
write_uint32_at(counter, location);
} break;
case simdjson::singlestage::json_type::number:
write_double(element.get_double());
break;
case simdjson::singlestage::json_type::string:
write_raw_string(element.get_raw_json_string());
break;
case simdjson::singlestage::json_type::boolean:
write_byte(0xc2 + element.get_bool());
break;
case simdjson::singlestage::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
write_byte(0xc0);
}
break;
default:
SIMDJSON_UNREACHABLE();
}
}
void simdjsonsinglestage2msgpack::recursive_processor_ref(simdjson::singlestage::value& element) {
switch (element.type()) {
case simdjson::singlestage::json_type::array: {
uint32_t counter = 0;
write_byte(0xdd);
uint8_t *location = skip_uint32();
for (auto child : element.get_array()) {
counter++;
simdjson::singlestage::value v = child.value();
recursive_processor_ref(v);
}
write_uint32_at(counter, location);
} break;
case simdjson::singlestage::json_type::object: {
uint32_t counter = 0;
write_byte(0xdf);
uint8_t *location = skip_uint32();
for (auto field : element.get_object()) {
counter++;
write_raw_string(field.key());
simdjson::singlestage::value v = field.value();
recursive_processor_ref(v);
}
write_uint32_at(counter, location);
} break;
case simdjson::singlestage::json_type::number:
write_double(element.get_double());
break;
case simdjson::singlestage::json_type::string:
write_raw_string(element.get_raw_json_string());
break;
case simdjson::singlestage::json_type::boolean:
write_byte(0xc2 + element.get_bool());
break;
case simdjson::singlestage::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
write_byte(0xc0);
}
break;
default:
SIMDJSON_UNREACHABLE();
}
}
struct simdjson_singlestage {
using StringType = std::string_view;
simdjsonsinglestage2msgpack parser{};
bool run(simdjson::padded_string &json, char *buffer,
std::string_view &result) {
result = parser.to_msgpack(json, reinterpret_cast<uint8_t *>(buffer));
return true;
}
};
BENCHMARK_TEMPLATE(json2msgpack, simdjson_singlestage)->UseManualTime();
} // namespace json2msgpack
#endif // SIMDJSON_EXCEPTIONS
@@ -13,6 +13,8 @@ void maybe_display_implementation() {
std::cout << "simdjson::dom implementation: " << simdjson::get_active_implementation()->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 1): " << simdjson::get_active_implementation()->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 2): " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "simdjson::singlestage implementation (stage 1): " << simdjson::get_active_implementation()->name() << std::endl;
std::cout << "simdjson::singlestage implementation (stage 2): " << simdjson::builtin_implementation()->name() << std::endl;
}
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
struct simdjson_singlestage {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (singlestage::object point : doc.find_field("coordinates")) {
result.emplace_back(json_benchmark::point{point.find_field("x"), point.find_field("y"), point.find_field("z")});
}
return true;
}
};
BENCHMARK_TEMPLATE(kostya, simdjson_singlestage)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,72 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_amazon_cellphones.h"
namespace large_amazon_cellphones {
using namespace simdjson;
template<bool threaded>
struct simdjson_singlestage {
using StringType = std::string;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
#ifdef SIMDJSON_THREADS_ENABLED
parser.threaded = threaded;
#endif
singlestage::document_stream stream = parser.iterate_many(json);
singlestage::document_stream::iterator i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto doc = *i;
size_t index{0};
StringType copy;
double rating;
uint64_t reviews;
for ( auto value : doc ) {
switch (index)
{
case 1:
copy = StringType(std::string_view(value));
break;
case 5:
rating = double(value);
break;
case 7:
reviews = uint64_t(value);
break;
default:
break;
}
index++;
}
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, large_amazon_cellphones::brand{
rating * reviews,
reviews
});
} else { // Otherwise, update key data
x->second.cumulative_rating += rating * reviews;
x->second.reviews_count += reviews;
}
}
return true;
}
};
BENCHMARK_TEMPLATE(large_amazon_cellphones, simdjson_singlestage<UNTHREADED>)->UseManualTime();
#ifdef SIMDJSON_THREADS_ENABLED
BENCHMARK_TEMPLATE(large_amazon_cellphones, simdjson_singlestage<THREADED>)->UseManualTime();
#endif
} // namespace amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,29 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_random.h"
namespace large_random {
using namespace simdjson;
struct simdjson_singlestage {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (singlestage::object coord : doc) {
result.emplace_back(json_benchmark::point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_singlestage)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,29 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_random.h"
namespace large_random {
using namespace simdjson;
struct simdjson_singlestage_unordered {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (singlestage::object coord : doc) {
result.emplace_back(json_benchmark::point{coord["x"], coord["y"], coord["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_singlestage_unordered)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
class OnDemand {
public:
simdjson_inline bool Run(const padded_string &json);
simdjson_inline const std::vector<my_point> &Result() { return container; }
simdjson_inline size_t ItemCount() { return container.size(); }
private:
singlestage::parser parser{};
std::vector<my_point> container{};
};
simdjson_inline bool OnDemand::Run(const padded_string &json) {
container.clear();
auto doc = parser.iterate(json);
for (singlestage::object coord : doc) {
container.emplace_back(my_point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, OnDemand);
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,48 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
struct simdjson_singlestage {
using StringType=std::string_view;
singlestage::parser parser{};
simdjson_inline uint64_t nullable_int(singlestage::value value) {
if (value.is_null()) { return 0; }
return value;
}
simdjson_inline twitter_user<std::string_view> read_user(singlestage::object user) {
return { user.find_field("id"), user.find_field("screen_name") };
}
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
// Walk the document, parsing the tweets as we go
auto doc = parser.iterate(json);
for (singlestage::object tweet : doc.find_field("statuses")) {
result.emplace_back(partial_tweets::tweet<std::string_view>{
tweet.find_field("created_at"),
tweet.find_field("id"),
tweet.find_field("text"),
nullable_int(tweet.find_field("in_reply_to_status_id")),
read_user(tweet.find_field("user")),
tweet.find_field("retweet_count"),
tweet.find_field("favorite_count")
});
}
return true;
}
};
BENCHMARK_TEMPLATE(partial_tweets, simdjson_singlestage)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class SingleStage {
public:
SingleStage() {
if(!displayed_implementation) {
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
displayed_implementation = true;
}
}
simdjson_inline bool Run(const padded_string &json);
simdjson_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_inline size_t ItemCount() { return tweets.size(); }
private:
singlestage::parser parser{};
std::vector<tweet> tweets{};
simdjson_inline uint64_t nullable_int(singlestage::value value) {
if (value.is_null()) { return 0; }
return value;
}
simdjson_inline twitter_user read_user(singlestage::object user) {
return { user.find_field("id"), user.find_field("screen_name") };
}
static inline bool displayed_implementation = false;
};
simdjson_inline bool SingleStage::Run(const padded_string &json) {
tweets.clear();
// Walk the document, parsing the tweets as we go
auto doc = parser.iterate(json);
for (singlestage::object tweet : doc.find_field("statuses")) {
tweets.emplace_back(partial_tweets::tweet{
tweet.find_field("created_at"),
tweet.find_field("id"),
tweet.find_field("text"),
nullable_int(tweet.find_field("in_reply_to_status_id")),
read_user(tweet.find_field("user")),
tweet.find_field("retweet_count"),
tweet.find_field("favorite_count")
});
}
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, SingleStage);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
@@ -0,0 +1,80 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "top_tweet.h"
namespace top_tweet {
using namespace simdjson;
struct simdjson_singlestage {
using StringType=std::string_view;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
result.retweet_count = -1;
// We save these DOM values for later so we don't have to parse them
// into string_views until we're sure which ones we want to parse
// NOTE: simdjson does not presently support reuse of objects or arrays--just scalars. This is
// why we have to grab the text and screen_name fields instead of just saving the tweet object.
singlestage::value screen_name, text;
auto doc = parser.iterate(json);
for (auto tweet : doc["statuses"]) {
// Since text, user.screen_name, and retweet_count generally appear in order, it's nearly free
// for us to retrieve them here (and will cost a bit more if we do it in the if
// statement).
auto tweet_text = tweet["text"];
auto tweet_screen_name = tweet["user"]["screen_name"];
int64_t retweet_count = tweet["retweet_count"];
if (retweet_count <= max_retweet_count && retweet_count >= result.retweet_count) {
result.retweet_count = retweet_count;
// TODO std::move should not be necessary
text = std::move(tweet_text);
screen_name = std::move(tweet_screen_name);
}
}
// Now that we know which was the most retweeted, parse the values in it
result.screen_name = screen_name;
result.text = text;
return result.retweet_count != -1;
}
};
BENCHMARK_TEMPLATE(top_tweet, simdjson_singlestage)->UseManualTime();
struct simdjson_singlestage_forward_only {
using StringType=std::string_view;
singlestage::parser parser{};
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
result.retweet_count = -1;
auto doc = parser.iterate(json);
for (auto tweet : doc["statuses"]) {
// Since text, user.screen_name, and retweet_count generally appear in order, it's nearly free
// for us to retrieve them here (and will cost a bit more if we do it in the if
// statement).
auto tweet_text = tweet["text"];
auto tweet_screen_name = tweet["user"]["screen_name"];
int64_t retweet_count = tweet["retweet_count"];
if (retweet_count <= max_retweet_count && retweet_count >= result.retweet_count) {
result.retweet_count = retweet_count;
result.text = tweet_text;
result.screen_name = tweet_screen_name;
}
}
return result.retweet_count != -1;
}
};
BENCHMARK_TEMPLATE(top_tweet, simdjson_singlestage_forward_only)->UseManualTime();
} // namespace top_tweet
#endif // SIMDJSON_EXCEPTIONS
+10
View File
@@ -35,4 +35,14 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_quickstart_test(quickstart_ondemand_noexceptions quickstart_ondemand_noexceptions.cpp NO_EXCEPTIONS LABELS quickstart_ondemand acceptance)
add_quickstart_test(quickstart_ondemand_noexceptions11 quickstart_ondemand_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11 LABELS quickstart_ondemand)
# SingleStage Quick Start
if (SIMDJSON_EXCEPTIONS)
add_quickstart_test(quickstart_singlestage quickstart_singlestage.cpp LABELS quickstart_singlestage acceptance)
add_quickstart_test(quickstart_singlestage11 quickstart_singlestage.cpp CXX_STANDARD c++11 LABELS quickstart_singlestage acceptance)
add_quickstart_test(quickstart_singlestage14 quickstart_singlestage.cpp CXX_STANDARD c++14 LABELS quickstart_singlestage)
endif()
add_quickstart_test(quickstart_singlestage_noexceptions quickstart_singlestage_noexceptions.cpp NO_EXCEPTIONS LABELS quickstart_singlestage acceptance)
add_quickstart_test(quickstart_singlestage_noexceptions11 quickstart_singlestage_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11 LABELS quickstart_singlestage)
endif()
@@ -0,0 +1,9 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
@@ -0,0 +1,21 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main(void) {
padded_string json;
auto error = padded_string::load("twitter.json").get(json);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
ondemand::parser parser;
ondemand::document tweets;
error = parser.iterate(json).get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t count;
error = tweets["search_metadata"]["count"].get(count);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << count << " results." << std::endl;
return EXIT_SUCCESS;
}
+1
View File
@@ -63,6 +63,7 @@ if(SIMDJSON_ENABLE_FUZZING)
implement_fuzzer(fuzz_minifyimpl) # minify *without* parsing, plus compare implementations
implement_fuzzer(fuzz_ndjson) # the ndjson api
implement_fuzzer(fuzz_ondemand)
# implement_fuzzer(fuzz_singlestage) # TODO why no worky
implement_fuzzer(fuzz_padded)
implement_fuzzer(fuzz_parser)
implement_fuzzer(fuzz_print_json)
+74
View File
@@ -0,0 +1,74 @@
#include "FuzzUtils.h"
#include "simdjson.h"
#include <cstddef>
#include <cstdint>
#include <string>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzData fd(Data, Size);
const int action = fd.getInt<0, 12>();
// split the remainder of the document into strings
auto strings = fd.splitIntoStrings();
while (strings.size() < 1) {
strings.emplace_back();
}
#if SIMDJSON_EXCEPTIONS
try {
#endif
simdjson::singlestage::parser parser;
simdjson::padded_string padded(strings[0]);
auto doc = parser.iterate(padded);
if (doc.error()) {
return 0;
}
for (auto item : doc) {
switch (action) {
case 0: {
simdjson_unused auto x = item.get_string();
} break;
case 1: {
simdjson_unused auto x = item.get_bool();
} break;
case 2: {
simdjson_unused auto x = item.get_array();
} break;
case 3: {
simdjson_unused auto x = item.get_int64();
} break;
case 4: {
simdjson_unused auto x = item.get_double();
} break;
case 5: {
simdjson_unused auto x = item.get_object();
} break;
case 6: {
simdjson_unused auto x = item.get_uint64();
} break;
case 7: {
simdjson_unused auto x = item.get_raw_json_string();
} break;
case 8: {
simdjson_unused auto x = item.is_null();
} break;
case 9: {
simdjson_unused auto x = item.begin();
} break;
case 10: {
simdjson_unused auto x = item.end();
} break;
case 11: {
for (auto e : item) {
simdjson_unused auto x = e.is_null();
}
} break;
default:;
}
}
#if SIMDJSON_EXCEPTIONS
} catch (...) {
}
#endif
return 0;
}
+1
View File
@@ -53,5 +53,6 @@
#include "simdjson/dom.h"
#include "simdjson/ondemand.h"
#include "simdjson/singlestage.h"
#endif // SIMDJSON_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_ARM64_SINGLESTAGE_H
#define SIMDJSON_ARM64_SINGLESTAGE_H
#include "simdjson/arm64/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/arm64/end.h"
#endif // SIMDJSON_ARM64_SINGLESTAGE_H
+36
View File
@@ -0,0 +1,36 @@
#ifndef SIMDJSON_BUILTIN_SINGLESTAGE_H
#define SIMDJSON_BUILTIN_SINGLESTAGE_H
#include "simdjson/builtin.h"
#include "simdjson/builtin/base.h"
#include "simdjson/generic/singlestage/dependencies.h"
#define SIMDJSON_AMALGAMATED
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
#include "simdjson/arm64/singlestage.h"
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
#include "simdjson/fallback/singlestage.h"
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
#include "simdjson/haswell/singlestage.h"
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
#include "simdjson/icelake/singlestage.h"
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
#include "simdjson/ppc64/singlestage.h"
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
#include "simdjson/westmere/singlestage.h"
#else
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
#endif
#undef SIMDJSON_AMALGAMATED
namespace simdjson {
/**
* @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::singlestage
*/
namespace singlestage = SIMDJSON_BUILTIN_IMPLEMENTATION::singlestage;
} // namespace simdjson
#endif // SIMDJSON_BUILTIN_SINGLESTAGE_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_FALLBACK_SINGLESTAGE_H
#define SIMDJSON_FALLBACK_SINGLESTAGE_H
#include "simdjson/fallback/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/fallback/end.h"
#endif // SIMDJSON_FALLBACK_SINGLESTAGE_H
+10
View File
@@ -96,6 +96,16 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi)
}
#endif
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
} // namespace jsoncharutils
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
@@ -13,29 +13,18 @@
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::value& x) noexcept {
@@ -66,7 +55,7 @@ inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION:
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(x.raw_json_token());
}
}
@@ -74,14 +63,14 @@ inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION:
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> x) {
@@ -0,0 +1,40 @@
#if defined(SIMDJSON_AMALGAMATED) && !defined(SIMDJSON_GENERIC_SINGLESTAGE_DEPENDENCIES_H)
#error simdjson/generic/singlestage/dependencies.h must be included before simdjson/generic/singlestage/amalgamated.h!
#endif
// Stuff other things depend on
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#include "simdjson/generic/singlestage/value.h"
#include "simdjson/generic/singlestage/logger.h"
#include "simdjson/generic/singlestage/token_iterator.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/json_type.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/parser.h"
// All other declarations
#include "simdjson/generic/singlestage/array.h"
#include "simdjson/generic/singlestage/array_iterator.h"
#include "simdjson/generic/singlestage/document.h"
#include "simdjson/generic/singlestage/field.h"
#include "simdjson/generic/singlestage/object.h"
#include "simdjson/generic/singlestage/object_iterator.h"
#include "simdjson/generic/singlestage/serialization.h"
// Inline definitions
#include "simdjson/generic/singlestage/array-inl.h"
#include "simdjson/generic/singlestage/array_iterator-inl.h"
#include "simdjson/generic/singlestage/document-inl.h"
#include "simdjson/generic/singlestage/field-inl.h"
#include "simdjson/generic/singlestage/json_iterator-inl.h"
#include "simdjson/generic/singlestage/json_type-inl.h"
#include "simdjson/generic/singlestage/logger-inl.h"
#include "simdjson/generic/singlestage/object-inl.h"
#include "simdjson/generic/singlestage/object_iterator-inl.h"
#include "simdjson/generic/singlestage/parser-inl.h"
#include "simdjson/generic/singlestage/raw_json_string-inl.h"
#include "simdjson/generic/singlestage/serialization-inl.h"
#include "simdjson/generic/singlestage/token_iterator-inl.h"
#include "simdjson/generic/singlestage/value-inl.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
@@ -0,0 +1,226 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/array.h"
#include "simdjson/generic/singlestage/array_iterator-inl.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/value.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
return iter.reset_array();
}
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
// Read the array index
size_t array_index = 0;
size_t i;
for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
uint8_t digit = uint8_t(json_pointer[i] - '0');
// Check for non-digit in array index. If it's there, we're trying to get a field in an object
if (digit > 9) { return INCORRECT_TYPE; }
array_index = array_index*10 + digit;
}
// 0 followed by other digits is invalid
if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
// Empty string is invalid; so is a "/" with no digits before it
if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::array &&value
) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array>(
std::forward<SIMDJSON_IMPLEMENTATION::singlestage::array>(value)
)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array>(error)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::begin() noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_INL_H
@@ -0,0 +1,199 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* singlestage::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> begin() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_H
@@ -0,0 +1,78 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/array_iterator.h"
#include "simdjson/generic/singlestage/value-inl.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::array_iterator &&value
) noexcept
: SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::simdjson_result(error_code error) noexcept
: SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_INL_H
@@ -0,0 +1,96 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_H
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called exactly once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
private:
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_ARRAY_ITERATOR_H
@@ -0,0 +1,46 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_BASE_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_BASE_H
#include "simdjson/generic/base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace singlestage {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::SIMDJSON_IMPLEMENTATION::number_type */
using number_type = simdjson::SIMDJSON_IMPLEMENTATION::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_BASE_H
@@ -0,0 +1,17 @@
#ifdef SIMDJSON_AMALGAMATED
#error simdjson/generic/singlestage/dependencies.h must be included before defining SIMDJSON_AMALGAMATED!
#endif
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_DEPENDENCIES_H
#define SIMDJSON_GENERIC_SINGLESTAGE_DEPENDENCIES_H
// Internal headers needed for singlestage generics.
// All includes not under simdjson/generic/singlestage must be here!
// Otherwise, amalgamation will fail.
#include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY
#include "simdjson/implementation.h"
#include "simdjson/padded_string.h"
#include "simdjson/padded_string_view.h"
#include "simdjson/internal/dom_parser_implementation.h"
#endif // SIMDJSON_GENERIC_SINGLESTAGE_DEPENDENCIES_H
@@ -0,0 +1,823 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/array-inl.h"
#include "simdjson/generic/singlestage/array_iterator.h"
#include "simdjson/generic/singlestage/document.h"
#include "simdjson/generic/singlestage/json_iterator-inl.h"
#include "simdjson/generic/singlestage/json_type.h"
#include "simdjson/generic/singlestage/object-inl.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/value.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline document::document(singlestage::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
iter.assert_at_document_depth();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
template<> simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
template<typename T> simdjson_inline error_code document::get(T &out) & noexcept {
return get<T>().get(out);
}
template<typename T> simdjson_inline error_code document::get(T &out) && noexcept {
return std::forward<document>(*this).get<T>().get(out);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline error_code document::consume() noexcept {
auto error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline bool document::is_negative() noexcept {
return get_root_value_iterator().is_root_negative();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_start_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::document &&value
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::document>(
std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document>(value)
)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get() & noexcept {
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document>(first).get<T>();
}
template<typename T>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get(T &out) & noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get(T &out) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get<SIMDJSON_IMPLEMENTATION::singlestage::document>() & noexcept = delete;
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get<SIMDJSON_IMPLEMENTATION::singlestage::document>() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document>(first);
}
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get<SIMDJSON_IMPLEMENTATION::singlestage::document>(SIMDJSON_IMPLEMENTATION::singlestage::document &out) & noexcept = delete;
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get<SIMDJSON_IMPLEMENTATION::singlestage::document>(SIMDJSON_IMPLEMENTATION::singlestage::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator SIMDJSON_IMPLEMENTATION::singlestage::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator SIMDJSON_IMPLEMENTATION::singlestage::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::operator SIMDJSON_IMPLEMENTATION::singlestage::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
} // namespace simdjson
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
#if SIMDJSON_EXCEPTIONS
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return raw_json_string(*doc); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<const char *> document_reference::current_location() noexcept { return doc->current_location(); }
simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); }
simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator SIMDJSON_IMPLEMENTATION::singlestage::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator SIMDJSON_IMPLEMENTATION::singlestage::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::operator SIMDJSON_IMPLEMENTATION::singlestage::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_INL_H
@@ -0,0 +1,806 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
}
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value.
*
* @returns A value value.
* @exception if a JSON value cannot be found
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive).
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```c++
* simdjson::singlestage::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* It is the default, however, because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an singlestage::number. An singlestage::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* singlestage::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* singlestage::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() automatically calls rewind between each call. Thus
* all values, objects and arrays that you have created so far (including unescaped strings)
* are invalidated. After calling at_pointer, you need to consume the result: string values
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
protected:
/**
* Consumes the document.
*/
simdjson_inline error_code consume() noexcept;
simdjson_inline document(singlestage::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(singlestage::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class singlestage::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
private:
document *doc{nullptr};
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::document> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> get_array() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at_pointer(std::string_view json_pointer) noexcept;
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::document_reference> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> get_array() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at_pointer(std::string_view json_pointer) noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_DOCUMENT_H
@@ -0,0 +1,90 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_FIELD_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_FIELD_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/field.h"
#include "simdjson/generic/singlestage/value-inl.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, singlestage::value>() {}
simdjson_inline field::field(raw_json_string key, singlestage::value &&value) noexcept
: std::pair<raw_json_string, singlestage::value>(key, std::forward<singlestage::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
return first;
}
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::field &&value
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::field>(
std::forward<SIMDJSON_IMPLEMENTATION::singlestage::field>(value)
)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::field>(error)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_FIELD_INL_H
@@ -0,0 +1,82 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_FIELD_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_FIELD_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/value.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test".
*/
simdjson_inline raw_json_string key() const noexcept;
/**
* Get the field value.
*/
simdjson_inline singlestage::value &value() & noexcept;
/**
* @overload singlestage::value &singlestage::value() & noexcept
*/
simdjson_inline singlestage::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, singlestage::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::field> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_FIELD_H
@@ -0,0 +1,409 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_INL_H
#include "simdjson/internal/dom_parser_implementation.h"
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/parser.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/logger-inl.h"
#include "simdjson/generic/singlestage/parser-inl.h"
#include "simdjson/generic/singlestage/token_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, singlestage::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
if(*peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(TAPE_ERROR, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek(0);
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
return parser->unescape(in, _string_buf_loc, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
return parser->unescape_wobbly(in, _string_buf_loc);
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_INL_H
@@ -0,0 +1,324 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/token_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
singlestage::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token to retrieve.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* This is not null-terminated; it is a view into the JSON.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, singlestage::parser *parser) noexcept;
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
template <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_iterator> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_JSON_ITERATOR_H
@@ -0,0 +1,118 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/json_type.h"
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::json_type &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_type>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::json_type>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_INL_H
@@ -0,0 +1,162 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/numberparsing.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* The type of a JSON value.
*/
enum class json_type {
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline singlestage::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::slow_float_parsing(simdjson_unused const uint8_t * src, W writer);
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::json_type> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_JSON_TYPE_H
@@ -0,0 +1,222 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/logger.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_INL_H
@@ -0,0 +1,58 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_H
#include "simdjson/generic/singlestage/base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_LOGGER_H
@@ -0,0 +1,260 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/field.h"
#include "simdjson/generic/singlestage/object.h"
#include "simdjson/generic/singlestage/object_iterator.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/value-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek(0)};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
return iter.reset_object();
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::object &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::object>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object>(error) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::begin() noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::object>(first)[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::singlestage::object>(first).find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_INL_H
@@ -0,0 +1,239 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
simdjson_inline simdjson_result<object_iterator> begin() noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive).
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```c++
* simdjson::singlestage::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* It is the default, however, because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* singlestage::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* singlestage::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the object. You should still consume values only once even if you
* can iterate through the object more than once. If you unescape a string within
* the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
friend class value;
friend class document;
friend struct simdjson_result<object>;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> begin() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> end() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at_pointer(std::string_view json_pointer) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_H
@@ -0,0 +1,138 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/object_iterator.h"
#include "simdjson/generic/singlestage/field-inl.h"
#include "simdjson/generic/singlestage/value_iterator-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_INL_H
@@ -0,0 +1,80 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
simdjson_inline object_iterator &operator++() noexcept;
private:
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_OBJECT_ITERATOR_H
@@ -0,0 +1,149 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_PARSER_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_PARSER_INL_H
#include "simdjson/padded_string.h"
#include "simdjson/padded_string_view.h"
#include "simdjson/implementation.h"
#include "simdjson/internal/dom_parser_implementation.h"
#include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/document.h"
#include "simdjson/generic/singlestage/parser.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity == capacity() && new_max_depth == max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity < dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::parser>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::parser &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::parser>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::parser>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_PARSER_INL_H
@@ -0,0 +1,253 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_PARSER_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_PARSER_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* singlestage::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* singlestage::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The singlestage::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of singlestage::value, singlestage::object, singlestage::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/** The capacity of this parser (the largest document it can process). */
simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
private:
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::parser> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::parser> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_PARSER_H
@@ -0,0 +1,203 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/json_iterator-inl.h"
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast<const char *>(buf); }
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
// if the content has no escape character, just scan through it quickly!
for(;pos < target.size() && target[pos] != '\\';pos++) {}
// slow path may begin.
bool escaping{false};
for(;pos < target.size();pos++) {
if((target[pos] == '"') && !escaping) {
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
// if the content has no escape character, just scan through it quickly!
for(;target[pos] && target[pos] != '\\';pos++) {}
// slow path may begin.
bool escaping{false};
for(;target[pos];pos++) {
if((target[pos] == '"') && !escaping) {
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
const char * r{raw()};
size_t pos{0};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>::unescape(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string>::unescape_wobbly(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_INL_H
@@ -0,0 +1,206 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an singlestage::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(SIMDJSON_IMPLEMENTATION::singlestage::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_RAW_JSON_STRING_H
@@ -0,0 +1,223 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/array.h"
#include "simdjson/generic/singlestage/document-inl.h"
#include "simdjson/generic/singlestage/json_type.h"
#include "simdjson/generic/singlestage/object.h"
#include "simdjson/generic/singlestage/serialization.h"
#include "simdjson/generic/singlestage/value.h"
#include "simdjson/generic/jsoncharutils.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace SIMDJSON_IMPLEMENTATION::singlestage;
SIMDJSON_IMPLEMENTATION::singlestage::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
SIMDJSON_IMPLEMENTATION::singlestage::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
SIMDJSON_IMPLEMENTATION::singlestage::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return SIMDJSON_IMPLEMENTATION::jsoncharutils::trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace singlestage {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::SIMDJSON_IMPLEMENTATION::singlestage
#endif // SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_INL_H
@@ -0,0 +1,103 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_H
#include "simdjson/generic/singlestage/base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::singlestage::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> x);
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace singlestage {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTATION::singlestage::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::singlestage::object> x);
#endif
}}} // namespace simdjson::SIMDJSON_IMPLEMENTATION::singlestage
#endif // SIMDJSON_GENERIC_SINGLESTAGE_SERIALIZATION_H
@@ -0,0 +1,89 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/token_iterator.h"
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator>::simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::token_iterator &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator>(value)) {}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_INL_H
@@ -0,0 +1,151 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/logger.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class value_iterator;
friend class object;
template <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::token_iterator> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_TOKEN_ITERATOR_H
@@ -0,0 +1,439 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_VALUE_INL_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_VALUE_INL_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/singlestage/array.h"
#include "simdjson/generic/singlestage/array_iterator.h"
#include "simdjson/generic/singlestage/json_iterator.h"
#include "simdjson/generic/singlestage/json_type.h"
#include "simdjson/generic/singlestage/object.h"
#include "simdjson/generic/singlestage/raw_json_string.h"
#include "simdjson/generic/singlestage/value.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<typename T> simdjson_inline error_code value::get(T &out) noexcept {
return get<T>().get(out);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline bool value::is_negative() noexcept {
return iter.is_negative();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::simdjson_result(
SIMDJSON_IMPLEMENTATION::singlestage::value &&value
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::value>(
std::forward<SIMDJSON_IMPLEMENTATION::singlestage::value>(value)
)
{
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get() noexcept {
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get(T &out) noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get<SIMDJSON_IMPLEMENTATION::singlestage::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get<SIMDJSON_IMPLEMENTATION::singlestage::value>(SIMDJSON_IMPLEMENTATION::singlestage::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator SIMDJSON_IMPLEMENTATION::singlestage::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator SIMDJSON_IMPLEMENTATION::singlestage::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_VALUE_INL_H
@@ -0,0 +1,707 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_VALUE_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_VALUE_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/singlestage/value_iterator.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T> simdjson_inline error_code get(T &out) noexcept;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive).
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```c++
* simdjson::singlestage::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* It is the default, however, because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an singlestage::number. An singlestage::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* singlestage::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* singlestage::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
};
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::value> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array> get_array() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() noexcept;
template<typename T> simdjson_inline error_code get(T &out) noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::array() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::singlestage::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive).
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```c++
* simdjson::singlestage::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* It is the default, however, because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value> at_pointer(std::string_view json_pointer) noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_VALUE_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,486 @@
#ifndef SIMDJSON_GENERIC_SINGLESTAGE_VALUE_ITERATOR_H
#ifndef SIMDJSON_AMALGAMATED
#define SIMDJSON_GENERIC_SINGLESTAGE_VALUE_ITERATOR_H
#include "simdjson/generic/singlestage/base.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#endif // SIMDJSON_AMALGAMATED
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace singlestage {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class array;
friend class value;
}; // value_iterator
} // namespace singlestage
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::singlestage::value_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::singlestage::value_iterator> {
public:
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::singlestage::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SINGLESTAGE_VALUE_ITERATOR_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_HASWELL_SINGLESTAGE_H
#define SIMDJSON_HASWELL_SINGLESTAGE_H
#include "simdjson/haswell/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/haswell/end.h"
#endif // SIMDJSON_HASWELL_SINGLESTAGE_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_ICELAKE_SINGLESTAGE_H
#define SIMDJSON_ICELAKE_SINGLESTAGE_H
#include "simdjson/icelake/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/icelake/end.h"
#endif // SIMDJSON_ICELAKE_SINGLESTAGE_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_PPC64_SINGLESTAGE_H
#define SIMDJSON_PPC64_SINGLESTAGE_H
#include "simdjson/ppc64/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/ppc64/end.h"
#endif // SIMDJSON_PPC64_SINGLESTAGE_H
+13
View File
@@ -0,0 +1,13 @@
#ifndef SIMDJSON_SINGLESTAGE_H
#define SIMDJSON_SINGLESTAGE_H
#include "simdjson/builtin/singlestage.h"
namespace simdjson {
/**
* @copydoc simdjson::builtin::singlestage
*/
namespace singlestage = builtin::singlestage;
} // namespace simdjson
#endif // SIMDJSON_SINGLESTAGE_H
+8
View File
@@ -0,0 +1,8 @@
#ifndef SIMDJSON_WESTMERE_SINGLESTAGE_H
#define SIMDJSON_WESTMERE_SINGLESTAGE_H
#include "simdjson/westmere/begin.h"
#include "simdjson/generic/singlestage/amalgamated.h"
#include "simdjson/westmere/end.h"
#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H
+4 -1
View File
@@ -12,6 +12,9 @@ cpus=$( grep processor /proc/cpuinfo | cut -d: -f 2 )
if [ "$1" = "ondemand" ]; then
echo "setting up ondemand"
policy="ondemand"
elif [ "$1" = "singlestage" ]; then
echo "setting up singlestage"
policy="singlestage"
elif [ "$1" = "performance" ]; then
echo "setting up for performance"
policy="performance"
@@ -19,7 +22,7 @@ elif [ "$1" = "list" ]; then
cpufreq-info
exit 0
else
echo "usage: powerpolicy.sh ondemand | performance list"
echo "usage: powerpolicy.sh ondemand | singlestage | performance list"
exit -1
fi
+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);
}

Some files were not shown because too many files have changed in this diff Show More