Compare commits

..

27 Commits

Author SHA1 Message Date
Daniel Lemire 0d2455029d Preparing patch release. 2022-11-23 10:59:48 -05:00
Daniel Lemire e94f027310 Serialize 0 as 0.0 (#1921)
* Serialize 0 as 0.0

* Extending the '.0'.
2022-11-23 10:31:42 -05:00
sean 2b766e4c09 Fix: Add padded_string_view overload for parser::parse (#1916) 2022-11-23 09:22:39 -05:00
Daniel Lemire a5db75e1a6 Adding more development checks to the DOM front-end (#1915)
* This adds SIMDJSON_DEVELOPMENT_CHECKS to the DOM API to help users
in the scenario of issue 1914.

* More documentation and warnings.

* Updating following comments by Tyson
2022-10-17 09:11:09 -04:00
Daniel Lemire 4bd66cb891 We branch on GCC for performance. (#1913) 2022-10-16 13:15:35 -04:00
Daniel Lemire 960a7ebba1 Switching to iterate_many 2022-10-06 11:59:12 -04:00
Daniel Lemire d520062f8e Bumping API 2022-10-06 11:50:33 -04:00
Daniel Lemire db08d78ed0 Documents better the type method and makes is_null return an error condition in some instances (#1909) 2022-10-06 11:47:40 -04:00
Daniel Lemire 46292b8d4b Update basics.md 2022-10-05 08:50:52 -04:00
Tyson Andre 46241287c6 Check for trailing tokens in json2msgpack ondemand benchmark (#1908)
Related to #1904

Users of the simdjson library will see json2msgpack as an available
example of how to recursively process json with the ondemand parser,
and checking for trailing tokens in a document is one part of json validation.

These checks shouldn't affect benchmark results performance.
The benchmark is run on the 631KB twitter.json file.
2022-10-05 08:49:09 -04:00
Daniel Lemire 137cb14bcc Documenting how one can check for the end of the document. (#1907) 2022-10-04 20:23:52 -04:00
Daniel Lemire 5e6be3ed7a Minor fix (documentation and safety) regarding max. depth in ondemand. (#1906) 2022-10-04 12:24:20 -04:00
Tyson Andre c6ab52eebb [skip ci] Add an .editorconfig for .cpp/.h/.md for whitespace settings (#1901)
Make it less likely to accidentally introduce tabs, trailing whitespace,
carriage returns, non-utf8 in files, or files without trailing newlines.

https://editorconfig.org/ has plugins for various editors/IDEs and is
enabled by default in some IDEs.
2022-10-03 11:14:00 -04:00
Daniel Lemire a1aab1fafe Preparing release 2.2.3 2022-10-02 16:32:37 -04:00
Daniel Lemire b92cbbe280 Fixing issue 1898 (#1899)
* Fixing issue 1898 Preserve sign for number with underflowing exponent (#1900)


Before this commit, simdjson parsed "-1e-999" and "-0e-999" and "-1e-342"
as 0.0.
After this commit, those JSON strings get parsed as -0.0.
(https://en.wikipedia.org/wiki/Signed_zero)

The old behavior was inconsistent with the way simdjson parsed "-0.0" as -0.0.

Co-authored-by: Daniel Lemire <daniel@lemire.me>

Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
2022-10-02 16:25:39 -04:00
Tyson Andre 6d2a09f8e5 [skip ci] Fix typo, formatting nit in HACKING.md (#1902)
vim syntax highlighting doesn't work properly without space between a
bulleted list and the start of a code snippet.
2022-10-02 16:19:34 -04:00
Tyson Andre 00564bf7d5 Micro-optimization for parsing surrogate pairs (#1897)
Load 2 bytes and compare the 2 bytes against `"\u"`
Compilers with optimizations turned on will turn this into a 16-bit load
then 16-bit compare on supported platforms
(with smaller compiled code size).

Make it obvious to the compiler that it's reading two
consecutive bytes of the same pointer

Add parse_surrogate_pairs to show the difference exists.
See discussion in #1896
2022-10-02 12:10:53 -04:00
Tyson Andre 5809e51ae4 fix: Reject surrogate pairs with invalid low surrogate (#1896)
Closes #1894

Reject low surrogates outside of the range U+DC00—U+DFFF

Related to https://unicodebook.readthedocs.io/unicode_encodings.html#utf-16-surrogate-pairs

A surrogate pair should consist of a high surrogate and low surrogate.
They're used to represent 0x010000-0x10FFFF in the JSON spec because
the JavaScript specification originally only supported `\uXXXX`.

Previously, simdjson would accept some combinations of valid high
surrogates and invalid low surrogates due to a bug in the check.
(e.g. `\uD888\u1234` was accepted)

U+D800—U+DBFF (1,024 code points): high surrogates
U+DC00—U+DFFF (1,024 code points): low surrogates
2022-09-30 12:13:16 -04:00
Tyson Andre d27e7cce71 Fix typos in doc/basics.md (#1893) 2022-09-30 08:34:37 -04:00
David Korenchuk f7dc03f93d Fix documentation of description() method in implementation (#1895) 2022-09-30 08:34:06 -04:00
sean d4ac1b51d0 Fix various warnings & if constexpr (#1888) 2022-09-27 23:22:47 -04:00
Daniel Lemire 6a4222da71 Adding a remark to the documentation. 2022-09-19 15:28:33 -04:00
Daniel Lemire bba88eb5e5 Fixing typo in the documentation. 2022-09-19 10:21:11 -04:00
Daniel Lemire 16b3816455 This fixes an error caused by overeager gcc static analyser (#1891) 2022-09-19 10:20:48 -04:00
Andrea Pappacoda e5a408386b build: add pkg-config support (#1767)
* build: add pkg-config support

The CMake build script now generates a simple pkg-config files that can
be easily used by non-CMake users.

The file is generated from a template file that gets filled in at
configure time.

As CMake doesn't have anything similar to Meson's pkg-config generator
the file is quite static, i.e. new simdjson public defines/dependencies
won't be picked up automatically.

This approach also suffers from one minor issue, mentioned in
[jtojnar/cmake-snips][]; in short, it doesn't work well when users
specify CMAKE_INSTALL_INCLUDEDIR and similar as absolute paths. It's not
a big deal, and it will easily fixable once you'll require CMake >=3.20.

Fixes #1763

[jtojnar/cmake-snips]: https://github.com/jtojnar/cmake-snips#concatenating-paths-when-building-pkg-config-files

* build: handle absolute paths in .pc generation

As mentioned in the previous commit message, correct concatenation of
paths is only available in CMake >=3.20, so handling absolute paths in
pkg-config file generation requires using jtojnar's JoinPaths module.

* ci: add debian job

This new jobs compiles simdjson on Debian Testing, a semi-rolling
release, so that new compilers are always tested.

This job also tests the pkg-config file introduced in commit
1096c3b299
2022-08-26 16:20:51 -04:00
Herman Semenov e65f28e61a Fixed if condition, Win64 _fseeki64, trivial constructors C++11 (#1883) 2022-08-18 14:16:46 -04:00
Daniel Lemire fbb46b99e2 Fixes and verifies issue 1878. (#1880)
* Fixes and verifies issue 1878.

* Changing how NULL is handled.

* Different design.
2022-08-08 22:00:38 -04:00
58 changed files with 1242 additions and 483 deletions
+12
View File
@@ -0,0 +1,12 @@
# https://editorconfig.org/
root = true
# Conservatively avoid changing defaults for other file types, e.g. raw json files for test cases,
# Makefiles, etc.
[*.{cpp,h,md}]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true
+1
View File
@@ -78,6 +78,7 @@
.gitattributes export-ignore
.gitignore export-ignore
.editorconfig export-ignore
# Sources
*.c text eol=lf diff=c
+33
View File
@@ -0,0 +1,33 @@
name: Debian
on: [push, pull_request]
defaults:
run:
shell: sh
permissions:
contents: read
jobs:
pkg-config:
runs-on: ubuntu-latest
container:
image: debian:testing
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
apt -y update
apt -y --no-install-recommends install g++ cmake make pkg-config
- name: Build and install
run: |
cmake -B build
cmake --build build
cmake --install build
- name: Test pkg-config
run: g++ examples/quickstart/quickstart.cpp $(pkg-config --cflags --libs simdjson)
-34
View File
@@ -1,34 +0,0 @@
name: Macos (Xcode 11)
on: [push, pull_request]
jobs:
macos-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
xcversion select 11.7
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
+21 -3
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 2.2.2
VERSION 3.0.1
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +20,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "13.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "13" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "14.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "14" CACHE STRING "simdjson library soversion")
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
@@ -152,6 +152,24 @@ install(
COMPONENT example_Development
)
# pkg-config
include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
if(SIMDJSON_ENABLE_THREADS)
set(PKGCONFIG_CFLAGS "-DSIMDJSON_THREADS_ENABLED=1")
if(CMAKE_THREAD_LIBS_INIT)
set(PKGCONFIG_LIBS_PRIVATE "Libs.private: ${CMAKE_THREAD_LIBS_INIT}")
endif()
endif()
configure_file("simdjson.pc.in" "simdjson.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/simdjson.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
#
# CPack
#
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "2.2.2"
PROJECT_NUMBER = "3.0.1"
# 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
+2 -2
View File
@@ -82,7 +82,7 @@ Other important files and directories:
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script).
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnnecessary system calls. We recommend checking the performance as follows:
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnecessary system calls. We recommend checking the performance as follows:
```bash
mkdir build
cd build
@@ -114,7 +114,7 @@ Other important files and directories:
* `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
* `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
* `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
*`jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
* `jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
> **Don't modify the files in singleheader/ directly; these are automatically generated.**
+1 -1
View File
@@ -3,7 +3,7 @@
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/2.0.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/3.0.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
+25
View File
@@ -656,6 +656,31 @@ static void error_code_twitter_image_sizes(State& state) noexcept {
}
BENCHMARK(error_code_twitter_image_sizes);
static void parse_surrogate_pairs(State& state) {
// NOTE: This mostly exists to show there's a tiny benefit to
// loading and comparing both bytes of "\\u" simultaneously.
// (which should also reduce the compiled code size).
// The repeated surrogate pairs make this easier to measure.
dom::parser parser;
const std::string_view data = "\"\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\\uD834\\uDD1E\"";
padded_string docdata{data};
// we do not want mem. alloc. in the loop.
auto error = parser.allocate(docdata.size());
if (error) {
cout << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse string" << error << endl;
return;
}
}
}
BENCHMARK(parse_surrogate_pairs);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
+72 -3
View File
@@ -51,6 +51,7 @@ private:
simdjson_inline void
write_raw_string(simdjson::ondemand::raw_json_string rjs);
inline void recursive_processor(simdjson::ondemand::value element);
inline void recursive_processor_ref(simdjson::ondemand::value& element);
simdjson::ondemand::parser parser;
uint8_t *buff{};
@@ -74,17 +75,34 @@ simdjson2msgpack::to_msgpack(const simdjson::padded_string &json,
write_byte(0xc2 + doc.get_bool());
break;
case simdjson::ondemand::json_type::null:
write_byte(0xc0);
// We check that the value is indeed null
// otherwise: an error is thrown.
if(doc.is_null()) {
write_byte(0xc0);
}
break;
case simdjson::ondemand::json_type::array:
case simdjson::ondemand::json_type::object:
default:
// impossible
break;
SIMDJSON_UNREACHABLE();
}
} else {
simdjson::ondemand::value val = doc;
#define SIMDJSON_GCC_COMPILER ((__GNUC__) && !(__clang__) && !(__INTEL_COMPILER))
#if SIMDJSON_GCC_COMPILER
// the GCC compiler does well with by-value passing.
// GCC has superior recursive inlining:
// https://stackoverflow.com/questions/29186186/why-does-gcc-generate-a-faster-program-than-clang-in-this-recursive-fibonacci-co
// https://godbolt.org/z/TeK4doE51
recursive_processor(val);
#else
recursive_processor_ref(val);
#endif
}
if (doc.current_location().error() == simdjson::SUCCESS) {
// Example of error detection - this won't be reached on twitter.json in the benchmark.
throw "There are unexpectedly tokens after the end of the json in the json2msgpack sample data";
}
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
}
@@ -156,7 +174,58 @@ void simdjson2msgpack::recursive_processor(simdjson::ondemand::value element) {
write_byte(0xc2 + element.get_bool());
break;
case simdjson::ondemand::json_type::null:
write_byte(0xc0);
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
write_byte(0xc0);
}
break;
default:
SIMDJSON_UNREACHABLE();
}
}
void simdjson2msgpack::recursive_processor_ref(simdjson::ondemand::value& element) {
switch (element.type()) {
case simdjson::ondemand::json_type::array: {
uint32_t counter = 0;
write_byte(0xdd);
uint8_t *location = skip_uint32();
for (auto child : element.get_array()) {
counter++;
simdjson::ondemand::value v = child.value();
recursive_processor_ref(v);
}
write_uint32_at(counter, location);
} break;
case simdjson::ondemand::json_type::object: {
uint32_t counter = 0;
write_byte(0xdf);
uint8_t *location = skip_uint32();
for (auto field : element.get_object()) {
counter++;
write_raw_string(field.key());
simdjson::ondemand::value v = field.value();
recursive_processor_ref(v);
}
write_uint32_at(counter, location);
} break;
case simdjson::ondemand::json_type::number:
write_double(element.get_double());
break;
case simdjson::ondemand::json_type::string:
write_raw_string(element.get_raw_json_string());
break;
case simdjson::ondemand::json_type::boolean:
write_byte(0xc2 + element.get_bool());
break;
case simdjson::ondemand::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
write_byte(0xc0);
}
break;
default:
SIMDJSON_UNREACHABLE();
+23
View File
@@ -0,0 +1,23 @@
# This module provides function for joining paths
# known from most languages
#
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()
+43 -11
View File
@@ -99,7 +99,7 @@ We recommend CMake version 3.15 or better.
See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file). It works under Linux, FreeBSD, macOS and Windows (including Visual Studio).
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The CMake build in simdjson can be tailored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
Versions
@@ -454,7 +454,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
size_t count = test_array.count_elements(); // requires simdjson 1.0 or better
std::cout << "Number of elements: " << count << std::endl;
for(ondemand::object elem: test_array) {
std::cout << simdjson::to_string(elem);
std::cout << simdjson::to_json_string(elem);
}
```
* **Counting fields in objects:** Other times, it is useful to scan an object to determine the number of fields prior to
@@ -482,8 +482,20 @@ support for users who avoid exceptions. See [the simdjson error handling documen
std::cout << "Number of fields: " << count << std::endl; // Prints "Number of fields: 2"
```
* **Tree Walking and JSON Element Types:** Sometimes you don't necessarily have a document
with a known type, and are trying to generically inspect or walk over JSON elements. To do that, you can use iterators and the `type()` method. You can also represent arbitrary JSON values with
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You may also access [raw strings](#raw-strings).
with a known type, and are trying to generically inspect or walk over JSON elements.
You can also represent arbitrary JSON values with
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`.
You can query the type of a document or a value with the `type()` method.
The `type()` method does not consume or validate documents and values, but it tells you whether they are
- arrays (`json_type::array`),
- objects (`json_type::object`)
- numbers (`json_type::number`),
- strings (`json_type::string`),
- Booleans (`json_type::boolean`),
- 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).
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) {
@@ -532,7 +544,11 @@ support for users who avoid exceptions. See [the simdjson error handling documen
cout << element.get_bool();
break;
case ondemand::json_type::null:
cout << "null";
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
cout << "null";
}
break;
}
}
@@ -931,11 +947,11 @@ bool parse() {
// Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc);
if(error) { std::cerr << error << std::endl; return false; }
ondemand::array cars;
ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars);
for (auto car_value : cars) {
ondemand::object car;
ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car);
if(error) { std::cerr << error << std::endl; return false; }
@@ -972,6 +988,19 @@ bool parse() {
}
```
For safety, you should only use our ondemand instances (e.g., `ondemand::object`)
after you have initialized them and checked that there is no error:
```c++
ondemand::object car; // invalid until the get() succeeds
// the `car` instance should not use used before it is initialized
error = car_value.get_object().get(car);
if(error) {
// the `car` instance should not use used
} else {
// the `car` instance can be safely used
}
```
The following examples illustrates how to iterate through the content of an object without
having to handle exceptions.
@@ -981,7 +1010,7 @@ having to handle exceptions.
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { return false; }
ondemand::object object;
ondemand::object object; // invalid until the get() succeeds
error = doc.get_object().get(object);
if(error) { return false; }
for(auto field : object) {
@@ -1010,7 +1039,7 @@ target_compile_definitions(simdjson PUBLIC SIMDJSON_EXCEPTIONS=OFF)
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```c++
simdjson::ondemande::document doc = parser.iterate(json); // Throws an exception if there was an error!
simdjson::ondemand::document doc = parser.iterate(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
@@ -1097,6 +1126,9 @@ for (auto val : doc) {
std::cout << doc.current_location() << std::endl; // Throws OUT_OF_BOUNDS
```
Conversely, if `doc.current_location().error() == simdjson::SUCCESS`,
then the document has more content.
Finally, the `current_location()` method may also be used even when no exceptions/errors
are thrown. This can be helpful for users that want to know the current state of iteration during parsing. For example:
@@ -1105,7 +1137,7 @@ auto json = R"( [[1,2,3], -23.4, {"key": "value"}, true] )"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
for (auto val : doc) {
ondemand::object obj;
ondemand::object obj; // invalid until the get() succeeds
auto error = val.get_object().get(obj); // Only get objects
if (!error) {
std::cout << doc.current_location() << std::endl; // Prints ""key": "value"}, true] "
@@ -1518,7 +1550,7 @@ Standard Compliance
The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification.
- The only insignificant whitespace characters allowed are the space, the horizontal tab, the line feed and the carriage return. In particular, a JSON document may not contain an unespaced null character.
- The only insignificant whitespace characters allowed are the space, the horizontal tab, the line feed and the carriage return. In particular, a JSON document may not contain an unescaped null character.
- A single string or a single number is considered to be a valid JSON document.
- We fully validate the numbers according to the JSON specification. For example, the string `01` is not valid JSON document since the specification states that *leading zeros are not allowed*.
- The specification allows implementations to set limits on the range and precision of numbers accepted. We support 64-bit floating-point numbers as well as integer values.
+1 -1
View File
@@ -1,5 +1,5 @@
We take our documentation seriously. Please start reading the documentation before you attempt to use simdjson. We hope you will enjoy reading us.
* Basics: https://github.com/simdjson/simdjson/blob/master/doc/basics.md is an overview of how to use simdjson and its APIs.
* parse_many: https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md describes an interface providing features to work with files or streams containing multiple small JSON documents. As fast and convenient as possible.
* iterate_many: https://github.com/simdjson/simdjson/blob/master/doc/iterate_many.md describes an interface providing features to work with files or streams containing multiple small JSON documents. As fast and convenient as possible.
* Performance: https://github.com/simdjson/simdjson/blob/master/doc/performance.md shows some more advanced scenarios and how to tune for them.
+9 -9
View File
@@ -47,7 +47,7 @@ dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padde
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. The `element`, `object` or `array` instances are mere thin wrappers akin to an `std::vector<int>::iterator`: they are invalid when default constructed, they must be tied to a valid document instance. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
@@ -167,7 +167,7 @@ While the simdjson library can be used in any project using C++ 11 and above, fi
```c++
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
dom::object object; // invalid until the get() succeeds
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
@@ -181,7 +181,7 @@ For comparison, here is the C++ 11 version of the same code:
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
dom::object object; // invalid until the get() succeeds
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
@@ -227,7 +227,7 @@ dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
for (dom::element car_element : cars) {
dom::object car;
dom::object car; // invalid until the get() succeeds
simdjson::error_code error;
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return; }
double x = car.at_pointer("/tire_pressure/1");
@@ -283,11 +283,11 @@ it selects the key "count" within that object.
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
simdjson::dom::element res; // invalid until the get() succeeds
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
@@ -311,7 +311,7 @@ Observe how we use the `at` method when querying an index into an array, and not
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
@@ -336,7 +336,7 @@ auto cars_json = R"( [
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::array cars;
dom::array cars; // invalid until the get() succeeds
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
@@ -382,7 +382,7 @@ auto abstract_json = R"( [
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
dom::array array; // invalid until after the next line
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
+7
View File
@@ -322,4 +322,11 @@ namespace std {
# define simdjson_fallthrough do {} while (0) /* fallthrough */
#endif // simdjson_fallthrough
#if SIMDJSON_DEVELOPMENT_CHECKS
#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { assert ((expr)); } while (0)
#else
#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { } while (0)
#endif
#endif // SIMDJSON_COMMON_DEFS_H
+6
View File
@@ -53,18 +53,23 @@ namespace dom {
simdjson_inline array::array() noexcept : tape{} {}
simdjson_inline array::array(const internal::tape_ref &_tape) noexcept : tape{_tape} {}
inline array::iterator array::begin() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.json_index + 1);
}
inline array::iterator array::end() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.after_element() - 1);
}
inline size_t array::size() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.scope_count();
}
inline size_t array::number_of_slots() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.matching_brace_index() - tape.json_index;
}
inline simdjson_result<element> array::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(json_pointer.empty()) { // an empty string means that we return the current node
return element(this->tape); // copy the current node
} else if(json_pointer[0] != '/') { // otherwise there is an error
@@ -105,6 +110,7 @@ inline simdjson_result<element> array::at_pointer(std::string_view json_pointer)
}
inline simdjson_result<element> array::at(size_t index) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i=0;
for (auto element : *this) {
if (i == index) { return element; }
+12
View File
@@ -186,11 +186,13 @@ simdjson_inline element::element() noexcept : tape{} {}
simdjson_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline element_type element::type() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
auto tape_type = tape.tape_ref_type();
return tape_type == internal::tape_type::FALSE_VALUE ? element_type::BOOL : static_cast<element_type>(tape_type);
}
inline simdjson_result<bool> element::get_bool() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(tape.is_true()) {
return true;
} else if(tape.is_false()) {
@@ -199,6 +201,7 @@ inline simdjson_result<bool> element::get_bool() const noexcept {
return INCORRECT_TYPE;
}
inline simdjson_result<const char *> element::get_c_str() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING: {
return tape.get_c_str();
@@ -208,6 +211,7 @@ inline simdjson_result<const char *> element::get_c_str() const noexcept {
}
}
inline simdjson_result<size_t> element::get_string_length() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING: {
return tape.get_string_length();
@@ -217,6 +221,7 @@ inline simdjson_result<size_t> element::get_string_length() const noexcept {
}
}
inline simdjson_result<std::string_view> element::get_string() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING:
return tape.get_string_view();
@@ -225,6 +230,7 @@ inline simdjson_result<std::string_view> element::get_string() const noexcept {
}
}
inline simdjson_result<uint64_t> element::get_uint64() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(simdjson_unlikely(!tape.is_uint64())) { // branch rarely taken
if(tape.is_int64()) {
int64_t result = tape.next_tape_value<int64_t>();
@@ -238,6 +244,7 @@ inline simdjson_result<uint64_t> element::get_uint64() const noexcept {
return tape.next_tape_value<int64_t>();
}
inline simdjson_result<int64_t> element::get_int64() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(simdjson_unlikely(!tape.is_int64())) { // branch rarely taken
if(tape.is_uint64()) {
uint64_t result = tape.next_tape_value<uint64_t>();
@@ -252,6 +259,7 @@ inline simdjson_result<int64_t> element::get_int64() const noexcept {
return tape.next_tape_value<int64_t>();
}
inline simdjson_result<double> element::get_double() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
// Performance considerations:
// 1. Querying tape_ref_type() implies doing a shift, it is fast to just do a straight
// comparison.
@@ -273,6 +281,7 @@ inline simdjson_result<double> element::get_double() const noexcept {
return tape.next_tape_value<double>();
}
inline simdjson_result<array> element::get_array() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_ARRAY:
return array(tape);
@@ -281,6 +290,7 @@ inline simdjson_result<array> element::get_array() const noexcept {
}
}
inline simdjson_result<object> element::get_object() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape);
@@ -360,6 +370,7 @@ inline simdjson_result<element> element::operator[](const char *key) const noexc
}
inline simdjson_result<element> element::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape).at_pointer(json_pointer);
@@ -395,6 +406,7 @@ inline simdjson_result<element> element::at_key_case_insensitive(std::string_vie
}
inline bool element::dump_raw_tape(std::ostream &out) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.doc->dump_raw_tape(out);
}
+4
View File
@@ -65,12 +65,15 @@ namespace dom {
simdjson_inline object::object() noexcept : tape{} {}
simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline object::iterator object::begin() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.json_index + 1);
}
inline object::iterator object::end() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.after_element() - 1);
}
inline size_t object::size() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.scope_count();
}
@@ -81,6 +84,7 @@ inline simdjson_result<element> object::operator[](const char *key) const noexce
return at_key(key);
}
inline simdjson_result<element> object::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(json_pointer.empty()) { // an empty string means that we return the current node
return element(this->tape); // copy the current node
} else if(json_pointer[0] != '/') { // otherwise there is an error
+10 -1
View File
@@ -42,7 +42,13 @@ inline simdjson_result<size_t> parser::read_file(const std::string &path) noexce
}
// Get the file size
if(std::fseek(fp, 0, SEEK_END) < 0) {
int ret;
#if defined(SIMDJSON_VISUAL_STUDIO) && !SIMDJSON_IS_32BITS
ret = _fseeki64(fp, 0, SEEK_END);
#else
ret = std::fseek(fp, 0, SEEK_END);
#endif // _WIN64
if(ret < 0) {
std::fclose(fp);
return IO_ERROR;
}
@@ -142,6 +148,9 @@ simdjson_inline simdjson_result<element> parser::parse(const std::string &s) & n
simdjson_inline simdjson_result<element> parser::parse(const padded_string &s) & noexcept {
return parse(s.data(), s.length(), false);
}
simdjson_inline simdjson_result<element> parser::parse(const padded_string_view &v) & noexcept {
return parse(v.data(), v.length(), false);
}
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
+3
View File
@@ -199,6 +199,9 @@ public:
/** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse(const padded_string &s) & noexcept;
simdjson_inline simdjson_result<element> parse(const padded_string &s) && =delete;
/** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse(const padded_string_view &v) & noexcept;
simdjson_inline simdjson_result<element> parse(const padded_string_view &v) && =delete;
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_inline simdjson_result<element> parse(const char *buf) noexcept = delete;
+2 -1
View File
@@ -18,7 +18,7 @@ enum error_code {
SUCCESS = 0, ///< No error
CAPACITY, ///< This parser can't support a document that big
MEMALLOC, ///< Error allocating memory, most likely out of memory
TAPE_ERROR, ///< Something went wrong while writing to the tape (stage 2), this is a generic error
TAPE_ERROR, ///< Something went wrong, this is a generic error
DEPTH_ERROR, ///< Your document exceeds the user-specified depth limitation
STRING_ERROR, ///< Problem while parsing a string
T_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 't'
@@ -45,6 +45,7 @@ enum error_code {
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early.
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
NUM_ERROR_CODES
};
+22 -21
View File
@@ -112,7 +112,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = 0.0;
d = negative ? -0.0 : 0.0;
return true;
}
@@ -227,7 +227,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = 0.0;
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
@@ -497,7 +497,8 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative,
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
WRITE_DOUBLE(0, src, writer);
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
@@ -549,7 +550,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + negative;
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
@@ -846,7 +847,7 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + negative;
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
@@ -890,7 +891,7 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + negative;
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
@@ -932,19 +933,19 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
const uint8_t *p = src + negative + 1;
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
const uint8_t *const start_digits = src;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
while (parse_digit(*src, i)) { src++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
@@ -956,11 +957,11 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*p != '"') { return NUMBER_ERROR; }
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
@@ -973,7 +974,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
// Check for minus sign
//
bool negative = (*src == '-');
src += negative;
src += uint8_t(negative);
//
// Parse the integer part.
@@ -1040,7 +1041,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src-negative, &d)) {
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
@@ -1052,7 +1053,7 @@ simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += negative;
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
@@ -1062,7 +1063,7 @@ simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *
simdjson_unused simdjson_inline simdjson_result<ondemand::number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += negative;
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
@@ -1092,7 +1093,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
// Check for minus sign
//
bool negative = (*src == '-');
src += negative;
src += uint8_t(negative);
//
// Parse the integer part.
@@ -1161,7 +1162,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src-negative, src_end, &d)) {
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
@@ -1172,7 +1173,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += negative + 1;
src += uint8_t(negative) + 1;
//
// Parse the integer part.
@@ -1239,7 +1240,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src-negative, &d)) {
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
@@ -94,7 +94,7 @@ simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string()
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool();
}
simdjson_inline bool document::is_null() noexcept {
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null();
}
@@ -354,7 +354,7 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_null() noexcept {
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
@@ -504,7 +504,7 @@ simdjson_inline simdjson_result<std::string_view> document_reference::get_string
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_raw_json_string(); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_bool(); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline bool document_reference::is_null() noexcept { return doc->is_null(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->is_null(); }
#if SIMDJSON_EXCEPTIONS
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
@@ -636,7 +636,7 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_null() noexcept {
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
@@ -648,7 +648,7 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
+13 -8
View File
@@ -126,11 +126,14 @@ public:
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null.
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
@@ -363,7 +366,9 @@ public:
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
/**
* Get the type of this JSON value.
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
@@ -595,7 +600,7 @@ public:
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline operator document&() const noexcept;
@@ -660,7 +665,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
@@ -727,7 +732,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
@@ -754,8 +759,8 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> get_number() noexcept;
@@ -154,6 +154,10 @@ simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
@@ -321,6 +325,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
@@ -331,11 +336,13 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
return parser->start_positions[depth];
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
parser->start_positions[depth] = position;
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
@@ -117,6 +117,14 @@ public:
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
@@ -152,7 +152,7 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5u ", depth);
printf("| %5i ", depth);
printf("| %.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
@@ -109,11 +109,10 @@ simdjson_inline size_t parser::max_depth() const noexcept {
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
size_t MINIMAL_DOCUMENT_CAPACITY = 32;
if(max_capacity < MINIMAL_DOCUMENT_CAPACITY) {
if(max_capacity < dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = MINIMAL_DOCUMENT_CAPACITY;
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
+10 -1
View File
@@ -230,13 +230,22 @@ public:
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/** The maximum depth of this parser (the most deeply nested objects and arrays it can process). */
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
@@ -4,23 +4,27 @@
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again.
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously.
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again.
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again.
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> x);
@@ -39,7 +43,8 @@ inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON
namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand {
/**
* Print JSON to an output stream.
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
@@ -50,7 +55,8 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTA
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value> x);
#endif
/**
* Print JSON to an output stream.
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
@@ -61,7 +67,8 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTA
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array> x);
#endif
/**
* Print JSON to an output stream.
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
@@ -76,7 +83,8 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_IMPLEMENTA
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream.
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
@@ -54,10 +54,9 @@ simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline bool value::is_null() noexcept {
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
@@ -311,8 +310,8 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_null() noexcept {
if (error()) { return false; }
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
@@ -404,7 +403,7 @@ simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENT
return first.current_location();
}
simdjson_inline int32_t simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::current_depth() const noexcept {
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
+10 -5
View File
@@ -150,11 +150,14 @@ public:
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null.
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
@@ -334,7 +337,9 @@ public:
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
@@ -583,7 +588,7 @@ public:
simdjson_inline simdjson_result<std::string_view> get_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline bool is_null() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() noexcept;
@@ -676,7 +681,7 @@ public:
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
};
@@ -468,8 +468,11 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_inline bool value_iterator::parse_null(const uint8_t *json) const noexcept {
return !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string() noexcept {
@@ -516,10 +519,11 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_b
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline bool value_iterator::is_null() noexcept {
auto result = parse_null(peek_non_root_scalar("null"));
if(result) { advance_non_root_scalar("null"); }
return result;
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
@@ -547,10 +551,16 @@ simdjson_inline simdjson_result<bool> value_iterator::is_root_integer() noexcept
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
return numberparsing::is_integer(tmpbuf);
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if((answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type> value_iterator::get_root_number_type() noexcept {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
auto max_len = peek_start_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
@@ -561,7 +571,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number_type>
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
return numberparsing::get_number_type(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
auto answer = numberparsing::get_number_type(tmpbuf);
if((answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number() noexcept {
auto max_len = peek_start_length();
@@ -577,6 +592,7 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number() noexce
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
@@ -596,7 +612,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("uint64"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string() noexcept {
@@ -608,7 +627,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("uint64"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64() noexcept {
@@ -621,7 +643,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("int64"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string() noexcept {
@@ -634,7 +659,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("int64"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double() noexcept {
@@ -649,7 +677,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("double"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
@@ -665,7 +696,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("double"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool() noexcept {
@@ -674,10 +708,15 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
uint8_t tmpbuf[5+1];
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { return incorrect_type_error("Not a boolean"); }
auto result = parse_bool(tmpbuf);
if(result.error() == SUCCESS) { advance_root_scalar("bool"); }
if(result.error() == SUCCESS) {
if (!_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
}
return result;
}
simdjson_inline bool value_iterator::is_root_null() noexcept {
// If there is trailing content, then the document is not null.
if (!_json_iter->is_single_token()) { return false; }
auto max_len = peek_start_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
@@ -289,7 +289,7 @@ public:
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline bool is_null() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
@@ -350,7 +350,7 @@ protected:
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline bool parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
+5 -6
View File
@@ -57,7 +57,7 @@ public:
* const implementation *impl = simdjson::get_active_implementation();
* cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
*
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64".
*/
virtual const std::string &name() const { return _name; }
@@ -67,7 +67,7 @@ public:
* const implementation *impl = simdjson::get_active_implementation();
* cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
*
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
* @return the description of the implementation, e.g. "Intel/AMD AVX2", "Intel/AMD SSE4.2", "ARM NEON".
*/
virtual const std::string &description() const { return _description; }
@@ -76,8 +76,7 @@ public:
* and the current CPU match. This function may poll the current CPU/system
* and should therefore not be called too often if performance is a concern.
*
*
* @return true if the implementation can be safely used on the current system (determined at runtime)
* @return true if the implementation can be safely used on the current system (determined at runtime).
*/
bool supported_by_runtime_system() const;
@@ -86,7 +85,7 @@ public:
*
* The instruction sets this implementation is compiled against.
*
* @return a mask of all required `internal::instruction_set::` values
* @return a mask of all required `internal::instruction_set::` values.
*/
virtual uint32_t required_instruction_sets() const { return _required_instruction_sets; };
@@ -99,7 +98,7 @@ public:
* @param capacity The largest document that will be passed to the parser.
* @param max_depth The maximum JSON object/array nesting this parser is expected to handle.
* @param dst The place to put the resulting parser implementation.
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
* @return the error code, or SUCCESS if there was no error.
*/
virtual error_code create_dom_parser_implementation(
size_t capacity,
+1 -1
View File
@@ -115,7 +115,7 @@ constexpr uint32_t cpuid_avx512pf_bit = 1 << 26; ///< @private bit 26 of EBX
constexpr uint32_t cpuid_avx512er_bit = 1 << 27; ///< @private bit 27 of EBX for EAX=0x7
constexpr uint32_t cpuid_avx512cd_bit = 1 << 28; ///< @private bit 28 of EBX for EAX=0x7
constexpr uint32_t cpuid_avx512bw_bit = 1 << 30; ///< @private bit 30 of EBX for EAX=0x7
constexpr uint32_t cpuid_avx512vl_bit = 1 << 31; ///< @private bit 31 of EBX for EAX=0x7
constexpr uint32_t cpuid_avx512vl_bit = 1U << 31; ///< @private bit 31 of EBX for EAX=0x7
constexpr uint32_t cpuid_avx512vbmi2_bit = 1 << 6; ///< @private bit 6 of ECX for EAX=0x7
constexpr uint32_t cpuid_sse42_bit = 1 << 20; ///< @private bit 20 of ECX for EAX=0x1
constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit 1 of ECX for EAX=0x1
+3 -1
View File
@@ -17,7 +17,9 @@ simdjson_inline tape_ref::tape_ref(const dom::document *_doc, size_t _json_index
simdjson_inline bool tape_ref::is_document_root() const noexcept {
return json_index == 1; // should we ever change the structure of the tape, this should get updated.
}
simdjson_inline bool tape_ref::usable() const noexcept {
return doc != nullptr; // when the document pointer is null, this tape_ref is uninitialized (should not be accessed).
}
// Some value types have a specific on-tape word value. It can be faster
// to check the type by doing a word-to-word comparison instead of extracting the
// most significant 8 bits.
+1
View File
@@ -38,6 +38,7 @@ public:
simdjson_inline const char * get_c_str() const noexcept;
inline std::string_view get_string_view() const noexcept;
simdjson_inline bool is_document_root() const noexcept;
simdjson_inline bool usable() const noexcept;
/** The document this element references. */
const dom::document *doc;
+8 -2
View File
@@ -45,7 +45,7 @@ inline char *allocate_padded_buffer(size_t length) noexcept {
} // namespace internal
inline padded_string::padded_string() noexcept {}
inline padded_string::padded_string() noexcept = default;
inline padded_string::padded_string(size_t length) noexcept
: viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
}
@@ -127,7 +127,13 @@ inline simdjson_result<padded_string> padded_string::load(std::string_view filen
}
// Get the file size
if(std::fseek(fp, 0, SEEK_END) < 0) {
int ret;
#if defined(SIMDJSON_VISUAL_STUDIO) && !SIMDJSON_IS_32BITS
ret = _fseeki64(fp, 0, SEEK_END);
#else
ret = std::fseek(fp, 0, SEEK_END);
#endif // _WIN64
if(ret < 0) {
std::fclose(fp);
return IO_ERROR;
}
+4 -4
View File
@@ -4,22 +4,22 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION 2.2.2
#define SIMDJSON_VERSION 3.0.1
namespace simdjson {
enum {
/**
* The major version (MAJOR.minor.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MAJOR = 2,
SIMDJSON_VERSION_MAJOR = 3,
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 2,
SIMDJSON_VERSION_MINOR = 0,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 2
SIMDJSON_VERSION_REVISION = 1
};
} // namespace simdjson
+11
View File
@@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=@PKGCONFIG_INCLUDEDIR@
libdir=@PKGCONFIG_LIBDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Cflags: -I${includedir} @PKGCONFIG_CFLAGS@
Libs: -L${libdir} -l@PROJECT_NAME@
@PKGCONFIG_LIBS_PRIVATE@
+140 -83
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2022-07-28 21:45:54 -0400. Do not edit! */
/* auto-generated on 2022-11-23 10:31:42 -0500. Do not edit! */
/* begin file src/simdjson.cpp */
#include "simdjson.h"
@@ -868,9 +868,9 @@ inline char *format_buffer(char *buf, int len, int decimal_exponent,
std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
// Make it look like a floating-point number (#362, #378)
// buf[n + 0] = '.';
// buf[n + 1] = '0';
return buf + (static_cast<size_t>(n));
buf[n + 0] = '.';
buf[n + 1] = '0';
return buf + (static_cast<size_t>(n)) + 2;
}
if (0 < n && n <= max_exp) {
@@ -933,10 +933,8 @@ char *to_chars(char *first, const char *last, double value) {
{
*first++ = '0';
// Make it look like a floating-point number (#362, #378)
if(negative) {
*first++ = '.';
*first++ = '0';
}
*first++ = '.';
*first++ = '0';
return first;
}
// Compute v = buffer * 10^decimal_exponent.
@@ -1589,7 +1587,8 @@ namespace internal {
{ INSUFFICIENT_PADDING, "simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." },
{ INCOMPLETE_ARRAY_OR_OBJECT, "JSON document ended early in the middle of an object or array." },
{ SCALAR_DOCUMENT_AS_VALUE, "A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "},
{ OUT_OF_BOUNDS, "Attempted to access location outside of document."}
{ OUT_OF_BOUNDS, "Attempted to access location outside of document."},
{ TRAILING_CONTENT, "Unexpected trailing content in the JSON input."}
}; // error_messages[]
} // namespace internal
@@ -3105,6 +3104,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -3114,12 +3121,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
@@ -3497,7 +3504,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
@@ -4189,22 +4196,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
@@ -5663,22 +5673,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
@@ -6883,6 +6896,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -6892,12 +6913,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
@@ -7277,7 +7298,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
@@ -8015,22 +8036,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
@@ -9266,6 +9290,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -9275,12 +9307,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
@@ -9658,7 +9690,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
@@ -10349,22 +10381,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
@@ -11563,6 +11598,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -11572,12 +11615,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
@@ -11955,7 +11998,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
@@ -12646,22 +12689,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
@@ -13895,6 +13941,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -13904,12 +13958,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
@@ -14287,7 +14341,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
@@ -14978,22 +15032,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
+333 -198
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -96,7 +96,7 @@ private:
*/
class json_scanner {
public:
json_scanner() {}
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish();
+11 -3
View File
@@ -162,6 +162,14 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
@@ -171,12 +179,12 @@ using namespace simd;
||(simd8x64<uint8_t>::NUM_CHUNKS == 2)
|| (simd8x64<uint8_t>::NUM_CHUNKS == 4),
"We support one, two or four chunks per 64-byte block.");
if(simd8x64<uint8_t>::NUM_CHUNKS == 1) {
SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 1) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
} if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
} else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
} else SIMDJSON_IF_CONSTEXPR (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+12 -9
View File
@@ -53,22 +53,25 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
const uint8_t *src_data = *src_ptr;
/* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */
if (((src_data[0] << 8) | src_data[1]) != ((static_cast<uint8_t> ('\\') << 8) | static_cast<uint8_t> ('u'))) {
return false;
}
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
// We have already checked that the high surrogate is valid and
// (code_point - 0xd800) < 1024.
//
// Check that code_point_2 is in the range 0xdc00..0xdfff
// and that code_point_2 was parsed from valid hex.
uint32_t low_bit = code_point_2 - 0xdc00;
if (low_bit >> 10) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
(((code_point - 0xd800) << 10) | low_bit) + 0x10000;
*src_ptr += 6;
} else if (code_point >= 0xdc00 && code_point <= 0xdfff) {
// If we encounter a low surrogate (not preceded by a high surrogate)
+2 -1
View File
@@ -33,7 +33,8 @@ namespace internal {
{ INSUFFICIENT_PADDING, "simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." },
{ INCOMPLETE_ARRAY_OR_OBJECT, "JSON document ended early in the middle of an object or array." },
{ SCALAR_DOCUMENT_AS_VALUE, "A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "},
{ OUT_OF_BOUNDS, "Attempted to access location outside of document."}
{ OUT_OF_BOUNDS, "Attempted to access location outside of document."},
{ TRAILING_CONTENT, "Unexpected trailing content in the JSON input."}
}; // error_messages[]
} // namespace internal
+5 -7
View File
@@ -860,9 +860,9 @@ inline char *format_buffer(char *buf, int len, int decimal_exponent,
std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
// Make it look like a floating-point number (#362, #378)
// buf[n + 0] = '.';
// buf[n + 1] = '0';
return buf + (static_cast<size_t>(n));
buf[n + 0] = '.';
buf[n + 1] = '0';
return buf + (static_cast<size_t>(n)) + 2;
}
if (0 < n && n <= max_exp) {
@@ -925,10 +925,8 @@ char *to_chars(char *first, const char *last, double value) {
{
*first++ = '0';
// Make it look like a floating-point number (#362, #378)
if(negative) {
*first++ = '.';
*first++ = '0';
}
*first++ = '.';
*first++ = '0';
return first;
}
// Compute v = buffer * 10^decimal_exponent.
+70 -2
View File
@@ -221,6 +221,64 @@ namespace number_tests {
std::vector<char> buf(1024);
simdjson::dom::parser parser;
bool is_pow_correct{1e-308 == std::pow(10,-308)};
int start_point = is_pow_correct ? -1000 : -307;
if(!is_pow_correct) {
std::cout << "On your system, the pow function is busted. Sorry about that. " << std::endl;
}
for (int i = start_point; i <= 308; ++i) {// large negative values should be zero.
size_t n = snprintf(buf.data(), buf.size(), "1e%d", i);
if (n >= buf.size()) { abort(); }
double actual;
auto error = parser.parse(buf.data(), n).get(actual);
if (error) { std::cerr << error << std::endl; return false; }
double expected = ((i >= -307) ? testing_power_of_ten[i + 307]: std::pow(10, i));
// In floating-point arithmetic, -0.0 == 0.0, so compare signs by checking the inverse of the numbers as well
if(actual!=expected || (actual == 0.0 && 1.0/actual!=1.0/expected)) {
std::cerr << "JSON '" << buf.data() << " parsed to ";
fprintf( stderr," %18.18g instead of %18.18g\n", actual, expected); // formatting numbers is easier with printf
SIMDJSON_SHOW_DEFINE(FLT_EVAL_METHOD);
return false;
}
}
printf("Powers of 10 can be parsed.\n");
return true;
}
bool negative_powers_of_ten() {
std::cout << __func__ << std::endl;
std::vector<char> buf(1024);
simdjson::dom::parser parser;
bool is_pow_correct{-1e-308 == -std::pow(10,-308)};
int start_point = is_pow_correct ? -1000 : -307;
if(!is_pow_correct) {
std::cout << "On your system, the pow function is busted. Sorry about that. " << std::endl;
}
for (int i = start_point; i <= 308; ++i) {// large negative values should be zero.
size_t n = snprintf(buf.data(), buf.size(), "-1e%d", i);
if (n >= buf.size()) { abort(); }
double actual;
auto error = parser.parse(buf.data(), n).get(actual);
if (error) { std::cerr << error << std::endl; return false; }
double expected = -(((i >= -307) ? testing_power_of_ten[i + 307]: std::pow(10, i)));
// In floating-point arithmetic, -0.0 == 0.0, so compare signs by checking the inverse of the numbers as well
if(actual!=expected || (actual == 0.0 && 1.0/actual!=1.0/expected)) {
std::cerr << "JSON '" << buf.data() << " parsed to ";
fprintf( stderr," %18.18g instead of %18.18g\n", actual, expected); // formatting numbers is easier with printf
SIMDJSON_SHOW_DEFINE(FLT_EVAL_METHOD);
return false;
}
}
printf("Negative values of powers of 10 can be parsed.\n");
return true;
}
bool signed_zero_underflow_exponent() {
std::cout << __func__ << std::endl;
std::vector<char> buf(1024);
simdjson::dom::parser parser;
bool is_pow_correct{1e-308 == std::pow(10,-308)};
int start_point = is_pow_correct ? -1000 : -307;
if(!is_pow_correct) {
@@ -269,6 +327,13 @@ namespace number_tests {
std::cerr << error << std::endl;
return false;
}
if(std::signbit(actual) != std::signbit(val)) {
std::cerr << std::hexfloat << actual << " but I was expecting " << val
<< std::endl;
std::cerr << "string: " << vals << std::endl;
std::cout << std::dec;
return false;
}
if (actual != val) {
std::cerr << std::hexfloat << actual << " but I was expecting " << val
<< std::endl;
@@ -284,16 +349,18 @@ namespace number_tests {
std::cout << std::dec;
return true;
}
bool truncated_borderline() {
std::cout << __func__ << std::endl;
std::string round_to_even = "9007199254740993.0";
for(size_t i = 0; i < 1000; i++) { round_to_even += "0"; }
return basic_test_64bit(round_to_even,9007199254740992);
return basic_test_64bit(round_to_even, 9007199254740992);
}
bool specific_tests() {
std::cout << __func__ << std::endl;
return basic_test_64bit("-2402844368454405395.2",-2402844368454405395.2) &&
return basic_test_64bit("-1e-999", -0.0) &&
basic_test_64bit("-2402844368454405395.2",-2402844368454405395.2) &&
basic_test_64bit("4503599627370496.5", 4503599627370496.5) &&
basic_test_64bit("4503599627475352.5", 4503599627475352.5) &&
basic_test_64bit("4503599627475353.5", 4503599627475353.5) &&
@@ -315,6 +382,7 @@ namespace number_tests {
small_integers() &&
powers_of_two() &&
powers_of_ten() &&
negative_powers_of_ten() &&
nines();
}
}
+4
View File
@@ -732,6 +732,10 @@ namespace document_stream_tests {
}
count++;
}
if(count != 1) {
std::cerr << "bad count" << std::endl;
return false;
}
#endif
return true;
}
+5 -1
View File
@@ -160,7 +160,11 @@ namespace ondemand_treewalk {
cout << element.get_bool();
break;
case ondemand::json_type::null:
cout << "null";
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
cout << "null";
}
break;
}
}
+33 -12
View File
@@ -119,7 +119,9 @@ namespace error_tests {
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc) {
simdjson_result<ondemand::value> val = doc["val"];
// Get everything that can fail in both forward and backwards order
ASSERT_EQUAL( val.is_null(), false );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -131,15 +133,18 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), false );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc) {
ondemand::value val;
ASSERT_SUCCESS( doc["val"].get(val) );
// Get everything that can fail in both forward and backwards order
ASSERT_EQUAL( val.is_null(), false );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -151,14 +156,17 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), false );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
json = R"(true)"_padded;
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](simdjson_result<ondemand::document> val) {
// Get everything that can fail in both forward and backwards order
ASSERT_EQUAL( val.is_null(), false );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -170,14 +178,17 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), false );
ASSERT_SUCCESS( val.get_bool());
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc) {
ondemand::document val;
ASSERT_SUCCESS( std::move(doc).get(val) ); // Get everything that can fail in both forward and backwards order
ASSERT_EQUAL( val.is_null(), false );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -189,8 +200,9 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), false );
ASSERT_SUCCESS( val.get_bool() );
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, false );
TEST_SUCCEED();
}));
@@ -216,13 +228,16 @@ namespace error_tests {
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc) {
ondemand::value val;
ASSERT_SUCCESS( doc["val"].get(val) );
// Get everything that can fail in both forward and backwards order
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
@@ -236,7 +251,9 @@ namespace error_tests {
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
json = R"(null)"_padded;
@@ -255,7 +272,9 @@ namespace error_tests {
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc) {
@@ -274,7 +293,9 @@ namespace error_tests {
ASSERT_ERROR( val.get_string(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_array(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_bool(), INCORRECT_TYPE );
ASSERT_EQUAL( val.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( val.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
TEST_SUCCEED();
}));
TEST_SUCCEED();
@@ -71,7 +71,7 @@ namespace json_pointer_tests {
ASSERT_SUCCESS(val.get_bool().error());
break;
case ondemand::json_type::null:
ASSERT_TRUE(val.is_null());
ASSERT_SUCCESS(val.is_null().error());
break;
default:
TEST_FAIL("unexpected type");
+42 -1
View File
@@ -63,6 +63,42 @@ namespace misc_tests {
TEST_SUCCEED();
}
// Test a surrogate pair with the low surrogate out of range
bool issue1894() {
TEST_START();
ondemand::parser parser;
auto json = R"("\uD888\u1234")"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR(doc.get_string().get(view), STRING_ERROR);
TEST_SUCCEED();
}
bool issue1894toolarge() {
TEST_START();
ondemand::parser parser;
auto json = R"("\uD888\uE000")"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_ERROR(doc.get_string().get(view), STRING_ERROR);
TEST_SUCCEED();
}
// Test the smallest surrogate pair, largest surrogate pair, and a surrogate pair in range.
bool issue1894success() {
TEST_START();
ondemand::parser parser;
auto json = R"("\uD888\uDC00\uD800\uDC00\uDBFF\uDFFF")"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
std::string_view view;
ASSERT_SUCCESS(doc.get_string().get(view));
ASSERT_EQUAL(view, "\xf0\xb2\x80\x80\xf0\x90\x80\x80\xf4\x8f\xbf\xbf");
TEST_SUCCEED();
}
bool issue1660() {
TEST_START();
ondemand::parser parser;
@@ -235,7 +271,9 @@ namespace misc_tests {
ASSERT_SUCCESS(global.get(global_object));
ondemand::value shadowable;
ASSERT_SUCCESS(global_object["shadowable"].get(shadowable));
ASSERT_TRUE(!shadowable.is_null());
bool is_null_value;
ASSERT_SUCCESS(shadowable.is_null().get(is_null_value));
ASSERT_TRUE(!is_null_value);
ondemand::value badvalue;
auto error = global_object["writable"].get(badvalue);
if(error == SUCCESS) {
@@ -459,6 +497,9 @@ namespace misc_tests {
bool run() {
return
issue1870() &&
issue1894() &&
issue1894toolarge() &&
issue1894success() &&
is_alive_root_array() &&
is_alive_root_object() &&
is_alive_array() &&
+72 -1
View File
@@ -170,6 +170,20 @@ namespace number_tests {
simdjson_unused auto blah2=blah.get(x);
}
bool issue_1898() {
TEST_START();
padded_string negative_zero_string(std::string_view("-1e-999"));
simdjson::ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(negative_zero_string).get(doc));
double x;
ASSERT_SUCCESS(doc.get(x));
// should be minus 0
ASSERT_TRUE(std::signbit(x));
ASSERT_TRUE(x == -0);
TEST_SUCCEED();
}
bool old_crashes() {
TEST_START();
github_issue_1273();
@@ -264,6 +278,61 @@ namespace number_tests {
TEST_SUCCEED();
}
bool issue1878() {
TEST_START();
ondemand::parser parser;
auto json = R"(123_abc)"_padded;
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_int64().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_int64().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_uint64().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_uint64().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_double().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_double().error(), TRAILING_CONTENT);
}
for (char ch : {'_', '%', 'z', '&', '\\', '/', '*'}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_number().error(), NUMBER_ERROR);
}
for (char ch : {'[', ']', '{', '}', ',', ' '}) {
json.data()[3] = ch;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_ERROR(doc.get_number().error(), TRAILING_CONTENT);
}
TEST_SUCCEED();
}
bool get_root_number_tests() {
TEST_START();
ondemand::parser parser;
@@ -321,7 +390,9 @@ namespace number_tests {
TEST_SUCCEED();
}
bool run() {
return get_root_number_tests() &&
return issue_1898() &&
issue1878() &&
get_root_number_tests() &&
get_number_tests()&&
small_integers() &&
powers_of_two() &&
+10 -2
View File
@@ -87,7 +87,11 @@ void recursive_print_json(ondemand::value element) {
cout << element.get_bool();
break;
case ondemand::json_type::null:
cout << "null";
// we check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
cout << "null";
}
break;
}
}
@@ -166,7 +170,11 @@ void recursive_print_json_breakline(ondemand::value element) {
cout << element.get_bool();
break;
case ondemand::json_type::null:
cout << "null";
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
cout << "null";
}
break;
}
}
+12 -4
View File
@@ -206,11 +206,15 @@ namespace scalar_tests {
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( doc.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( doc_result.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
return true;
}));
json = "[null]"_padded;
@@ -219,7 +223,9 @@ namespace scalar_tests {
for (auto value_result : doc_result) {
ondemand::value value;
ASSERT_SUCCESS( value_result.get(value) );
ASSERT_EQUAL( value.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( value.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
count++;
}
ASSERT_EQUAL( count, 1 );
@@ -228,7 +234,9 @@ namespace scalar_tests {
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
int count = 0;
for (auto value_result : doc_result) {
ASSERT_EQUAL( value_result.is_null(), true );
bool is_null_value;
ASSERT_SUCCESS( value_result.is_null().get(is_null_value) );
ASSERT_EQUAL( is_null_value, true );
count++;
}
ASSERT_EQUAL( count, 1 );
+12
View File
@@ -63,7 +63,19 @@ int main(int argc, const char *argv[]) {
simdjson::ondemand::document doc;
error = parser.iterate(docdata).get(doc);
if(error != simdjson::SUCCESS) { std::cout << error << std::endl; return EXIT_FAILURE; }
// This locates the document and captures a string_view instance, it does
// not validate the document:
std::cout << doc;
// check if there is more content
const char * endofstream;
if(doc.current_location().get(endofstream) == simdjson::SUCCESS) {
// there is more content !
// let us find what it is:
size_t len = docdata.data() + docdata.size() - endofstream;
std::string_view content{endofstream, len};
std::cerr << "\nThere is leftover content: '" << content << "'" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
simdjson::dom::parser parser;