Merge pull request #629 from simdjson/jkeiser/parse-element
Return document::element from parser.parse()
This commit is contained in:
commit
9f265711a8
|
@ -49,7 +49,7 @@ The simdjson library is easily consumable with a single .h and .cpp file.
|
|||
#include "simdjson.h"
|
||||
int main(void) {
|
||||
simdjson::document::parser parser;
|
||||
simdjson::document& tweets = parser.load("twitter.json");
|
||||
simdjson::document::element tweets = parser.load("twitter.json");
|
||||
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
|
||||
}
|
||||
```
|
||||
|
|
|
@ -17,7 +17,7 @@ const padded_string EMPTY_ARRAY("[]", 2);
|
|||
static void twitter_count(State& state) {
|
||||
// Prints the number of results in twitter.json
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
uint64_t result_count = doc["search_metadata"]["count"];
|
||||
if (result_count != 100) { return; }
|
||||
|
@ -45,7 +45,7 @@ BENCHMARK(iterator_twitter_count);
|
|||
static void twitter_default_profile(State& state) {
|
||||
// Count unique users with a default profile.
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
set<string_view> default_users;
|
||||
for (document::object tweet : doc["statuses"].as_array()) {
|
||||
|
@ -62,7 +62,7 @@ BENCHMARK(twitter_default_profile);
|
|||
static void twitter_image_sizes(State& state) {
|
||||
// Count unique image sizes
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||
for (document::object tweet : doc["statuses"].as_array()) {
|
||||
|
@ -85,7 +85,7 @@ BENCHMARK(twitter_image_sizes);
|
|||
static void error_code_twitter_count(State& state) noexcept {
|
||||
// Prints the number of results in twitter.json
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
auto [value, error] = doc["search_metadata"]["count"].as_uint64_t();
|
||||
if (error) { return; }
|
||||
|
@ -97,7 +97,7 @@ BENCHMARK(error_code_twitter_count);
|
|||
static void error_code_twitter_default_profile(State& state) noexcept {
|
||||
// Count unique users with a default profile.
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
set<string_view> default_users;
|
||||
|
||||
|
@ -161,7 +161,7 @@ BENCHMARK(iterator_twitter_default_profile);
|
|||
static void error_code_twitter_image_sizes(State& state) noexcept {
|
||||
// Count unique image sizes
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (auto _ : state) {
|
||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||
auto [statuses, error] = doc["statuses"].as_array();
|
||||
|
|
|
@ -29,7 +29,7 @@ static void parser_parse_exception(State& state) {
|
|||
if (parser.set_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
for (auto _ : state) {
|
||||
try {
|
||||
UNUSED document &doc = parser.parse(EMPTY_ARRAY);
|
||||
UNUSED document::element doc = parser.parse(EMPTY_ARRAY);
|
||||
} catch(simdjson_error &j) {
|
||||
return;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ static void document_parse_exception(State& state) {
|
|||
for (auto _ : state) {
|
||||
try {
|
||||
document::parser parser;
|
||||
UNUSED document &doc = parser.parse(EMPTY_ARRAY);
|
||||
UNUSED document::element doc = parser.parse(EMPTY_ARRAY);
|
||||
} catch(simdjson_error &j) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -95,9 +95,9 @@ void simdjson_recurse(std::vector<int64_t> & v, simdjson::document::element elem
|
|||
}
|
||||
|
||||
__attribute__((noinline)) std::vector<int64_t>
|
||||
simdjson_just_dom(simdjson::document &doc) {
|
||||
simdjson_just_dom(simdjson::document::element doc) {
|
||||
std::vector<int64_t> answer;
|
||||
simdjson_recurse(answer, doc.root());
|
||||
simdjson_recurse(answer, doc);
|
||||
remove_duplicates(answer);
|
||||
return answer;
|
||||
}
|
||||
|
@ -106,8 +106,8 @@ __attribute__((noinline)) std::vector<int64_t>
|
|||
simdjson_compute_stats(const simdjson::padded_string &p) {
|
||||
std::vector<int64_t> answer;
|
||||
simdjson::document::parser parser;
|
||||
simdjson::document &doc = parser.parse(p);
|
||||
simdjson_recurse(answer, doc.root());
|
||||
simdjson::document::element doc = parser.parse(p);
|
||||
simdjson_recurse(answer, doc);
|
||||
remove_duplicates(answer);
|
||||
return answer;
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ int main(int argc, char *argv[]) {
|
|||
BEST_TIME("sasjon (just parse) ", sasjon_just_parse(p), false, , repeat,
|
||||
volume, !just_data);
|
||||
simdjson::document::parser parser;
|
||||
simdjson::document &doc = parser.parse(p);
|
||||
simdjson::document::element doc = parser.parse(p);
|
||||
BEST_TIME("simdjson (just dom) ", simdjson_just_dom(doc).size(), size,
|
||||
, repeat, volume, !just_data);
|
||||
char *buffer = (char *)malloc(p.size() + 1);
|
||||
|
|
|
@ -95,7 +95,7 @@ simdjson_compute_stats(const simdjson::padded_string &p) {
|
|||
return s;
|
||||
}
|
||||
s.valid = true;
|
||||
simdjson_recurse(s, doc.root());
|
||||
simdjson_recurse(s, doc);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
|||
reinterpret_cast<const uint8_t *>(p.data()), p.size());
|
||||
answer.byte_count = p.size();
|
||||
answer.structural_indexes_count = parser.n_structural_indexes;
|
||||
simdjson_recurse(answer, doc.root());
|
||||
simdjson_recurse(answer, doc);
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ The simdjson library offers a simple DOM tree API, which you can access by creat
|
|||
|
||||
```c++
|
||||
document::parser parser;
|
||||
document &doc = parser.load(filename); // load and parse a file
|
||||
document::element doc = parser.load(filename); // load and parse a file
|
||||
```
|
||||
|
||||
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
|
||||
|
@ -46,16 +46,14 @@ SIMDJSON_PADDING bytes at the end) and calling `parse()`:
|
|||
|
||||
```c++
|
||||
document::parser parser;
|
||||
document &doc = parser.parse("[1,2,3]"_padded); // parse a string
|
||||
document::element doc = parser.parse("[1,2,3]"_padded); // parse a string
|
||||
```
|
||||
|
||||
Using the Parsed JSON
|
||||
---------------------
|
||||
|
||||
Once you have a document, you can navigate it with idiomatic C++ iterators, operators and casts.
|
||||
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
|
||||
|
||||
* **Document Root:** To get the top level JSON element, get `doc.root()`. Many of the
|
||||
methods below will work on the document object itself, as well.
|
||||
* **Extracting Values:** You can cast a JSON element to a native type: `double(element)` or
|
||||
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
|
||||
document::object and document::array. You can also use is_*typename*()` to test if it is a
|
||||
|
@ -112,7 +110,7 @@ auto cars_json = R"( [
|
|||
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
|
||||
] )"_padded;
|
||||
document::parser parser;
|
||||
document &cars = parser.parse(cars_json);
|
||||
document::element cars = parser.parse(cars_json);
|
||||
cout << cars["/0/tire_pressure/1"] << endl; // Prints 39.9
|
||||
```
|
||||
|
||||
|
@ -123,7 +121,7 @@ All simdjson APIs that can fail return `simdjson_result<T>`, which is a <valu
|
|||
pair. The error codes and values can be accessed directly, reading the error like so:
|
||||
|
||||
```c++
|
||||
auto [doc, error] = parser.parse(json); // doc is a document&
|
||||
auto [doc, error] = parser.parse(json); // doc is a document::element
|
||||
if (error) { cerr << error << endl; exit(1); }
|
||||
// Use document here now that we've checked for the error
|
||||
```
|
||||
|
@ -138,7 +136,7 @@ behavior.
|
|||
> circumvent this, you can use this instead:
|
||||
>
|
||||
> ```c++
|
||||
> document &doc;
|
||||
> document::element doc;
|
||||
> error_code error;
|
||||
> parser.parse(json).tie(doc, error); // <-- Assigns to doc and error just like "auto [doc, error]"
|
||||
> ```
|
||||
|
@ -199,7 +197,7 @@ for (document::element car_element : cars) {
|
|||
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
|
||||
|
||||
```c++
|
||||
document &doc = parser.parse(json); // Throws an exception if there was an error!
|
||||
document::element doc = parser.parse(json); // Throws an exception if there was an error!
|
||||
```
|
||||
|
||||
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
|
||||
|
@ -219,7 +217,7 @@ auto ndjson = R"(
|
|||
{ "foo": 3 }
|
||||
)"_padded;
|
||||
document::parser parser;
|
||||
for (document &doc : parser.load_many(filename)) {
|
||||
for (document::element doc : parser.load_many(filename)) {
|
||||
cout << doc["foo"] << endl;
|
||||
}
|
||||
// Prints 1 2 3
|
||||
|
|
|
@ -21,7 +21,7 @@ buffers hot in cache and keeping memory allocation and initialization to a minim
|
|||
document::parser parser;
|
||||
|
||||
// This initializes buffers and a document big enough to handle this JSON.
|
||||
document &doc = parser.parse("[ true, false ]"_padded);
|
||||
document::element doc = parser.parse("[ true, false ]"_padded);
|
||||
cout << doc << endl;
|
||||
|
||||
// This reuses the existing buffers, and reuses and *overwrites* the old document
|
||||
|
@ -29,17 +29,16 @@ doc = parser.parse("[1, 2, 3]"_padded);
|
|||
cout << doc << endl;
|
||||
|
||||
// This also reuses the existing buffers, and reuses and *overwrites* the old document
|
||||
document &doc2 = parser.parse("true"_padded);
|
||||
document::element doc2 = parser.parse("true"_padded);
|
||||
// Even if you keep the old reference around, doc and doc2 refer to the same document.
|
||||
cout << doc << endl;
|
||||
cout << doc2 << endl;
|
||||
```
|
||||
|
||||
It's not just internal buffers though. The simdjson library reuses the document itself. Notice that reference?
|
||||
`document &doc`? That's key. You are only *borrowing* the document from simdjson, which purposely
|
||||
reuses and overwrites it each time you call parse. This prevent wasteful and unnecessary memory
|
||||
allocation in 99% of cases where JSON is just read, used, and converted to native values
|
||||
or thrown away.
|
||||
It's not just internal buffers though. The simdjson library reuses the document itself. document::element, document::object and document::array are *references* to the internal document.
|
||||
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
|
||||
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
|
||||
JSON is just read, used, and converted to native values or thrown away.
|
||||
|
||||
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
|
||||
|
||||
|
@ -47,38 +46,6 @@ This is key: don't keep the `document&`, `document::element`, `document::array`,
|
|||
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
|
||||
arrays that you own.
|
||||
|
||||
### Keeping documents around for longer
|
||||
|
||||
If you really need to keep parsed JSON documents around for a long time, you can **take** the
|
||||
document by declaring an actual `document` value.
|
||||
|
||||
```c++
|
||||
document::parser parser;
|
||||
|
||||
// This initializes buffers and a document big enough to handle this JSON.
|
||||
// By casting to document instead of document&, it "steals" the document from the parser so that it
|
||||
// cannot be overwritten.
|
||||
document keep_doc = parser.parse("[ true, false ]"_padded);
|
||||
|
||||
// This reuses the existing buffers, but initializes a new document.
|
||||
document &doc = parser.parse("[1, 2, 3]"_padded);
|
||||
|
||||
// Now keep_doc and doc refer to different documents.
|
||||
cout << keep_doc << endl;
|
||||
cout << doc << endl;
|
||||
```
|
||||
|
||||
If you're using error codes, it can be done like this:
|
||||
|
||||
```c++
|
||||
auto [doc_ref, error] = parser.parse(json); // doc_ref is a document&
|
||||
if (error) { cerr << error << endl; exit(1); }
|
||||
document keep_doc = doc_ref; // "steal" the document from the parser
|
||||
```
|
||||
|
||||
This won't allocate anything or copy document memory: instead, it will *steal* the document memory
|
||||
from the parser. The parser will simply allocate new document memory the next time you call parse.
|
||||
|
||||
Server Loops: Long-Running Processes and Memory Capacity
|
||||
--------------------------------------------------------
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
class parser;
|
||||
class stream;
|
||||
|
||||
class doc_result;
|
||||
class element_result;
|
||||
class element_result;
|
||||
class array_result;
|
||||
class object_result;
|
||||
|
@ -69,35 +69,6 @@ public:
|
|||
* Get the root element of this document as a JSON array.
|
||||
*/
|
||||
element root() const noexcept;
|
||||
/**
|
||||
* Get the root element of this document as a JSON array.
|
||||
*/
|
||||
array_result as_array() const noexcept;
|
||||
/**
|
||||
* Get the root element of this document as a JSON object.
|
||||
*/
|
||||
object_result as_object() const noexcept;
|
||||
/**
|
||||
* Get the root element of this document.
|
||||
*/
|
||||
operator element() const noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
/**
|
||||
* Read the root element of this document as a JSON array.
|
||||
*
|
||||
* @return The JSON array.
|
||||
* @exception simdjson_error(UNEXPECTED_TYPE) if the JSON element is not an array
|
||||
*/
|
||||
operator array() const noexcept(false);
|
||||
/**
|
||||
* Read this element as a JSON object (key/value pairs).
|
||||
*
|
||||
* @return The JSON object.
|
||||
* @exception simdjson_error(UNEXPECTED_TYPE) if the JSON element is not an object
|
||||
*/
|
||||
operator object() const noexcept(false);
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
/**
|
||||
* Dump the raw tape for debugging.
|
||||
|
@ -107,93 +78,6 @@ public:
|
|||
*/
|
||||
bool dump_raw_tape(std::ostream &os) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result operator[](std::string_view json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result operator[](const char *json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc.at("/foo/a/1") == 20
|
||||
* doc.at("/")["foo"]["a"].at(1) == 20
|
||||
* doc.at("")["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result at(std::string_view json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value at the given index.
|
||||
*
|
||||
* @return The value at the given index, or:
|
||||
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
|
||||
*/
|
||||
inline element_result at(size_t index) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given key.
|
||||
*
|
||||
* The key will be matched against **unescaped** JSON:
|
||||
*
|
||||
* document::parser parser;
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
|
||||
*
|
||||
* @return The value associated with this field, or:
|
||||
* - NO_SUCH_FIELD if the field does not exist in the object
|
||||
*/
|
||||
inline element_result at_key(std::string_view s) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given key.
|
||||
*
|
||||
* Note: The key will be matched against **unescaped** JSON:
|
||||
*
|
||||
* document::parser parser;
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
|
||||
*
|
||||
* @return The value associated with this field, or:
|
||||
* - NO_SUCH_FIELD if the field does not exist in the object
|
||||
*/
|
||||
inline element_result at_key(const char *s) const noexcept;
|
||||
|
||||
std::unique_ptr<uint64_t[]> tape;
|
||||
std::unique_ptr<uint8_t[]> string_buf;// should be at least byte_capacity
|
||||
|
||||
|
@ -206,154 +90,6 @@ private:
|
|||
template<typename T>
|
||||
class minify;
|
||||
|
||||
/**
|
||||
* A parsed document reference, or an error if the parse failed.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(json);
|
||||
*
|
||||
* ## Document Ownership
|
||||
*
|
||||
* The `document &` refers to an internal document the parser reuses on each `parse()` call. It will
|
||||
* become invalidated on the next `parse()`.
|
||||
*
|
||||
* This is more efficient for common cases where documents are parsed and used one at a time. If you
|
||||
* need to keep the document around longer, you may *take* it from the parser by casting it:
|
||||
*
|
||||
* document &doc = parser.parse(); // take ownership
|
||||
*
|
||||
* If you do this, the parser will automatically allocate a new document on the next `parse()` call.
|
||||
*
|
||||
* ## Error Codes vs. Exceptions
|
||||
*
|
||||
* This result type allows the user to pick whether to use exceptions or not.
|
||||
*
|
||||
* Use like this to avoid exceptions:
|
||||
*
|
||||
* auto [doc, error] = parser.parse(json);
|
||||
* if (error) { exit(1); }
|
||||
*
|
||||
* Use like this if you'd prefer to use exceptions:
|
||||
*
|
||||
* document &doc = parser.parse(json);
|
||||
*
|
||||
*/
|
||||
class document::doc_result : public simdjson_result<document&> {
|
||||
public:
|
||||
/**
|
||||
* Read this document as a JSON objec.
|
||||
*
|
||||
* @return The object value, or:
|
||||
* - UNEXPECTED_TYPE if the JSON document is not an object
|
||||
*/
|
||||
inline object_result as_object() const noexcept;
|
||||
|
||||
/**
|
||||
* Read this document as a JSON array.
|
||||
*
|
||||
* @return The array value, or:
|
||||
* - UNEXPECTED_TYPE if the JSON document is not an array
|
||||
*/
|
||||
inline array_result as_array() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the root element of this document.
|
||||
*/
|
||||
inline element_result root() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result operator[](std::string_view json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result operator[](const char *json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc.at("/foo/a/1") == 20
|
||||
* doc.at("/")["foo"]["a"].at(1) == 20
|
||||
* doc.at("")["foo"]["a"].at(1) == 20
|
||||
*
|
||||
* @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 element_result at(std::string_view json_pointer) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value at the given index.
|
||||
*
|
||||
* @return The value at the given index, or:
|
||||
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
|
||||
*/
|
||||
inline element_result at(size_t index) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given key.
|
||||
*
|
||||
* The key will be matched against **unescaped** JSON:
|
||||
*
|
||||
* document::parser parser;
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
|
||||
*
|
||||
* @return The value associated with this field, or:
|
||||
* - NO_SUCH_FIELD if the field does not exist in the object
|
||||
*/
|
||||
inline element_result at_key(std::string_view s) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given key.
|
||||
*
|
||||
* Note: The key will be matched against **unescaped** JSON:
|
||||
*
|
||||
* document::parser parser;
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
|
||||
* parser.parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
|
||||
*
|
||||
* @return The value associated with this field, or:
|
||||
* - NO_SUCH_FIELD if the field does not exist in the object
|
||||
*/
|
||||
inline element_result at_key(const char *s) const noexcept;
|
||||
|
||||
~doc_result()=default;
|
||||
doc_result(document &doc, error_code error) noexcept;
|
||||
friend class document::parser;
|
||||
friend class document::stream;
|
||||
}; // class document::doc_result
|
||||
|
||||
namespace internal {
|
||||
/**
|
||||
* The possible types in the tape. Internal only.
|
||||
|
@ -579,7 +315,7 @@ public:
|
|||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* document::element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
|
@ -596,7 +332,7 @@ public:
|
|||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* document::element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc["/foo/a/1"] == 20
|
||||
* doc["/"]["foo"]["a"].at(1) == 20
|
||||
* doc[""]["foo"]["a"].at(1) == 20
|
||||
|
@ -613,7 +349,7 @@ public:
|
|||
* Get the value associated with the given JSON pointer.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* document::element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})");
|
||||
* doc.at("/foo/a/1") == 20
|
||||
* doc.at("/")["foo"]["a"].at(1) == 20
|
||||
* doc.at("")["foo"]["a"].at(1) == 20
|
||||
|
@ -662,6 +398,9 @@ public:
|
|||
*/
|
||||
inline element_result at_key(const char *s) const noexcept;
|
||||
|
||||
/** @private for debugging. Prints out the root element. */
|
||||
inline bool dump_raw_tape(std::ostream &out) const noexcept;
|
||||
|
||||
private:
|
||||
really_inline element(const document *_doc, size_t _json_index) noexcept;
|
||||
friend class document;
|
||||
|
@ -947,8 +686,7 @@ private:
|
|||
friend class document::object;
|
||||
};
|
||||
|
||||
|
||||
/** The result of a JSON navigation that may fail. */
|
||||
/** The result of a JSON navigation that may fail. */
|
||||
class document::element_result : public simdjson_result<document::element> {
|
||||
public:
|
||||
really_inline element_result() noexcept;
|
||||
|
@ -1070,7 +808,7 @@ public:
|
|||
* Load a JSON document from a file and return a reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.load("jsonexamples/twitter.json");
|
||||
* const document::element doc = parser.load("jsonexamples/twitter.json");
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
@ -1090,13 +828,13 @@ public:
|
|||
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
|
||||
* - other json errors if parsing fails.
|
||||
*/
|
||||
inline doc_result load(const std::string& path) noexcept;
|
||||
inline element_result load(const std::string& path) noexcept;
|
||||
|
||||
/**
|
||||
* Load a file containing many JSON documents.
|
||||
*
|
||||
* document::parser parser;
|
||||
* for (const document &doc : parser.parse_many(path)) {
|
||||
* for (const document::element doc : parser.parse_many(path)) {
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
|
@ -1153,7 +891,7 @@ public:
|
|||
* Parse a JSON document and return a temporary reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.parse(buf, len);
|
||||
* document::element doc = parser.parse(buf, len);
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
@ -1184,13 +922,13 @@ public:
|
|||
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
|
||||
* - other json errors if parsing fails.
|
||||
*/
|
||||
inline doc_result parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept;
|
||||
inline element_result parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept;
|
||||
|
||||
/**
|
||||
* Parse a JSON document and return a temporary reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.parse(buf, len);
|
||||
* const document::element doc = parser.parse(buf, len);
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
@ -1221,13 +959,13 @@ public:
|
|||
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
|
||||
* - other json errors if parsing fails.
|
||||
*/
|
||||
really_inline doc_result parse(const char *buf, size_t len, bool realloc_if_needed = true) noexcept;
|
||||
really_inline element_result parse(const char *buf, size_t len, bool realloc_if_needed = true) noexcept;
|
||||
|
||||
/**
|
||||
* Parse a JSON document and return a temporary reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.parse(s);
|
||||
* const document::element doc = parser.parse(s);
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
@ -1256,13 +994,13 @@ public:
|
|||
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
|
||||
* - other json errors if parsing fails.
|
||||
*/
|
||||
really_inline doc_result parse(const std::string &s) noexcept;
|
||||
really_inline element_result parse(const std::string &s) noexcept;
|
||||
|
||||
/**
|
||||
* Parse a JSON document and return a temporary reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.parse(s);
|
||||
* const document::element doc = parser.parse(s);
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
@ -1281,16 +1019,16 @@ public:
|
|||
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
|
||||
* - other json errors if parsing fails.
|
||||
*/
|
||||
really_inline doc_result parse(const padded_string &s) noexcept;
|
||||
really_inline element_result parse(const padded_string &s) noexcept;
|
||||
|
||||
// We do not want to allow implicit conversion from C string to std::string.
|
||||
really_inline doc_result parse(const char *buf) noexcept = delete;
|
||||
really_inline element_result parse(const char *buf) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Parse a buffer containing many JSON documents.
|
||||
*
|
||||
* document::parser parser;
|
||||
* for (const document &doc : parser.parse_many(buf, len)) {
|
||||
* for (const document::element doc : parser.parse_many(buf, len)) {
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
|
@ -1352,7 +1090,7 @@ public:
|
|||
* Parse a buffer containing many JSON documents.
|
||||
*
|
||||
* document::parser parser;
|
||||
* for (const document &doc : parser.parse_many(buf, len)) {
|
||||
* for (const document::element doc : parser.parse_many(buf, len)) {
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
|
@ -1414,7 +1152,7 @@ public:
|
|||
* Parse a buffer containing many JSON documents.
|
||||
*
|
||||
* document::parser parser;
|
||||
* for (const document &doc : parser.parse_many(buf, len)) {
|
||||
* for (const document::element doc : parser.parse_many(buf, len)) {
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
|
@ -1475,7 +1213,7 @@ public:
|
|||
* Parse a buffer containing many JSON documents.
|
||||
*
|
||||
* document::parser parser;
|
||||
* for (const document &doc : parser.parse_many(buf, len)) {
|
||||
* for (const document::element doc : parser.parse_many(buf, len)) {
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
|
@ -1528,7 +1266,7 @@ public:
|
|||
inline stream parse_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
|
||||
// We do not want to allow implicit conversion from C string to std::string.
|
||||
really_inline doc_result parse_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
really_inline element_result parse_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
|
||||
/**
|
||||
* The largest document this parser can automatically support.
|
||||
|
@ -1730,11 +1468,6 @@ private:
|
|||
//
|
||||
inline simdjson_result<size_t> read_file(const std::string &path) noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
// Used internally to get the document
|
||||
inline const document &get_document() const noexcept(false);
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
friend class document::parser::Iterator;
|
||||
friend class document::stream;
|
||||
}; // class parser
|
||||
|
@ -1743,7 +1476,7 @@ private:
|
|||
* Minifies a JSON element or document, printing the smallest possible valid JSON.
|
||||
*
|
||||
* document::parser parser;
|
||||
* document &doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
* document::element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
* cout << minify(doc) << endl; // prints [1,2,3]
|
||||
*
|
||||
*/
|
||||
|
@ -1780,16 +1513,6 @@ private:
|
|||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& out, minify<T> formatter) { return formatter.print(out); }
|
||||
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* By default, the document will be printed minified.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The document to print.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, const document &value) { return out << minify(value); }
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
|
@ -1833,18 +1556,6 @@ inline std::ostream& operator<<(std::ostream& out, const document::key_value_pai
|
|||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* By default, the value will be printed minified.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The value to print.
|
||||
* @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, const document::doc_result &value) noexcept(false) { return out << minify(value); }
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
/**
|
||||
* Get the current document (or error).
|
||||
*/
|
||||
really_inline doc_result operator*() noexcept;
|
||||
really_inline element_result operator*() noexcept;
|
||||
/**
|
||||
* Advance to the next document.
|
||||
*/
|
||||
|
|
|
@ -191,53 +191,11 @@ inline document::object::iterator document::object_result::end() const noexcept(
|
|||
inline document::element document::root() const noexcept {
|
||||
return element(this, 1);
|
||||
}
|
||||
inline document::array_result document::as_array() const noexcept {
|
||||
return root().as_array();
|
||||
}
|
||||
inline document::object_result document::as_object() const noexcept {
|
||||
return root().as_object();
|
||||
}
|
||||
inline document::operator element() const noexcept {
|
||||
return root();
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
inline document::operator document::array() const noexcept(false) {
|
||||
return root();
|
||||
}
|
||||
inline document::operator document::object() const noexcept(false) {
|
||||
return root();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#define REPORT_ERROR(CODE, MESSAGE) ((std::cerr << MESSAGE << std::endl), CODE)
|
||||
#define REPORT_ERROR(CODE, MESSAGE) (CODE)
|
||||
#define RETURN_ERROR(CODE, MESSAGE) return REPORT_ERROR((CODE), (MESSAGE));
|
||||
|
||||
inline document::element_result document::at(std::string_view json_pointer) const noexcept {
|
||||
if (json_pointer == "") { return root(); }
|
||||
// NOTE: JSON pointer requires a / at the beginning of the document; we allow it to be optional.
|
||||
return root().at(json_pointer.substr(json_pointer[0] == '/' ? 1 : 0));
|
||||
}
|
||||
inline document::element_result document::at(size_t index) const noexcept {
|
||||
return as_array().at(index);
|
||||
}
|
||||
inline document::element_result document::at_key(std::string_view key) const noexcept {
|
||||
return as_object().at_key(key);
|
||||
}
|
||||
inline document::element_result document::at_key(const char *key) const noexcept {
|
||||
return as_object().at_key(key);
|
||||
}
|
||||
inline document::element_result document::operator[](std::string_view json_pointer) const noexcept {
|
||||
return at(json_pointer);
|
||||
}
|
||||
inline document::element_result document::operator[](const char *json_pointer) const noexcept {
|
||||
return (*this)[std::string_view(json_pointer)];
|
||||
}
|
||||
|
||||
|
||||
WARN_UNUSED
|
||||
inline error_code document::set_capacity(size_t capacity) noexcept {
|
||||
if (capacity == 0) {
|
||||
|
@ -353,76 +311,23 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
|
|||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// doc_result inline implementation
|
||||
//
|
||||
inline document::doc_result::doc_result(document &doc, error_code error) noexcept : simdjson_result<document&>(doc, error) { }
|
||||
|
||||
inline document::array_result document::doc_result::as_array() const noexcept {
|
||||
return root().as_array();
|
||||
}
|
||||
inline document::object_result document::doc_result::as_object() const noexcept {
|
||||
return root().as_object();
|
||||
}
|
||||
inline document::element_result document::doc_result::root() const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.root();
|
||||
}
|
||||
|
||||
inline document::element_result document::doc_result::operator[](std::string_view key) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first[key];
|
||||
}
|
||||
inline document::element_result document::doc_result::operator[](const char *json_pointer) const noexcept {
|
||||
return (*this)[std::string_view(json_pointer)];
|
||||
}
|
||||
inline document::element_result document::doc_result::at(std::string_view key) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at(key);
|
||||
}
|
||||
inline document::element_result document::doc_result::at(size_t index) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at(index);
|
||||
}
|
||||
inline document::element_result document::doc_result::at_key(std::string_view key) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_key(key);
|
||||
}
|
||||
inline document::element_result document::doc_result::at_key(const char *key) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_key(key);
|
||||
}
|
||||
|
||||
//
|
||||
// document::parser inline implementation
|
||||
//
|
||||
really_inline document::parser::parser(size_t max_capacity, size_t max_depth) noexcept
|
||||
: _max_capacity{max_capacity}, _max_depth{max_depth}, loaded_bytes(nullptr, &aligned_free_char) {
|
||||
|
||||
}
|
||||
: _max_capacity{max_capacity}, _max_depth{max_depth}, loaded_bytes(nullptr, &aligned_free_char) {}
|
||||
inline bool document::parser::is_valid() const noexcept { return valid; }
|
||||
inline int document::parser::get_error_code() const noexcept { return error; }
|
||||
inline std::string document::parser::get_error_message() const noexcept { return error_message(int(error)); }
|
||||
inline bool document::parser::print_json(std::ostream &os) const noexcept {
|
||||
if (!is_valid()) { return false; }
|
||||
os << minify(doc);
|
||||
os << doc.root();
|
||||
return true;
|
||||
}
|
||||
inline bool document::parser::dump_raw_tape(std::ostream &os) const noexcept {
|
||||
return is_valid() ? doc.dump_raw_tape(os) : false;
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
inline const document &document::parser::get_document() const noexcept(false) {
|
||||
if (!is_valid()) {
|
||||
throw simdjson_error(error);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
inline simdjson_result<size_t> document::parser::read_file(const std::string &path) noexcept {
|
||||
// Open the file
|
||||
std::FILE *fp = std::fopen(path.c_str(), "rb");
|
||||
|
@ -461,9 +366,9 @@ inline simdjson_result<size_t> document::parser::read_file(const std::string &pa
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
inline document::doc_result document::parser::load(const std::string &path) noexcept {
|
||||
inline document::element_result document::parser::load(const std::string &path) noexcept {
|
||||
auto [len, code] = read_file(path);
|
||||
if (code) { return doc_result(doc, code); }
|
||||
if (code) { return code; }
|
||||
|
||||
return parse(loaded_bytes.get(), len, false);
|
||||
}
|
||||
|
@ -473,35 +378,36 @@ inline document::stream document::parser::load_many(const std::string &path, siz
|
|||
return stream(*this, (const uint8_t*)loaded_bytes.get(), len, batch_size, code);
|
||||
}
|
||||
|
||||
inline document::doc_result document::parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept {
|
||||
inline document::element_result document::parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept {
|
||||
error_code code = ensure_capacity(len);
|
||||
if (code) { return doc_result(doc, code); }
|
||||
if (code) { return code; }
|
||||
|
||||
if (realloc_if_needed) {
|
||||
const uint8_t *tmp_buf = buf;
|
||||
buf = (uint8_t *)internal::allocate_padded_buffer(len);
|
||||
if (buf == nullptr)
|
||||
return doc_result(doc, MEMALLOC);
|
||||
return MEMALLOC;
|
||||
memcpy((void *)buf, tmp_buf, len);
|
||||
}
|
||||
|
||||
code = simdjson::active_implementation->parse(buf, len, *this);
|
||||
|
||||
// We're indicating validity via the doc_result, so set the parse state back to invalid
|
||||
valid = false;
|
||||
error = UNINITIALIZED;
|
||||
if (realloc_if_needed) {
|
||||
aligned_free((void *)buf); // must free before we exit
|
||||
}
|
||||
return doc_result(doc, code);
|
||||
if (code) { return code; }
|
||||
|
||||
// We're indicating validity via the element_result, so set the parse state back to invalid
|
||||
valid = false;
|
||||
error = UNINITIALIZED;
|
||||
return doc.root();
|
||||
}
|
||||
really_inline document::doc_result document::parser::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept {
|
||||
really_inline document::element_result document::parser::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept {
|
||||
return parse((const uint8_t *)buf, len, realloc_if_needed);
|
||||
}
|
||||
really_inline document::doc_result document::parser::parse(const std::string &s) noexcept {
|
||||
really_inline document::element_result document::parser::parse(const std::string &s) noexcept {
|
||||
return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
|
||||
}
|
||||
really_inline document::doc_result document::parser::parse(const padded_string &s) noexcept {
|
||||
really_inline document::element_result document::parser::parse(const padded_string &s) noexcept {
|
||||
return parse(s.data(), s.length(), false);
|
||||
}
|
||||
|
||||
|
@ -1027,14 +933,14 @@ inline document::element_result document::element::at_key(const char *key) const
|
|||
return as_object().at_key(key);
|
||||
}
|
||||
|
||||
inline bool document::element::dump_raw_tape(std::ostream &out) const noexcept {
|
||||
return doc->dump_raw_tape(out);
|
||||
}
|
||||
|
||||
//
|
||||
// minify inline implementation
|
||||
//
|
||||
|
||||
template<>
|
||||
inline std::ostream& minify<document>::print(std::ostream& out) {
|
||||
return out << minify<document::element>(value.root());
|
||||
}
|
||||
template<>
|
||||
inline std::ostream& minify<document::element>::print(std::ostream& out) {
|
||||
using tape_type=internal::tape_type;
|
||||
|
@ -1191,11 +1097,6 @@ inline std::ostream& minify<document::key_value_pair>::print(std::ostream& out)
|
|||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
template<>
|
||||
inline std::ostream& minify<document::doc_result>::print(std::ostream& out) {
|
||||
if (value.error()) { throw simdjson_error(value.error()); }
|
||||
return out << minify<document>(value.first);
|
||||
}
|
||||
template<>
|
||||
inline std::ostream& minify<document::element_result>::print(std::ostream& out) {
|
||||
if (value.error()) { throw simdjson_error(value.error()); }
|
||||
|
|
|
@ -127,8 +127,10 @@ really_inline document::stream::iterator::iterator(stream& stream, bool _is_end)
|
|||
: _stream{stream}, finished{_is_end} {
|
||||
}
|
||||
|
||||
really_inline document::doc_result document::stream::iterator::operator*() noexcept {
|
||||
return doc_result(_stream.parser.doc, _stream.error == SUCCESS_AND_HAS_MORE ? SUCCESS : _stream.error);
|
||||
really_inline document::element_result document::stream::iterator::operator*() noexcept {
|
||||
error_code error = _stream.error == SUCCESS_AND_HAS_MORE ? SUCCESS : _stream.error;
|
||||
if (error) { return error; }
|
||||
return _stream.parser.doc.root();
|
||||
}
|
||||
|
||||
really_inline document::stream::iterator& document::stream::iterator::operator++() noexcept {
|
||||
|
|
|
@ -1526,7 +1526,7 @@ public:
|
|||
* Load a JSON document from a file and return a reference to it.
|
||||
*
|
||||
* document::parser parser;
|
||||
* const document &doc = parser.load("jsonexamples/twitter.json");
|
||||
* const document::element doc = parser.load("jsonexamples/twitter.json");
|
||||
*
|
||||
* ### IMPORTANT: Document Lifetime
|
||||
*
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace number_tests {
|
|||
buf[n] = '\0';
|
||||
fflush(NULL);
|
||||
|
||||
auto [actual, error] = parser.parse(buf, n).root().as_int64_t();
|
||||
auto [actual, error] = parser.parse(buf, n).as_int64_t();
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
if (actual != i) {
|
||||
std::cerr << "JSON '" << buf << " parsed to " << actual << " instead of " << i << std::endl;
|
||||
|
@ -57,11 +57,9 @@ namespace number_tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
printf("Small integers can be parsed.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool powers_of_two() {
|
||||
std::cout << __func__ << std::endl;
|
||||
char buf[1024];
|
||||
|
@ -72,8 +70,7 @@ namespace number_tests {
|
|||
auto n = sprintf(buf, "%.*e", std::numeric_limits<double>::max_digits10 - 1, expected);
|
||||
buf[n] = '\0';
|
||||
fflush(NULL);
|
||||
|
||||
auto [actual, error] = parser.parse(buf, n).root().as_double();
|
||||
auto [actual, error] = parser.parse(buf, n).as_double();
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
int ulp = f64_ulp_dist(actual,expected);
|
||||
if(ulp > maxulp) maxulp = ulp;
|
||||
|
@ -82,7 +79,6 @@ namespace number_tests {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
printf("Powers of 2 can be parsed, maxulp = %d.\n", maxulp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -168,7 +164,7 @@ namespace number_tests {
|
|||
buf[n] = '\0';
|
||||
fflush(NULL);
|
||||
|
||||
auto [actual, error] = parser.parse(buf, n).root().as_double();
|
||||
auto [actual, error] = parser.parse(buf, n).as_double();
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
double expected = ((i >= -307) ? testing_power_of_ten[i + 307]: std::pow(10, i));
|
||||
int ulp = (int) f64_ulp_dist(actual, expected);
|
||||
|
@ -462,7 +458,7 @@ namespace parse_api_tests {
|
|||
document::parser parser;
|
||||
auto [doc, error] = parser.parse(BASIC_JSON);
|
||||
if (error) { cerr << error << endl; return false; }
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
return true;
|
||||
}
|
||||
bool parser_parse_many() {
|
||||
|
@ -471,7 +467,7 @@ namespace parse_api_tests {
|
|||
int count = 0;
|
||||
for (auto [doc, error] : parser.parse_many(BASIC_NDJSON)) {
|
||||
if (error) { cerr << error << endl; return false; }
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
count++;
|
||||
}
|
||||
if (count != 2) { cerr << "parse_many returned " << count << " documents, expected 2" << endl; return false; }
|
||||
|
@ -494,7 +490,7 @@ namespace parse_api_tests {
|
|||
document::parser parser;
|
||||
auto [doc, error] = parser.load(JSON_TEST_PATH);
|
||||
if (error) { cerr << error << endl; return false; }
|
||||
if (!doc.root().is_object()) { cerr << "Document did not parse as an object" << endl; return false; }
|
||||
if (!doc.is_object()) { cerr << "Document did not parse as an object" << endl; return false; }
|
||||
return true;
|
||||
}
|
||||
bool parser_load_many() {
|
||||
|
@ -503,7 +499,7 @@ namespace parse_api_tests {
|
|||
int count = 0;
|
||||
for (auto [doc, error] : parser.load_many(NDJSON_TEST_PATH)) {
|
||||
if (error) { cerr << error << endl; return false; }
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
count++;
|
||||
}
|
||||
if (count != 793) { cerr << "Expected 793 documents, but load_many loaded " << count << " documents." << endl; return false; }
|
||||
|
@ -515,16 +511,16 @@ namespace parse_api_tests {
|
|||
bool parser_parse_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
const document& doc = parser.parse(BASIC_JSON);
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
document::element doc = parser.parse(BASIC_JSON);
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
return true;
|
||||
}
|
||||
bool parser_parse_many_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
int count = 0;
|
||||
for (const document &doc : parser.parse_many(BASIC_NDJSON)) {
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
for (const document::element doc : parser.parse_many(BASIC_NDJSON)) {
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
count++;
|
||||
}
|
||||
if (count != 2) { cerr << "parse_many returned " << count << " documents, expected 2" << endl; return false; }
|
||||
|
@ -534,16 +530,16 @@ namespace parse_api_tests {
|
|||
bool parser_load_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
const document &doc = parser.load(JSON_TEST_PATH);
|
||||
if (!doc.root().is_object()) { cerr << "Document did not parse as an object" << endl; return false; }
|
||||
const document::element doc = parser.load(JSON_TEST_PATH);
|
||||
if (!doc.is_object()) { cerr << "Document did not parse as an object" << endl; return false; }
|
||||
return true;
|
||||
}
|
||||
bool parser_load_many_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
int count = 0;
|
||||
for (const document &doc : parser.load_many(NDJSON_TEST_PATH)) {
|
||||
if (!doc.root().is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
for (const document::element doc : parser.load_many(NDJSON_TEST_PATH)) {
|
||||
if (!doc.is_array()) { cerr << "Document did not parse as an array" << endl; return false; }
|
||||
count++;
|
||||
}
|
||||
if (count != 793) { cerr << "Expected 1 document, but load_many loaded " << count << " documents." << endl; return false; }
|
||||
|
@ -927,7 +923,7 @@ namespace dom_api_tests {
|
|||
int i = 0;
|
||||
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(json);
|
||||
document::element doc = parser.parse(json);
|
||||
for (auto [key, value] : doc.as_object()) {
|
||||
if (key != expected_key[i] || uint64_t(value) != expected_value[i]) { cerr << "Expected " << expected_key[i] << " = " << expected_value[i] << ", got " << key << "=" << uint64_t(value) << endl; return false; }
|
||||
i++;
|
||||
|
@ -943,7 +939,7 @@ namespace dom_api_tests {
|
|||
int i=0;
|
||||
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(json);
|
||||
document::element doc = parser.parse(json);
|
||||
for (uint64_t value : doc.as_array()) {
|
||||
if (value != expected_value[i]) { cerr << "Expected " << expected_value[i] << ", got " << value << endl; return false; }
|
||||
i++;
|
||||
|
@ -1017,7 +1013,7 @@ namespace dom_api_tests {
|
|||
std::cout << "Running " << __func__ << std::endl;
|
||||
string json(R"({ "a": 1, "b": 2, "c": 3})");
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(json);
|
||||
document::element doc = parser.parse(json);
|
||||
if (uint64_t(doc["a"]) != 1) { cerr << "Expected uint64_t(doc[\"a\"]) to be 1, was " << uint64_t(doc["a"]) << endl; return false; }
|
||||
return true;
|
||||
}
|
||||
|
@ -1035,7 +1031,7 @@ namespace dom_api_tests {
|
|||
std::cout << "Running " << __func__ << std::endl;
|
||||
// Prints the number of results in twitter.json
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
uint64_t result_count = doc["search_metadata"]["count"];
|
||||
if (result_count != 100) { cerr << "Expected twitter.json[metadata_count][count] = 100, got " << result_count << endl; return false; }
|
||||
return true;
|
||||
|
@ -1046,7 +1042,7 @@ namespace dom_api_tests {
|
|||
// Print users with a default profile.
|
||||
set<string_view> default_users;
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (document::object tweet : doc["statuses"].as_array()) {
|
||||
document::object user = tweet["user"];
|
||||
if (user["default_profile"]) {
|
||||
|
@ -1062,7 +1058,7 @@ namespace dom_api_tests {
|
|||
// Print image names and sizes
|
||||
set<pair<uint64_t, uint64_t>> image_sizes;
|
||||
document::parser parser;
|
||||
document &doc = parser.load(JSON_TEST_PATH);
|
||||
document::element doc = parser.load(JSON_TEST_PATH);
|
||||
for (document::object tweet : doc["statuses"].as_array()) {
|
||||
auto [media, not_found] = tweet["entities"]["media"];
|
||||
if (!not_found) {
|
||||
|
@ -1217,7 +1213,7 @@ namespace format_tests {
|
|||
bool print_element_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << doc["foo"];
|
||||
return assert_minified(s, "1");
|
||||
|
@ -1225,7 +1221,7 @@ namespace format_tests {
|
|||
bool print_minify_element_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << minify(doc["foo"]);
|
||||
return assert_minified(s, "1");
|
||||
|
@ -1234,7 +1230,7 @@ namespace format_tests {
|
|||
bool print_element_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::element value = doc["foo"];
|
||||
ostringstream s;
|
||||
s << value;
|
||||
|
@ -1243,7 +1239,7 @@ namespace format_tests {
|
|||
bool print_minify_element_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::element value = doc["foo"];
|
||||
ostringstream s;
|
||||
s << minify(value);
|
||||
|
@ -1253,7 +1249,7 @@ namespace format_tests {
|
|||
bool print_array_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << doc["bar"].as_array();
|
||||
return assert_minified(s, "[1,2,3]");
|
||||
|
@ -1261,7 +1257,7 @@ namespace format_tests {
|
|||
bool print_minify_array_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << minify(doc["bar"].as_array());
|
||||
return assert_minified(s, "[1,2,3]");
|
||||
|
@ -1270,7 +1266,7 @@ namespace format_tests {
|
|||
bool print_object_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << doc["baz"].as_object();
|
||||
return assert_minified(s, R"({"a":1,"b":2,"c":3})");
|
||||
|
@ -1278,7 +1274,7 @@ namespace format_tests {
|
|||
bool print_minify_object_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
ostringstream s;
|
||||
s << minify(doc["baz"].as_object());
|
||||
return assert_minified(s, R"({"a":1,"b":2,"c":3})");
|
||||
|
@ -1287,7 +1283,7 @@ namespace format_tests {
|
|||
bool print_array_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::array value = doc["bar"];
|
||||
ostringstream s;
|
||||
s << value;
|
||||
|
@ -1296,7 +1292,7 @@ namespace format_tests {
|
|||
bool print_minify_array_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::array value = doc["bar"];
|
||||
ostringstream s;
|
||||
s << minify(value);
|
||||
|
@ -1306,7 +1302,7 @@ namespace format_tests {
|
|||
bool print_object_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::object value = doc["baz"];
|
||||
ostringstream s;
|
||||
s << value;
|
||||
|
@ -1315,7 +1311,7 @@ namespace format_tests {
|
|||
bool print_minify_object_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
document::parser parser;
|
||||
document &doc = parser.parse(DOCUMENT);
|
||||
document::element doc = parser.parse(DOCUMENT);
|
||||
document::object value = doc["baz"];
|
||||
ostringstream s;
|
||||
s << minify(value);
|
||||
|
|
|
@ -74,8 +74,8 @@ namespace parser_load {
|
|||
bool parser_load_many_chain() {
|
||||
TEST_START();
|
||||
document::parser parser;
|
||||
for (auto doc_result : parser.load_many(NONEXISTENT_FILE)) {
|
||||
auto [val, error] = doc_result["foo"].as_uint64_t();
|
||||
for (auto doc : parser.load_many(NONEXISTENT_FILE)) {
|
||||
auto [val, error] = doc["foo"].as_uint64_t();
|
||||
ASSERT_ERROR(error, IO_ERROR);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
|
|
@ -60,23 +60,29 @@ bool json_pointer_failure_test(const char *json_pointer, error_code expected_fai
|
|||
|
||||
int main() {
|
||||
if (
|
||||
json_pointer_success_test("/~1~001abc/1/\\\" 0/0", "value0") &&
|
||||
json_pointer_success_test("/~1~001abc/1/\\\" 0/1", "value1") &&
|
||||
json_pointer_failure_test("/~1~001abc/1/\\\" 0/2", INDEX_OUT_OF_BOUNDS) && // index actually out of bounds
|
||||
json_pointer_success_test("/arr") && // get array
|
||||
json_pointer_failure_test("/arr/0", INDEX_OUT_OF_BOUNDS) && // array index 0 out of bounds on empty array
|
||||
json_pointer_success_test("/~1~001abc") && // get object
|
||||
json_pointer_success_test("/0", "0 ok") && // object index with integer-ish key
|
||||
json_pointer_success_test("/01", "01 ok") && // object index with key that would be an invalid integer
|
||||
json_pointer_success_test("/", "empty ok") && // object index with empty key
|
||||
json_pointer_failure_test("/~01abc", NO_SUCH_FIELD) && // Test that we don't try to compare the literal key
|
||||
json_pointer_failure_test("/~1~001abc/01", INVALID_JSON_POINTER) && // Leading 0 in integer index
|
||||
json_pointer_failure_test("/~1~001abc/", INVALID_JSON_POINTER) && // Empty index to array
|
||||
json_pointer_failure_test("/~1~001abc/-", INDEX_OUT_OF_BOUNDS) && // End index is always out of bounds
|
||||
json_pointer_success_test("") &&
|
||||
json_pointer_success_test("~1~001abc") &&
|
||||
json_pointer_success_test("~1~001abc/1") &&
|
||||
json_pointer_success_test("~1~001abc/1/\\\" 0") &&
|
||||
json_pointer_success_test("~1~001abc/1/\\\" 0/0", "value0") &&
|
||||
json_pointer_success_test("~1~001abc/1/\\\" 0/1", "value1") &&
|
||||
json_pointer_failure_test("~1~001abc/1/\\\" 0/2", INDEX_OUT_OF_BOUNDS) && // index actually out of bounds
|
||||
json_pointer_success_test("arr") && // get array
|
||||
json_pointer_failure_test("arr/0", INDEX_OUT_OF_BOUNDS) && // array index 0 out of bounds on empty array
|
||||
json_pointer_success_test("~1~001abc") && // get object
|
||||
json_pointer_success_test("0", "0 ok") && // object index with integer-ish key
|
||||
json_pointer_success_test("01", "01 ok") && // object index with key that would be an invalid integer
|
||||
json_pointer_success_test("", "empty ok") && // object index with empty key
|
||||
json_pointer_failure_test("~01abc", NO_SUCH_FIELD) && // Test that we don't try to compare the literal key
|
||||
json_pointer_failure_test("~1~001abc/01", INVALID_JSON_POINTER) && // Leading 0 in integer index
|
||||
json_pointer_failure_test("~1~001abc/", INVALID_JSON_POINTER) && // Empty index to array
|
||||
json_pointer_failure_test("~1~001abc/-", INDEX_OUT_OF_BOUNDS) && // End index is always out of bounds
|
||||
true
|
||||
) {
|
||||
std::cout << "Success!" << std::endl;
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "Failed!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ void parser_parse_many_exception() {
|
|||
auto json = "[1, 2, 3] true [ true, false ]"_padded;
|
||||
cout << "Parsing " << json.data() << " ..." << endl;
|
||||
document::parser parser;
|
||||
for (const document &doc : parser.parse_many(json)) {
|
||||
for (const document::element doc : parser.parse_many(json)) {
|
||||
cout << doc << endl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
|||
s.structural_indexes_count = parser.n_structural_indexes;
|
||||
|
||||
// simdjson::document::iterator iter(doc);
|
||||
recurse(doc.root(), s, 0);
|
||||
recurse(doc, s, 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue