diff --git a/.vscode/settings.json b/.vscode/settings.json index 839f41151..ed569deec 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -98,6 +98,8 @@ "queue": "cpp", "shared_mutex": "cpp", "ranges": "cpp", - "span": "cpp" + "span": "cpp", + "__verbose_abort": "cpp", + "charconv": "cpp" } } \ No newline at end of file diff --git a/doc/basics.md b/doc/basics.md index 8613e7292..bc3905d2f 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -30,7 +30,7 @@ An overview of what you need to know to use simdjson, with examples. - [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines) - [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings) - [Dynamic Number Types](#dynamic-number-types) - - [Raw Strings](#raw-strings) + - [Raw Strings From Keys](#raw-strings-from-keys) - [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string) - [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance) - [Thread Safety](#thread-safety) @@ -223,7 +223,7 @@ walking through the original JSON text, merrily reading commas and colons and br you get where you are going. This is the key to On Demand's performance: since it's just an iterator, it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want to use. On Demand is also ideally suited when you want to capture part of the document without parsing it -immediately (e.g., see [Raw Strings](#raw-strings)). +immediately (e.g., see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)). We refer to "On Demand" as a front-end component since it is an interface between the low-level parsing functions and the user. It hides much of the complexity of parsing JSON @@ -258,7 +258,7 @@ copy the data into their own favorite class instances (e.g., alternatives to `st A `std::string_view` instance is effectively just a pointer to a region in memory representing a string. In simdjson, we return `std::string_view` instances that either point within the -input string you parsed (when using [raw Strings](#raw-strings)), or to a temporary string buffer inside +input string you parsed (see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)), or to a temporary string buffer inside our parser class instances that is valid until the parser object is destroyed or you use it to parse another document. When using `std::string_view` instances, it is your responsibility to ensure that `std::string_view` instance does not outlive the pointed-to memory (e.g., either the input @@ -557,7 +557,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen - null (`json_type::null`). You must still validate and consume the values (e.g., call `is_null()`) after calling `type()`. - You may also access [raw strings](#raw-strings). + You may also access [the raw JSON string](#general-direct-access-to-the-raw-json-string). For example, the following is a quick and dirty recursive function that verbosely prints the JSON document as JSON. This example also illustrates lifecycle requirements: the `document` instance holds the iterator. The document must remain in scope while you are accessing instances of `value`, `object` and `array`. ```c++ void recursive_print_json(ondemand::value element) { @@ -1936,7 +1936,7 @@ It will output: 9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999 ``` -Raw Strings +Raw Strings From Keys ----------- It is sometimes useful to have access to a raw (unescaped) string: we make available a @@ -1972,10 +1972,25 @@ JSON string to a user-provided buffer: } ``` +Some users might prefer to have a direct access to an `std::string_view` instance +pointing inside the source document. The `key_raw_json_token()` method serves this +purpose. It provides a view on the key, including the starting quote character, +and everything up to the next `:` character after the final quote character. E.g., +if the key is `"name"` then `key_raw_json_token()` returns an `std::string_view` which +begins with `"name"` and may containing trailing white-space characters. +```C++ + auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded; + ondemand::parser parser; + ondemand::document doc = parser.iterate(json); + for (auto key_value : doc.get_object()) { + std::string_view keysv = key_value.key_raw_json_token(); // keysv is "\"name\" " + } +``` + General Direct Access to the Raw JSON String -------------------------------- -If your value is a string, the `raw_json_string` you with `get_raw_json_string()` gives you direct access to the unprocessed +If your value is a string, the `raw_json_string` you get with `get_raw_json_string()` gives you direct access to the unprocessed string. But the simdjson library allows you to have access to the raw underlying JSON more generally, not just for strings. @@ -2427,7 +2442,29 @@ bool example() { } ``` +* Example 4: Passing an array to a function +```C++ + +#include "simdjson.h" +#include + +// prints the content of the array as hexadecimal 64-bit integers +void f(simdjson::ondemand::array v) { + for(uint64_t val : v) { + std::cout << "0x" << std::hex << val << std::endl; + } +} + + +int main(void) { + simdjson::padded_string json = R"( [ 897314173811950000, 3122321 ])"_padded; + simdjson::ondemand::parser parser; + simdjson::ondemand::document doc = parser.iterate(json); + f(doc.get_array()); + return EXIT_SUCCESS; +} +``` Performance Tips -------- diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index d5cd205cf..0c0d23c31 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -269,6 +269,13 @@ simdjson_inline simdjson_result document::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + simdjson_inline bool document::is_negative() noexcept { return get_root_value_iterator().is_root_negative(); } @@ -501,6 +508,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } @@ -669,6 +680,7 @@ simdjson_inline simdjson_result document_reference::find_field_unordered( simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } simdjson_inline simdjson_result 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(); } @@ -809,6 +821,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index 9559ba26b..1aec08c12 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -432,6 +432,14 @@ public: */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + /** * Checks whether the document is a negative number. * @@ -704,6 +712,7 @@ public: simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; @@ -780,6 +789,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline int32_t current_depth() const noexcept; simdjson_inline bool at_end() const noexcept; @@ -850,6 +860,7 @@ public: simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result current_location() noexcept; simdjson_inline simdjson_result current_depth() const noexcept; simdjson_inline simdjson_result is_negative() noexcept; diff --git a/include/simdjson/generic/ondemand/field-inl.h b/include/simdjson/generic/ondemand/field-inl.h index c7d189ce1..061f1860d 100644 --- a/include/simdjson/generic/ondemand/field-inl.h +++ b/include/simdjson/generic/ondemand/field-inl.h @@ -43,6 +43,12 @@ simdjson_inline raw_json_string field::key() const noexcept { return first; } + +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} + simdjson_inline value &field::value() & noexcept { return second; } @@ -76,10 +82,17 @@ simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(allow_replacement); } + simdjson_inline simdjson_result simdjson_result::value() noexcept { if (error()) { return error(); } return std::move(first.value()); diff --git a/include/simdjson/generic/ondemand/field.h b/include/simdjson/generic/ondemand/field.h index 2be04fee8..082f401bb 100644 --- a/include/simdjson/generic/ondemand/field.h +++ b/include/simdjson/generic/ondemand/field.h @@ -42,6 +42,11 @@ public: * an unescaped C string: e.g., key() == "test". */ simdjson_inline raw_json_string key() const noexcept; + /** + * Get the unprocessed key as a string_view. This includes the quotes and may include + * some spaces after the last quote. + */ + simdjson_inline std::string_view key_raw_json_token() const noexcept; /** * Get the field value. */ @@ -74,6 +79,7 @@ public: simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; simdjson_inline simdjson_result value() noexcept; }; diff --git a/include/simdjson/generic/ondemand/json_iterator.h b/include/simdjson/generic/ondemand/json_iterator.h index 68ff4f146..36335dbfd 100644 --- a/include/simdjson/generic/ondemand/json_iterator.h +++ b/include/simdjson/generic/ondemand/json_iterator.h @@ -300,6 +300,7 @@ protected: friend class raw_json_string; friend class parser; friend class value_iterator; + friend class field; template 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 diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h index 17d4884a5..41e02fa24 100644 --- a/include/simdjson/generic/ondemand/value-inl.h +++ b/include/simdjson/generic/ondemand/value-inl.h @@ -185,6 +185,14 @@ simdjson_inline simdjson_result value::is_scalar() noexcept { return ! ((this_type == json_type::array) || (this_type == json_type::object)); } +simdjson_inline simdjson_result value::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + + simdjson_inline bool value::is_negative() noexcept { return iter.is_negative(); } @@ -411,6 +419,10 @@ simdjson_inline simdjson_result simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index ca9e42062..51ceee037 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -401,6 +401,13 @@ public: * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; /** * Checks whether the value is a negative number. @@ -629,6 +636,7 @@ protected: friend class object; friend struct simdjson_result; friend struct simdjson_result; + friend class field; }; } // namespace ondemand @@ -743,6 +751,7 @@ public: */ simdjson_inline simdjson_result type() noexcept; simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; simdjson_inline simdjson_result is_negative() noexcept; simdjson_inline simdjson_result is_integer() noexcept; simdjson_inline simdjson_result get_number_type() noexcept; diff --git a/include/simdjson/generic/ondemand/value_iterator.h b/include/simdjson/generic/ondemand/value_iterator.h index e36cb70d8..a18e17008 100644 --- a/include/simdjson/generic/ondemand/value_iterator.h +++ b/include/simdjson/generic/ondemand/value_iterator.h @@ -469,6 +469,7 @@ protected: friend class object; friend class array; friend class value; + friend class field; }; // value_iterator } // namespace ondemand diff --git a/tests/ondemand/ondemand_readme_examples.cpp b/tests/ondemand/ondemand_readme_examples.cpp index c495a3c6c..9f4f73980 100644 --- a/tests/ondemand/ondemand_readme_examples.cpp +++ b/tests/ondemand/ondemand_readme_examples.cpp @@ -1254,6 +1254,38 @@ bool simple_error_example() { #if SIMDJSON_EXCEPTIONS + +#include "simdjson.h" +#include + + // prints the content of the array as hexadecimal 64-bit integers + void f(simdjson::ondemand::array v) { + for(uint64_t val : v) { + std::cout << "0x" << std::hex << val << std::endl; + } + } + + + int callf(void) { + simdjson::padded_string json = R"( [ 897314173811950000, 3122321 ])"_padded; + simdjson::ondemand::parser parser; + simdjson::ondemand::document doc = parser.iterate(json); + f(doc.get_array()); + return EXIT_SUCCESS; + } + + bool key_raw_json_token() { + TEST_START(); + auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded; + ondemand::parser parser; + ondemand::document doc = parser.iterate(json); + for (auto key_value : doc.get_object()) { + std::string_view keysv = key_value.key_raw_json_token(); + ASSERT_EQUAL(keysv,"\"name\" "); + } + TEST_SUCCEED(); + } + bool raw_string() { TEST_START(); auto json = R"( {"name": "Jack The Ripper \u0033"} )"_padded; @@ -1709,6 +1741,7 @@ bool value_raw_json_object() { bool run() { return true #if SIMDJSON_EXCEPTIONS + && key_raw_json_token() && to_optional() && value_raw_json_array() && value_raw_json_object() && gen_raw1() && gen_raw2() && gen_raw3()