Compare commits

...

11 Commits

Author SHA1 Message Date
Daniel Lemire c5c43e9c7f Release 2023-07-06 21:48:59 -04:00
Daniel Lemire 031ee32969 Undoing PR 2004 due to performance concerns. (#2029)
* Undoing PR 2004 due to performance concerns.

Fixes https://github.com/simdjson/simdjson/issues/2028
Fixes https://github.com/simdjson/simdjson/issues/2021

* Adding final lines.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-07-06 21:34:14 -04:00
John Keiser b613326d69 Merge pull request #2027 from simdjson/jkeiser/number_type-namespace
Move numberparsing functions to architecture top level
2023-07-06 12:52:16 -07:00
John Keiser 53dad74d8e Don't put number_type into ondemand 2023-07-05 14:26:23 -07:00
John Keiser e4da19f5cf Make more prominent hacking quickstart 2023-07-05 13:36:47 -07:00
John Keiser 342b58986a Add clangd file for VS Code 2023-07-05 13:13:32 -07:00
Daniel Lemire 7edc475886 Update README.md 2023-07-03 18:46:21 -04:00
Dirk Stolle d2f8e84fc1 update various actions in GitHub Actions to v3 (#2024)
The updated actions are actions/cache, actions/checkout and
actions/upload-artifact.
2023-06-26 09:09:09 -04:00
Dirk Stolle 08000518cb Fix some typos (#2025) 2023-06-26 08:37:52 -04:00
Daniel Lemire 90040d96a5 Update basics.md 2023-06-22 21:38:45 -04:00
Daniel Lemire 2934892c0b Improving documentation. (#2020)
* Improving documentation.

* Fix typo

* Moving the definition.

* [skip ci] typo
2023-06-20 13:51:53 -04:00
40 changed files with 542 additions and 484 deletions
+2
View File
@@ -0,0 +1,2 @@
CompileFlags:
CompilationDatabase: build
+3 -3
View File
@@ -124,7 +124,7 @@ jobs:
done done
- name: Save the corpus as a github artifact - name: Save the corpus as a github artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: corpus name: corpus
path: corpus.tar path: corpus.tar
@@ -147,7 +147,7 @@ jobs:
run: tar cf valgrind.tar valgrind-*.txt run: tar cf valgrind.tar valgrind-*.txt
- name: Save valgrind output as a github artifact - name: Save valgrind output as a github artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: valgrindresults name: valgrindresults
@@ -155,7 +155,7 @@ jobs:
if-no-files-found: ignore if-no-files-found: ignore
- name: Archive any crashes as an artifact - name: Archive any crashes as an artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: crashes name: crashes
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
name: Build on ubuntu-20.04 ppc64le name: Build on ubuntu-20.04 ppc64le
steps: steps:
- uses: actions/checkout@v2.1.0 - uses: actions/checkout@v3
- uses: uraimo/run-on-arch-action@v2.0.5 - uses: uraimo/run-on-arch-action@v2.0.5
name: Run commands name: Run commands
id: runcmd id: runcmd
+2 -2
View File
@@ -9,8 +9,8 @@ jobs:
! contains(toJSON(github.event.commits.*.message), '[skip github]') ! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/cache@v2 - uses: actions/cache@v3
with: with:
path: dependencies/.cache path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }} key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF} - {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Configure - name: Configure
run: | run: |
cmake -DSIMDJSON_CXX_STANDARD=20 -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build cmake -DSIMDJSON_CXX_STANDARD=20 -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
+1 -1
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project( project(
simdjson simdjson
# The version number is modified by tools/release.py # The version number is modified by tools/release.py
VERSION 3.2.0 VERSION 3.2.1
DESCRIPTION "Parsing gigabytes of JSON per second" DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/" HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C LANGUAGES CXX C
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = "3.2.0" PROJECT_NUMBER = "3.2.1"
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
+12 -2
View File
@@ -6,16 +6,26 @@ Here is wisdom about how to build, test and run simdjson from within the reposit
If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md) guide. If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md) guide.
- [Hacking simdjson](#hacking-simdjson)
- [Build Quickstart](#build-quickstart)
- [Design notes](#design-notes) - [Design notes](#design-notes)
- [Developer mode](#developer-mode) - [Developer mode](#developer-mode)
- [Directory Structure and Source](#directory-structure-and-source) - [Directory Structure and Source](#directory-structure-and-source)
- [Runtime Dispatching](#runtime-dispatching) - [Runtime Dispatching](#runtime-dispatching)
- [Regenerating Single-Header Files](#regenerating-single-header-files) - [Regenerating Single-Header Files](#regenerating-single-header-files)
- [Usage (CMake on 64-bit platforms like Linux, FreeBSD or macOS)](#usage-cmake-on-64-bit-platforms-like-linux-freebsd-or-macos) - [Usage (CMake on 64-bit platforms like Linux, FreeBSD or macOS)](#usage-cmake-on-64-bit-platforms-like-linux-freebsd-or-macos)
- [Usage (CMake on 64-bit Windows using Visual Studio 2019)](#usage-cmake-on-64-bit-windows-using-visual-studio-2019) - [Usage (CMake on 64-bit Windows using Visual Studio 2019 or better)](#usage-cmake-on-64-bit-windows-using-visual-studio-2019-or-better)
- [Various References](#various-references) - [Various References](#various-references)
Build Quickstart
------------------------------
```bash
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build .
```
Design notes Design notes
------------------------------ ------------------------------
+1
View File
@@ -57,6 +57,7 @@ Real-world usage
- [fastgltf](https://github.com/spnda/fastgltf) - [fastgltf](https://github.com/spnda/fastgltf)
- [vast](https://github.com/tenzir/vast) - [vast](https://github.com/tenzir/vast)
- [ada-url](https://github.com/ada-url/ada) - [ada-url](https://github.com/ada-url/ada)
- [fastgron](https://github.com/adamritter/fastgron)
If you are planning to use simdjson in a product, please work from one of our releases. If you are planning to use simdjson in a product, please work from one of our releases.
+1 -1
View File
@@ -15,7 +15,7 @@ struct simdjsondom2msgpack {
* @param json JSON input * @param json JSON input
* @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes * @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes
* of padding) * of padding)
* @return std::string_view msgpack output, writting to the temporary buffer * @return std::string_view msgpack output, writing to the temporary buffer
*/ */
inline std::string_view to_msgpack(const simdjson::padded_string &json, inline std::string_view to_msgpack(const simdjson::padded_string &json,
uint8_t *buf); uint8_t *buf);
+1 -1
View File
@@ -36,7 +36,7 @@ struct simdjson2msgpack {
* @param json JSON input * @param json JSON input
* @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes * @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes
* of padding) * of padding)
* @return std::string_view msgpack output, writting to the temporary buffer * @return std::string_view msgpack output, writing to the temporary buffer
*/ */
inline std::string_view to_msgpack(const simdjson::padded_string &json, inline std::string_view to_msgpack(const simdjson::padded_string &json,
uint8_t *buf); uint8_t *buf);
+16 -8
View File
@@ -335,8 +335,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool, `double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`, ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method, `get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for ondemand::object and ondemand::array. An exception is thrown if the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
the cast is not possible. Importantly, when getting an ondemand::object or ondemand::array instance, its content is the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error Handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus, not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
for example, you could have an ondemand::object instance pointing at the invalid JSON `{ "this is not a valid object" }`: the validation occurs as you access the content. for example, you could have an ondemand::object instance pointing at the invalid JSON `{ "this is not a valid object" }`: the validation occurs as you access the content.
The `get_string()` returns a valid UTF-8 string, after The `get_string()` returns a valid UTF-8 string, after
@@ -354,8 +354,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> `get_string()` twice on an object (or to cast an object twice to `std::string_view`). > `get_string()` twice on an object (or to cast an object twice to `std::string_view`).
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will * **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string, doing a character-by-character scan through the object looking for the field with the matching string, doing a character-by-character
comparison. For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsability as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The is in error. Furthermore, you can only consume one field at a time, on the same object. The
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`. value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -887,10 +887,17 @@ auto error = parser.iterate(json).get(doc);
if (error) { cerr << error << endl; exit(1); } if (error) { cerr << error << endl; exit(1); }
``` ```
When there is no error, the error code simdjson::SUCCESS is returned: it evaluates as false as a Boolean. When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
We have several error codes to indicate errors, they all evaluate to true as a Boolean: your software should not generally not depend on exact We have several error codes to indicate errors, they all evaluate to true as a Boolean: your software should not generally not depend on exact
error codes. We may change the error codes in future releases and the exact error codes could vary depending on your system. error codes. We may change the error codes in future releases and the exact error codes could vary depending on your system.
Some errors are recoverable:
* You may get the error `simdjson::INCORRECT_TYPE` after trying to convert a value to an incorrect type: e.g., you expected a number and try to convert the value to a number, but it is an array.
* You may query a key from an object, but the key is missing in which case you get the error `simdjson::NO_SUCH_FIELD`: e.g., you call `obj["myname"]` and the object does not have a key `"myname"`.
Other errors (e.g., `simdjson::INCOMPLETE_ARRAY_OR_OBJECT`) may indicate a fatal error and often follow from the fact that the document is not valid JSON. In which case, it is no longer possible to continue accessing the document: calling the method `is_alive()` on the document instance returns false. All following accesses will keep returning the same fatal error (e.g., `simdjson::INCOMPLETE_ARRAY_OR_OBJECT`).
When you use the code without exceptions, it is your responsibility to check for error before using the When you use the code without exceptions, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined behavior. Most compilers should be able to help you if you activate the right result: if there is an error, the result value will not be valid and using it will caused undefined behavior. Most compilers should be able to help you if you activate the right
set of warnings: they can identify variables that are written to but never otherwise accessed. set of warnings: they can identify variables that are written to but never otherwise accessed.
@@ -1422,10 +1429,9 @@ This parser can't support a document that big
If your documents are large (e.g., larger than a megabyte), then the `iterate_many` function is maybe ill-suited. It is really meant to support reading efficiently streams of relatively small documents (e.g., a few kilobytes each). If you have larger documents, you should use other functions like `iterate`. If your documents are large (e.g., larger than a megabyte), then the `iterate_many` function is maybe ill-suited. It is really meant to support reading efficiently streams of relatively small documents (e.g., a few kilobytes each). If you have larger documents, you should use other functions like `iterate`.
We also provide some support for comma-separated documents and other advanced features.
See [iterate_many.md](iterate_many.md) for detailed information and design. See [iterate_many.md](iterate_many.md) for detailed information and design.
Parsing Numbers Inside Strings Parsing Numbers Inside Strings
------------------------------ ------------------------------
@@ -1948,6 +1954,8 @@ Performance Tips
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. - Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`.
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`). - For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance. - For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
- Never seek to access a field twice (e.g., o["data"] and later again o["data"]). Instead capture once an ondemand::value and reuse it.
- If you must access several different keys in an object, it might be preferable to iterate through all the fields in the object instead, and branch on the field keys.
- If possible, refer to each object and array in your code once. For example, the following code repeatedly refers to the `"data"` key to create an object... - If possible, refer to each object and array in your code once. For example, the following code repeatedly refers to the `"data"` key to create an object...
```C++ ```C++
std::string_view make = o["data"]["make"]; std::string_view make = o["data"]["make"];
@@ -1961,4 +1969,4 @@ Performance Tips
std::string_view year = data["year"]; std::string_view year = data["year"];
std::string_view rating = data["rating"]; std::string_view rating = data["rating"];
``` ```
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The logs output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. The log level is set to `INFO` by default, but can be modified with the environment variable `SIMDJSON_LOG_LEVEL`. Setting the environment variable `SIMDJSON_LOG_LEVEL` to `ERROR` will log only errors. - To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
+45 -2
View File
@@ -129,6 +129,18 @@ Some official formats **(non-exhaustive list)**:
API API
--- ---
Example:
```c++
auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
ondemand::parser parser;
ondemand::document_stream docs = parser.iterate_many(json);
for (auto doc : docs) {
std::cout << doc["foo"] << std::endl;
}
// Prints 1 2 3
```
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API. See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
## Use cases ## Use cases
@@ -238,7 +250,38 @@ This will print:
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet. Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
Comma separated documents Comma-separated documents
----------- -----------
`iterate_many` also takes in an option to allow parsing of comma separated documents. In this mode, the entire buffer is processed in 1 batch and batch size will be increased to be as large as the JSON passed. Therefore, the capacity of the parser has to be sufficient to support the batch size set. We also support comma-separated documents, but with some performance limitations. The `iterate_many` function takes in an option to allow parsing of comma separated documents (which defaults on false). In this mode, the entire buffer is processed in one batch. Therefore, the total size of the document should not exceed the maximal capacity of the parser (4 GB). This mode also effectively disallow multithreading. It is therefore mostly suitable for not "very large" inputs. In this mode, the batch_size parameter
is effectively ignored, as it is set to at least the document size.
Example:
```C++
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
ondemand::parser parser;
ondemand::document_stream doc_stream;
// We pass '32' as the batch size, but it is a bogus parameter because, since
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
if(error) { std::cerr << error << std::endl; return; }
for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl;
}
```
This will print:
```
number
number
number
number
string
string
string
object
array
```
+3 -2
View File
@@ -3,10 +3,11 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
// we don't have SSE, so let us use a scalar function // we don't have SSE, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ // credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val; uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t)); std::memcpy(&val, chars, sizeof(uint64_t));
@@ -15,7 +16,7 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+4 -2
View File
@@ -10,8 +10,9 @@ void found_float(double result, const uint8_t *buf);
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ // credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val; uint64_t val;
memcpy(&val, chars, sizeof(uint64_t)); memcpy(&val, chars, sizeof(uint64_t));
@@ -19,11 +20,12 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
} }
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars)); return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+42 -31
View File
@@ -3,8 +3,9 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
/// @private
namespace numberparsing {
namespace ondemand {
/** /**
* The type of a JSON number * The type of a JSON number
*/ */
@@ -13,13 +14,6 @@ enum class number_type {
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement 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 unsigned_integer /// a positive integer larger or equal to 1<<63
}; };
}
namespace {
/// @private
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS #ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
@@ -34,6 +28,7 @@ namespace numberparsing {
#endif #endif
namespace { namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double. // Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). // The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. // The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
@@ -45,7 +40,7 @@ simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool
std::memcpy(&d, &mantissa, sizeof(d)); std::memcpy(&d, &mantissa, sizeof(d));
return d; return d;
} }
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is // Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result. // true, negate the result.
// This function will only work in some cases, when it does not work, success is // This function will only work in some cases, when it does not work, success is
@@ -62,10 +57,11 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
#endif #endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest. // We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991) { if (0 <= power && power <= 22 && i <= 9007199254740991)
#else #else
if (-22 <= power && power <= 22 && i <= 9007199254740991) { if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif #endif
{
// convert the integer into a double. This is lossless since // convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1. // 0 <= i <= 2^53 - 1.
d = double(i); d = double(i);
@@ -317,6 +313,7 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
// to handle that max may be a macro on windows). // to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest()); return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
} }
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr)); *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values. // We do not accept infinite values.
@@ -350,16 +347,6 @@ simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
0x3333333333333333); 0x3333333333333333);
} }
template<typename W>
error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) {
double d;
if (parse_float_fallback(src, &d)) {
writer.append_double(d);
return SUCCESS;
}
return INVALID_NUMBER(src);
}
template<typename I> template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) { simdjson_inline bool parse_digit(const uint8_t c, I &i) {
@@ -372,7 +359,7 @@ simdjson_inline bool parse_digit(const uint8_t c, I &i) {
return true; return true;
} }
simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the // we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer // floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the // z that fits in 53 bits, then we will be able to convert back the
@@ -460,6 +447,20 @@ simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t d
return digit_count - size_t(start - start_digits); return digit_count - size_t(start - start_digits);
} }
} // unnamed namespace
/** @private */
template<typename W>
error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) {
double d;
if (parse_float_fallback(src, &d)) {
writer.append_double(d);
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W> template<typename W>
simdjson_inline error_code 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) { simdjson_inline error_code 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) {
// If we frequently had to deal with long strings of digits, // If we frequently had to deal with long strings of digits,
@@ -531,7 +532,7 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const src) noexcept { return 0; } simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const src) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept { return false; } simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<ondemand::number_type> get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; }
#else #else
// parse the number at src // parse the number at src
@@ -573,7 +574,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
if ('.' == *p) { if ('.' == *p) {
is_float = true; is_float = true;
++p; ++p;
SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows digit_count = int(p - start_digits); // used later to guard against overflows
} }
if (('e' == *p) || ('E' == *p)) { if (('e' == *p) || ('E' == *p)) {
@@ -1061,7 +1062,7 @@ simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *
return false; return false;
} }
simdjson_unused simdjson_inline simdjson_result<ondemand::number_type> get_number_type(const uint8_t * src) noexcept { simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-'); bool negative = (*src == '-');
src += uint8_t(negative); src += uint8_t(negative);
const uint8_t *p = src; const uint8_t *p = src;
@@ -1070,20 +1071,20 @@ simdjson_unused simdjson_inline simdjson_result<ondemand::number_type> get_numbe
if (jsoncharutils::is_structural_or_whitespace(*p)) { if (jsoncharutils::is_structural_or_whitespace(*p)) {
// We have an integer. // We have an integer.
// If the number is negative and valid, it must be a signed integer. // If the number is negative and valid, it must be a signed integer.
if(negative) { return ondemand::number_type::signed_integer; } if(negative) { return number_type::signed_integer; }
// We want values larger or equal to 9223372036854775808 to be unsigned // We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers. // integers, and the other values to be signed integers.
int digit_count = int(p - src); int digit_count = int(p - src);
if(digit_count >= 19) { if(digit_count >= 19) {
const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808"); const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) {
return ondemand::number_type::unsigned_integer; return number_type::unsigned_integer;
} }
} }
return ondemand::number_type::signed_integer; return number_type::signed_integer;
} }
// Hopefully, we have 'e' or 'E' or '.'. // Hopefully, we have 'e' or 'E' or '.'.
return ondemand::number_type::floating_point_number; return number_type::floating_point_number;
} }
// Never read at src_end or beyond // Never read at src_end or beyond
@@ -1245,10 +1246,20 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
} }
return d; return d;
} }
} //namespace {}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING #endif // SIMDJSON_SKIPNUMBERPARSING
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace numberparsing } // namespace numberparsing
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+3
View File
@@ -10,6 +10,9 @@ namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */ /** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t; using depth_t = int32_t;
/** @copydoc simdjson::SIMDJSON_IMPLEMENTATION::numberparsing::number_type */
using number_type = simdjson::SIMDJSON_IMPLEMENTATION::numberparsing::number_type;
} // namespace ondemand } // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
@@ -456,7 +456,7 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
return first.is_integer(); return first.is_integer();
} }
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_number_type() noexcept { simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_number_type(); return first.get_number_type();
} }
@@ -739,7 +739,7 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); } if (error()) { return error(); }
return first.is_integer(); return first.is_integer();
} }
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_number_type() noexcept { simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_number_type(); return first.get_number_type();
} }
+2 -2
View File
@@ -730,7 +730,7 @@ public:
simdjson_inline bool at_end() const noexcept; simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept; simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept; simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> get_number_type() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const 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<std::string_view> raw_json_token() noexcept;
@@ -795,7 +795,7 @@ public:
simdjson_inline simdjson_result<int32_t> current_depth() const 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_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept; simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> get_number_type() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const 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<std::string_view> raw_json_token() noexcept;
@@ -298,8 +298,8 @@ protected:
friend class raw_json_string; friend class raw_json_string;
friend class parser; friend class parser;
friend class value_iterator; friend class value_iterator;
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) noexcept; 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) noexcept;
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) noexcept; 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) noexcept;
}; // json_iterator }; // json_iterator
} // namespace ondemand } // namespace ondemand
@@ -15,15 +15,6 @@ inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
return out; return out;
} }
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) { inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value(); return out << type.value();
@@ -84,11 +84,11 @@ protected:
*/ */
friend class value_iterator; friend class value_iterator;
template<typename W> 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); 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> template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer); friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
template<typename W>
friend error_code numberparsing::slow_float_parsing(simdjson_unused const uint8_t * src, W writer);
/** Store a signed 64-bit value to the number. */ /** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept; simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */ /** Store an unsigned 64-bit value to the number. */
@@ -120,7 +120,6 @@ protected:
* @param type The json_type. * @param type The json_type.
*/ */
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept; inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept;
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
/** /**
+13 -46
View File
@@ -1,4 +1,3 @@
#include <memory>
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand { namespace ondemand {
@@ -19,68 +18,36 @@ static inline char printable_char(char c) {
} }
} }
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::LOG_ERROR; }
return log_level::LOG_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();
}
template<typename... Args>
inline std::string string_format(const std::string& format, const Args&... args)
{
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...);
return std::string(buf.get(), buf.get() + size - 1);
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { 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::LOG_INFO); log_line(iter, "", type, detail, delta, depth_delta);
} }
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept { 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::LOG_INFO); log_line(iter, index, depth, "", type, detail);
} }
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { 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::LOG_INFO); log_line(iter, "", type, detail, delta, depth_delta);
} }
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept { 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::LOG_INFO); log_line(iter, index, depth, "+", type, detail);
if (LOG_ENABLED) { log_depth++; } if (LOG_ENABLED) { log_depth++; }
} }
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept { 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::LOG_INFO); log_line(iter, "+", type, "", delta, depth_delta);
if (LOG_ENABLED) { log_depth++; } if (LOG_ENABLED) { log_depth++; }
} }
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept { inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; } if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::LOG_INFO); log_line(iter, "-", type, "", delta, depth_delta);
} }
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept { 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::LOG_ERROR); log_line(iter, "ERROR: ", error, detail, delta, depth_delta);
} }
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept { 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::LOG_ERROR); log_line(iter, index, depth, "ERROR: ", error, detail);
} }
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
@@ -104,7 +71,7 @@ inline void log_error(const value_iterator &iter, const char *error, const char
} }
inline void log_headers() noexcept { inline void log_headers() noexcept {
if (LOG_ENABLED && simdjson_unlikely(should_log(log_level::LOG_INFO))) { if (LOG_ENABLED) {
// Technically a static variable is not thread-safe, but if you are using threads // Technically a static variable is not thread-safe, but if you are using threads
// and logging... well... // and logging... well...
static bool displayed_hint{false}; static bool displayed_hint{false};
@@ -154,11 +121,11 @@ inline void log_headers() noexcept {
} }
} }
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) noexcept { inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level); log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail);
} }
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) noexcept { 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) noexcept {
if (LOG_ENABLED && simdjson_unlikely(should_log(level))) { if (LOG_ENABLED) {
const int indent = depth*2; const int indent = depth*2;
const auto buf = iter.token.buf; const auto buf = iter.token.buf;
printf("| %*s%s%-*s ", printf("| %*s%s%-*s ",
+5 -9
View File
@@ -5,13 +5,11 @@ namespace ondemand {
class json_iterator; class json_iterator;
class value_iterator; class value_iterator;
// 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 { namespace logger {
enum class log_level : int32_t {
LOG_INFO = 0,
LOG_ERROR = 1
};
#if SIMDJSON_VERBOSE_LOGGING #if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true; static constexpr const bool LOG_ENABLED = true;
#else #else
@@ -21,11 +19,9 @@ enum class log_level : int32_t {
// We do not want these functions to be 'really inlined' since real inlining is // 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 // for performance purposes and if you are using the loggers, you do not care about
// performance (or should not). // performance (or should not).
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args);
static inline void log_headers() noexcept; static inline void log_headers() noexcept;
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, log_level level) noexcept; 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) noexcept;
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, log_level level) noexcept; 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) 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_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, 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_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
+4 -24
View File
@@ -5,23 +5,13 @@ namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept { simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value; bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) { if (!has_value) { return NO_SUCH_FIELD; }
auto key_str = std::string(key.data(), key.size());
auto msg = logger::string_format("Cannot find key: %s", key_str.c_str());
logger::log_error(iter, msg.c_str());
return NO_SUCH_FIELD;
}
return value(iter.child()); return value(iter.child());
} }
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept { simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value; bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) { if (!has_value) { return NO_SUCH_FIELD; }
auto key_str = std::string(key.data(), key.size());
auto msg = logger::string_format("Cannot find key: %s", key_str.c_str());
logger::log_error(iter, msg.c_str());
return NO_SUCH_FIELD;
}
return value(iter.child()); return value(iter.child());
} }
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept { simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
@@ -33,23 +23,13 @@ simdjson_inline simdjson_result<value> object::operator[](const std::string_view
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept { simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
bool has_value; bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) { if (!has_value) { return NO_SUCH_FIELD; }
auto key_str = std::string(key.data(), key.size());
auto msg = logger::string_format("Cannot find key: %s", key_str.c_str());
logger::log_error(iter, msg.c_str());
return NO_SUCH_FIELD;
}
return value(iter.child()); return value(iter.child());
} }
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept { simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
bool has_value; bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) { if (!has_value) { return NO_SUCH_FIELD; }
auto key_str = std::string(key.data(), key.size());
auto msg = logger::string_format("Cannot find key: %s", key_str.c_str());
logger::log_error(iter, msg.c_str());
return NO_SUCH_FIELD;
}
return value(iter.child()); return value(iter.child());
} }
@@ -212,6 +212,11 @@ public:
* spot is cache-related: small enough to fit in cache, yet big enough to * spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop. * parse as many documents as possible in one tight loop.
* Defaults to 10MB, which has been a reasonable sweet spot in our tests. * Defaults to 10MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated (defaults on false) This allows a mode where the documents are
* separated by commas instead of whitespace. It comes with a performance
* penalty because the entire document is indexed at once (and the document must be
* less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter
* is effectively ignored, as it is set to at least the document size.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors: * @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails * - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity. * - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
@@ -116,8 +116,8 @@ protected:
friend class json_iterator; friend class json_iterator;
friend class value_iterator; friend class value_iterator;
friend class object; friend class object;
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 lvl) noexcept; 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) noexcept;
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 lvl) noexcept; 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) noexcept;
}; };
} // namespace ondemand } // namespace ondemand
@@ -357,7 +357,7 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); } if (error()) { return error(); }
return first.is_integer(); return first.is_integer();
} }
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_number_type() noexcept { simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_number_type(); return first.get_number_type();
} }
+1 -1
View File
@@ -688,7 +688,7 @@ public:
simdjson_inline simdjson_result<bool> is_scalar() noexcept; simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept; simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept; simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> get_number_type() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept; simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
@@ -584,7 +584,7 @@ simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check
return answer; return answer;
} }
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept { simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::numberparsing::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_start_length(); auto max_len = peek_start_length();
auto json = peek_root_scalar("number"); auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
+3 -2
View File
@@ -3,8 +3,9 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful. // this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0'); const __m128i ascii0 = _mm_set1_epi8('0');
@@ -23,7 +24,7 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
t4); // only captures the sum of the first 8 digits, drop the rest t4); // only captures the sum of the first 8 digits, drop the rest
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+2 -2
View File
@@ -3,7 +3,7 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful. // this actually computes *16* values so we are being wasteful.
@@ -23,7 +23,7 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
t4); // only captures the sum of the first 8 digits, drop the rest t4); // only captures the sum of the first 8 digits, drop the rest
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+4 -4
View File
@@ -9,12 +9,12 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function // we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ // credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
static simdjson_inline uint32_t /** @private */
parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val; uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t)); std::memcpy(&val, chars, sizeof(uint64_t));
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
@@ -29,7 +29,7 @@ parse_eight_digits_unrolled(const uint8_t *chars) {
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H #define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */ /** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.0" #define SIMDJSON_VERSION "3.2.1"
namespace simdjson { namespace simdjson {
enum { enum {
@@ -19,7 +19,7 @@ enum {
/** /**
* The revision (major.minor.REVISION) of simdjson being used. * The revision (major.minor.REVISION) of simdjson being used.
*/ */
SIMDJSON_VERSION_REVISION = 0 SIMDJSON_VERSION_REVISION = 1
}; };
} // namespace simdjson } // namespace simdjson
+3 -2
View File
@@ -3,8 +3,9 @@
namespace simdjson { namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION { namespace SIMDJSON_IMPLEMENTATION {
namespace { namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful. // this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0'); const __m128i ascii0 = _mm_set1_epi8('0');
@@ -23,7 +24,7 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
t4); // only captures the sum of the first 8 digits, drop the rest t4); // only captures the sum of the first 8 digits, drop the rest
} }
} // unnamed namespace } // namespace numberparsing
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
+4 -2
View File
@@ -126,8 +126,10 @@ def dofile(fid, prepath, filename):
# Forcing it to be UTC is difficult, because it needs to be portable # Forcing it to be UTC is difficult, because it needs to be portable
# between gnu date and busybox date. # between gnu date and busybox date.
try: try:
timestamp = subprocess.run(['git', 'show', '-s', '--format=%ci', 'HEAD'], proc = subprocess.run(['git', 'show', '-s', '--format=%ci', 'HEAD'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip() stdout=subprocess.PIPE)
print("the commandline is {}".format(proc.args))
timestamp = proc.stdout.decode('utf-8').strip()
except: except:
print("git not found, timestamp based on current time") print("git not found, timestamp based on current time")
timestamp = str(datetime.datetime.now()) timestamp = str(datetime.datetime.now())
+1 -1
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2023-06-15 09:11:14 -0400. Do not edit! */ /* auto-generated on 2023-07-06 21:34:14 -0400. Do not edit! */
/* begin file src/simdjson.cpp */ /* begin file src/simdjson.cpp */
#include "simdjson.h" #include "simdjson.h"
+317 -305
View File
File diff suppressed because it is too large Load Diff
+17 -1
View File
@@ -1037,6 +1037,22 @@ int load_example_except_morecomplete(void) {
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
bool allow_comma_separated_example() {
TEST_START();
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
ondemand::parser parser;
ondemand::document_stream doc_stream;
// We pass '32' as the batch size, but it is a bogus parameter because, since
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
if(error) { std::cerr << error << std::endl; return false; }
for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl;
}
TEST_SUCCEED();
}
#endif #endif
bool test_load_example() { bool test_load_example() {
TEST_START(); TEST_START();
@@ -1067,7 +1083,6 @@ bool current_location_tape_error() {
TEST_SUCCEED(); TEST_SUCCEED();
} }
bool current_location_user_error() { bool current_location_user_error() {
TEST_START(); TEST_START();
auto json = R"( [1,2,3] )"_padded; auto json = R"( [1,2,3] )"_padded;
@@ -1301,6 +1316,7 @@ bool run() {
&& gen_raw1() && gen_raw2() && gen_raw3() && gen_raw1() && gen_raw2() && gen_raw3()
&& at_end() && at_end()
&& example1956() && example1958() && example1956() && example1958()
&& allow_comma_separated_example()
// && basics_1() // Fails because twitter.json isn't in current directory. Compile test only. // && basics_1() // Fails because twitter.json isn't in current directory. Compile test only.
&& basics_treewalk() && basics_treewalk()
&& basics_treewalk_breakline() && basics_treewalk_breakline()
+10 -3
View File
@@ -23,8 +23,9 @@ def toversionstring(major, minor, rev):
def topaddedversionstring(major, minor, rev): def topaddedversionstring(major, minor, rev):
return str(major)+str(minor).zfill(3)+str(rev).zfill(3) return str(major)+str(minor).zfill(3)+str(rev).zfill(3)
print("Calling git rev-parse --abbrev-ref HEAD")
pipe = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) pipe = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("the commandline is {}".format(pipe.args))
branchresult = pipe.communicate()[0].decode().strip() branchresult = pipe.communicate()[0].decode().strip()
if(branchresult != "master"): if(branchresult != "master"):
@@ -34,8 +35,9 @@ ret = subprocess.call(["git", "remote", "update"])
if(ret != 0): if(ret != 0):
sys.exit(ret) sys.exit(ret)
print("Calling git log HEAD.. --oneline")
pipe = subprocess.Popen(["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) pipe = subprocess.Popen(["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("the commandline is {}".format(pipe.args))
uptodateresult = pipe.communicate()[0].decode().strip() uptodateresult = pipe.communicate()[0].decode().strip()
if(len(uptodateresult) != 0): if(len(uptodateresult) != 0):
@@ -151,12 +153,17 @@ for line in fileinput.input(doxyfile, inplace=1, backup='.bak'):
print("modified "+doxyfile+", a backup was made") print("modified "+doxyfile+", a backup was made")
print("running amalgamate.py")
cp = subprocess.run(["python3", maindir+ os.sep + "singleheader/amalgamate.py"], stdout=subprocess.DEVNULL) # doesn't capture output
print("the commandline is {}".format(cp.args))
cp = subprocess.run(["python3", "amalgamate.py"], stdout=subprocess.DEVNULL, cwd=maindir+ os.sep + "singleheader") # doesn't capture output
if(cp.returncode != 0): if(cp.returncode != 0):
print("Failed to run amalgamate") print("Failed to run amalgamate")
print("running doxygen")
cp = subprocess.run(["doxygen"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=maindir) # doesn't capture output cp = subprocess.run(["doxygen"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=maindir) # doesn't capture output
print("the commandline is {}".format(cp.args))
if(cp.returncode != 0): if(cp.returncode != 0):
print("Failed to run doxygen") print("Failed to run doxygen")