Merge pull request #1342 from simdjson/jkeiser/iter-safety

Safety: assert in debug mode when on demand values are used out of order
This commit is contained in:
John Keiser 2020-12-16 15:58:29 -08:00 committed by GitHub
commit f785f76d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 450 additions and 248 deletions

View File

@ -19,7 +19,7 @@ environment:
- job_name: VS2017 (Static, No Threads)
image: Visual Studio 2017
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
CTEST_ARGS: -LE explicitonly
- job_name: VS2019 (Win32)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
@ -31,7 +31,7 @@ environment:
- job_name: VS2015
image: Visual Studio 2015
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
CTEST_ARGS: -LE explicitonly
build_script:
- mkdir build

View File

@ -103,7 +103,14 @@ commands:
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L acceptance &&
ctest $CTEST_FLAGS -LE acceptance -E checkperf
ctest $CTEST_FLAGS -LE acceptance -LE explicitonly
cmake_assert_test:
steps:
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L assert
cmake_test_all:
steps:
@ -112,9 +119,9 @@ commands:
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -DSIMDJSON_IMPLEMENTATION="haswell;westmere;fallback" -L acceptance -LE per_implementation &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
@ -144,6 +151,16 @@ jobs:
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_JUST_LIBRARY=ON }
steps: [ cmake_build, cmake_install_test, cmake_installed_test_cxx20 ]
assert-gcc10:
description: Build the library with asserts on, install it and run tests
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
steps: [ cmake_test, cmake_assert_test ]
assert-clang10:
description: Build just the library, install it and do a basic test
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
steps: [ cmake_test, cmake_assert_test ]
gcc10-perftest:
description: Build and run performance tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
executor: gcc10
@ -174,22 +191,22 @@ jobs:
sanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
@ -245,12 +262,12 @@ jobs:
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
workflows:
@ -292,4 +309,8 @@ workflows:
# testing "just the library"
- justlib-gcc10
# testing asserts
- assert-gcc10
- assert-clang10
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows

View File

@ -23,4 +23,4 @@ task:
- make
test_script:
- cd build
- ctest --output-on-failure -E checkperf
- ctest --output-on-failure -LE explicitonly

View File

@ -9,7 +9,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y g++ cmake gcc git
@ -30,7 +30,7 @@ steps:
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y clang++-6.0 cmake git
@ -51,7 +51,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
@ -78,7 +78,7 @@ steps:
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- mkdir build
- cd build
@ -101,7 +101,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
@ -124,7 +124,7 @@ steps:
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- mkdir build
- cd build
@ -143,7 +143,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
@ -170,7 +170,7 @@ steps:
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- mkdir build
- cd build
@ -194,7 +194,7 @@ steps:
CXX: clang++-11
CMAKE_FLAGS: -GNinja
BUILD_FLAGS:
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -std=c++20 -stdlib=libc++
commands:
- mkdir build
@ -214,7 +214,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
@ -239,7 +239,7 @@ steps:
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
@ -261,7 +261,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
@ -283,7 +283,7 @@ steps:
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
@ -303,7 +303,7 @@ steps:
environment:
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CC: gcc
CXX: g++
commands:
@ -331,7 +331,7 @@ steps:
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
@ -357,7 +357,7 @@ steps:
CXX: clang++-9
BUILD_FLAGS: -- -j 4
CMAKE_FLAGS: -GNinja -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
- mkdir build
@ -378,7 +378,7 @@ steps:
CXX: clang++-9
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
- mkdir build
@ -399,7 +399,7 @@ steps:
CXX: clang++-7
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
- mkdir build
@ -419,7 +419,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq

View File

@ -34,4 +34,4 @@ jobs:
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -E checkperf"
./alpine.sh bash -c "cd build_for_alpine && ctest -LE explicitonly"

View File

@ -51,4 +51,4 @@ jobs:
cd build
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -E checkperf
ctest -j4 --output-on-failure -LE explicitonly

View File

@ -23,6 +23,6 @@ jobs:
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -E checkperf &&
ctest -j --output-on-failure -LE explicitonly &&
make 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

View File

@ -23,6 +23,6 @@ jobs:
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -E checkperf &&
ctest -j --output-on-failure -LE explicitonly &&
make 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

View File

@ -31,6 +31,6 @@ jobs:
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -E checkperf --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"

View File

@ -31,5 +31,5 @@ jobs:
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -E checkperf --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"

View File

@ -31,6 +31,6 @@ jobs:
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -E checkperf --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"

View File

@ -176,7 +176,7 @@ install:
- if [[ "${STATIC}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON";
fi
- export CTEST_FLAGS="-j4 --output-on-failure -E checkperf"
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
script:
- mkdir build

View File

@ -41,7 +41,7 @@
#
# Next you can test it as follows:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -E checkperf"
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -LE explicitonly"
#
# The run the complete tests requires you to have built all of simdjson.
#

View File

@ -89,7 +89,7 @@ if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_G
# COMMAND ECHO $<TARGET_FILE:perfdiff> \"$<TARGET_FILE:parse> -t ${SIMDJSON_CHECKPERF_ARGS}\" \"${CHECKPERF_PARSE} -t ${SIMDJSON_CHECKPERF_ARGS}\" }
COMMAND $<TARGET_FILE:perfdiff> $<TARGET_FILE:parse> ${CHECKPERF_PARSE} -H -t ${SIMDJSON_CHECKPERF_ARGS}
)
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation)
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation explicitonly)
set_property(TEST checkperf APPEND PROPERTY DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
else()

View File

@ -72,24 +72,24 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
option(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING "Under Visual Studio, add Zi to the compile flag and DEBUG to the link file to add debugging information to the release build for easier profiling inside tools like VTune" OFF)
if(MSVC)
if("${MSVC_TOOLSET_VERSION}" STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it, cmake -G"Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
else()
# Recent version of Visual Studio expected (2017, 2019...). Prior versions are unsupported.
target_compile_options(simdjson-internal-flags INTERFACE /WX /W3 /sdl /w34714) # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019
endif()
if(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
target_link_options(simdjson-flags INTERFACE /DEBUG )
target_compile_options(simdjson-flags INTERFACE /Zi)
endif()
else()
if("${MSVC_TOOLSET_VERSION}" STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it, cmake -G"Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
else()
# Recent version of Visual Studio expected (2017, 2019...). Prior versions are unsupported.
target_compile_options(simdjson-internal-flags INTERFACE /WX /W3 /sdl /w34714) # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019
endif()
if(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
target_link_options(simdjson-flags INTERFACE /DEBUG )
target_compile_options(simdjson-flags INTERFACE /Zi)
endif(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
else(MSVC)
if(NOT WIN32)
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
endif()
target_compile_options(simdjson-internal-flags INTERFACE -Werror -Wall -Wextra -Weffc++)
target_compile_options(simdjson-internal-flags INTERFACE -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion)
endif()
endif(MSVC)
#
# Optional flags
@ -232,5 +232,9 @@ if(${CMAKE_C_COMPILER_ID} MATCHES "Intel") # icc / icpc
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-intel")
endif()
include (CheckSymbolExists)
CHECK_SYMBOL_EXISTS(fork unistd.h HAVE_POSIX_FORK)
CHECK_SYMBOL_EXISTS(wait sys/wait.h HAVE_POSIX_WAIT)
install(TARGETS simdjson-flags EXPORT simdjson-config)
install(TARGETS simdjson-internal-flags EXPORT simdjson-config)

View File

@ -5,13 +5,6 @@ namespace SIMDJSON_IMPLEMENTATION {
// internal::implementation_simdjson_result_base<T> inline implementation
//
/**
* Create a new empty result with error = UNINITIALIZED.
*/
template<typename T>
simdjson_really_inline implementation_simdjson_result_base<T>::~implementation_simdjson_result_base() noexcept {
}
template<typename T>
simdjson_really_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
// on the clang compiler that comes with current macOS (Apple clang version 11.0.0),
@ -70,9 +63,6 @@ simdjson_really_inline implementation_simdjson_result_base<T>::implementation_si
template<typename T>
simdjson_really_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
template<typename T>
simdjson_really_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base() noexcept
: implementation_simdjson_result_base(T{}, UNINITIALIZED) {}
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -30,7 +30,7 @@ struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_really_inline implementation_simdjson_result_base() noexcept;
simdjson_really_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
@ -47,21 +47,6 @@ struct implementation_simdjson_result_base {
*/
simdjson_really_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move a result.
*/
simdjson_really_inline implementation_simdjson_result_base(implementation_simdjson_result_base<T> &&value) noexcept = default;
/**
* Copy a result.
*/
simdjson_really_inline implementation_simdjson_result_base(const implementation_simdjson_result_base<T> &value) = default;
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_really_inline ~implementation_simdjson_result_base() noexcept;
/**
* Move the value and the error to the provided variables.
*
@ -114,8 +99,8 @@ struct implementation_simdjson_result_base {
#endif // SIMDJSON_EXCEPTIONS
T first;
error_code second;
T first{};
error_code second{UNINITIALIZED};
}; // struct implementation_simdjson_result_base
} // namespace SIMDJSON_IMPLEMENTATION

View File

@ -8,9 +8,6 @@ simdjson_really_inline document::document(ondemand::json_iterator &&_iter) noexc
logger::log_start_value(iter, "document");
}
simdjson_really_inline void document::assert_at_start() const noexcept {
iter.assert_at_start();
}
simdjson_really_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
@ -19,8 +16,11 @@ simdjson_really_inline value document::as_value() noexcept {
return as_value_iterator();
}
simdjson_really_inline value_iterator document::as_value_iterator() noexcept {
assert_at_start();
return value_iterator(&iter, 1);
iter.assert_at_root();
return value_iterator(&iter, 1, iter.root_checkpoint());
}
simdjson_really_inline value_iterator document::as_non_root_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_checkpoint());
}
simdjson_really_inline simdjson_result<array> document::get_array() & noexcept {
@ -30,15 +30,12 @@ simdjson_really_inline simdjson_result<object> document::get_object() & noexcept
return as_value().get_object();
}
simdjson_really_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
assert_at_start();
return as_value_iterator().require_root_uint64();
}
simdjson_really_inline simdjson_result<int64_t> document::get_int64() noexcept {
assert_at_start();
return as_value_iterator().require_root_int64();
}
simdjson_really_inline simdjson_result<double> document::get_double() noexcept {
assert_at_start();
return as_value_iterator().require_root_double();
}
simdjson_really_inline simdjson_result<std::string_view> document::get_string() & noexcept {
@ -48,11 +45,9 @@ simdjson_really_inline simdjson_result<raw_json_string> document::get_raw_json_s
return as_value().get_raw_json_string();
}
simdjson_really_inline simdjson_result<bool> document::get_bool() noexcept {
assert_at_start();
return as_value_iterator().require_root_bool();
}
simdjson_really_inline bool document::is_null() noexcept {
assert_at_start();
return as_value_iterator().is_root_null();
}
@ -95,10 +90,20 @@ simdjson_really_inline simdjson_result<array_iterator> document::end() & noexcep
return {};
}
simdjson_really_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return get_object()[key];
if (iter.at_root()) {
return get_object()[key];
} else {
// If we're not at the root, this is not the first key we've grabbed
return object::resume(as_non_root_value_iterator())[key];
}
}
simdjson_really_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return get_object()[key];
if (iter.at_root()) {
return get_object()[key];
} else {
// If we're not at the root, this is not the first key we've grabbed
return object::resume(as_non_root_value_iterator())[key];
}
}
} // namespace ondemand

View File

@ -20,17 +20,16 @@ class array_iterator;
*/
class document {
public:
simdjson_really_inline document(document &&other) noexcept = default;
simdjson_really_inline document &operator=(document &&other) noexcept = default;
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_really_inline document() noexcept = default;
simdjson_really_inline document(const document &other) = delete;
simdjson_really_inline document &operator=(const document &other) = delete;
simdjson_really_inline document(const document &other) noexcept = delete;
simdjson_really_inline document(document &&other) noexcept = default;
simdjson_really_inline document &operator=(const document &other) noexcept = delete;
simdjson_really_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
@ -230,10 +229,9 @@ protected:
simdjson_really_inline value as_value() noexcept;
simdjson_really_inline value_iterator as_value_iterator() noexcept;
simdjson_really_inline value_iterator as_non_root_value_iterator() noexcept;
static simdjson_really_inline document start(ondemand::json_iterator &&iter) noexcept;
simdjson_really_inline void assert_at_start() const noexcept;
//
// Fields
//
@ -261,10 +259,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> : public SIM
public:
simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::document &&value) noexcept; ///< @private
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> &&a) noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> get_array() & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> get_object() & noexcept;

View File

@ -90,11 +90,15 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
return report_error(TAPE_ERROR, "not enough close braces");
}
simdjson_really_inline bool json_iterator::at_start() const noexcept {
return token.index == &parser->dom_parser.structural_indexes[0];
simdjson_really_inline bool json_iterator::at_root() const noexcept {
return token.checkpoint() == root_checkpoint();
}
simdjson_really_inline void json_iterator::assert_at_start() const noexcept {
simdjson_really_inline const uint32_t *json_iterator::root_checkpoint() const noexcept {
return parser->dom_parser.structural_indexes.get();
}
simdjson_really_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
// Visual Studio Clang treats unique_ptr.get() as "side effecting."
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO

View File

@ -60,12 +60,17 @@ public:
/**
* Tell whether the iterator is still at the start
*/
simdjson_really_inline bool at_start() const noexcept;
simdjson_really_inline bool at_root() const noexcept;
/**
* Get the root value iterator
*/
simdjson_really_inline const uint32_t *root_checkpoint() const noexcept;
/**
* Assert if the iterator is not at the start
*/
simdjson_really_inline void assert_at_start() const noexcept;
simdjson_really_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
@ -184,8 +189,6 @@ public:
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator> &&a) noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson

View File

@ -43,18 +43,18 @@ namespace ondemand {
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
simdjson_warn_unused simdjson_really_inline error_code object::find_field_raw(const std::string_view key) noexcept {
return iter.find_field_raw(key);
}
simdjson_really_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
SIMDJSON_TRY( find_field_raw(key) );
return value(iter.iter.child());
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) { return NO_SUCH_FIELD; }
return value(iter.child());
}
simdjson_really_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
SIMDJSON_TRY( find_field_raw(key) );
return value(iter.iter.child());
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) { return NO_SUCH_FIELD; }
return value(iter.child());
}
simdjson_really_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
@ -71,6 +71,9 @@ simdjson_really_inline object object::started(value_iterator &iter) noexcept {
simdjson_unused bool has_value = iter.started_object();
return iter;
}
simdjson_really_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_really_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
@ -78,7 +81,8 @@ simdjson_really_inline object::object(const value_iterator &_iter) noexcept
}
simdjson_really_inline object_iterator object::begin() noexcept {
SIMDJSON_ASSUME( iter.at_start || iter.iter._json_iter->_depth < iter.iter._depth );
// Expanded version of SIMDJSON_ASSUME( iter.at_field_start() || !iter.is_open() )
SIMDJSON_ASSUME( (iter._json_iter->token.index == iter._start_index + 1) || (iter._json_iter->_depth < iter._depth) );
return iter;
}
simdjson_really_inline object_iterator object::end() noexcept {

View File

@ -49,11 +49,12 @@ protected:
static simdjson_really_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<object> try_start(value_iterator &iter) noexcept;
static simdjson_really_inline object started(value_iterator &iter) noexcept;
simdjson_really_inline object(const value_iterator &_iter) noexcept;
static simdjson_really_inline object resume(const value_iterator &iter) noexcept;
simdjson_really_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_really_inline error_code find_field_raw(const std::string_view key) noexcept;
object_iterator iter{};
value_iterator iter{};
friend class value;
friend class document;

View File

@ -7,8 +7,7 @@ namespace ondemand {
//
simdjson_really_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter},
at_start{true}
: iter{_iter}
{}
simdjson_really_inline simdjson_result<field> object_iterator::operator*() noexcept {
@ -80,39 +79,6 @@ simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
simdjson_warn_unused simdjson_really_inline error_code object_iterator::find_field_raw(const std::string_view key) noexcept {
if (!iter.is_open()) { return NO_SUCH_FIELD; }
// Unless this is the first field, we need to advance past the , and check for }
error_code error;
bool has_value;
if (at_start) {
at_start = false;
has_value = true;
} else {
if ((error = iter.skip_child() )) { iter.abandon(); return error; }
if ((error = iter.has_next_field().get(has_value) )) { iter.abandon(); return error; }
}
while (has_value) {
// Get the key
raw_json_string actual_key;
if ((error = iter.field_key().get(actual_key) )) { iter.abandon(); return error; };
if ((error = iter.field_value() )) { iter.abandon(); return error; }
// Check if it matches
if (actual_key == key) {
logger::log_event(iter, "match", key, -2);
return SUCCESS;
}
logger::log_event(iter, "no match", key, -2);
SIMDJSON_TRY( iter.skip_child() ); // Skip the value entirely
if ((error = iter.has_next_field().get(has_value) )) { iter.abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return NO_SUCH_FIELD;
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -29,11 +29,6 @@ public:
// Checks for ']' and ','
simdjson_really_inline object_iterator &operator++() noexcept;
/**
* Find the field with the given key. May be used in place of ++.
*/
simdjson_warn_unused simdjson_really_inline error_code find_field_raw(const std::string_view key) noexcept;
private:
/**
* The underlying JSON iterator.
@ -42,15 +37,6 @@ private:
* is first used, and never changes afterwards.
*/
value_iterator iter{};
/**
* Whether we are at the start.
*
* PERF NOTE: this should be elided into inline control flow: it is only used for the first []
* or * call, and SSA optimizers commonly do first-iteration loop optimization.
*
* SAFETY: this is not safe; the object_iterator can be copied freely, so the state CAN be lost.
*/
bool at_start{};
simdjson_really_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;

View File

@ -130,10 +130,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::parser> : public SIMDJ
public:
simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::parser &&value) noexcept; ///< @private
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::parser> &&a) noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson

View File

@ -109,9 +109,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> : pub
public:
simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> &a) noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_really_inline simdjson_result<const char *> raw() const noexcept;

View File

@ -39,6 +39,10 @@ simdjson_really_inline bool token_iterator::operator<=(const token_iterator &oth
return index <= other.index;
}
simdjson_really_inline const uint32_t *token_iterator::checkpoint() const noexcept {
return index;
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -49,6 +49,11 @@ public:
*/
simdjson_really_inline const uint8_t *advance() noexcept;
/**
* Save the current index to be restored later.
*/
simdjson_really_inline const uint32_t *checkpoint() const noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
@ -77,6 +82,8 @@ protected:
const uint32_t *index{};
friend class json_iterator;
friend class value_iterator;
friend class object;
friend simdjson_really_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept;
};
@ -91,9 +98,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::token_iterator> : publ
public:
simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::token_iterator> &&a) noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
};

View File

@ -2,20 +2,21 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
simdjson_really_inline value_iterator::value_iterator(json_iterator *json_iter, depth_t depth) noexcept
simdjson_really_inline value_iterator::value_iterator(json_iterator *json_iter, depth_t depth, const uint32_t *start_index) noexcept
: _json_iter{json_iter},
_depth{depth}
_depth{depth},
_start_index{start_index}
{
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
assert_at_start();
if (*_json_iter->advance() != '{') { logger::log_error(*_json_iter, "Not an object"); return INCORRECT_TYPE; }
return started_object();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_start_object() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
assert_at_start();
if (*_json_iter->peek() != '{') { logger::log_error(*_json_iter, "Not an object"); return INCORRECT_TYPE; }
_json_iter->advance();
@ -23,8 +24,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->advance();
@ -37,7 +36,7 @@ simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object(
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
assert_at_next();
switch (*_json_iter->advance()) {
case '}':
@ -52,36 +51,43 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
}
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_raw(const char *key) noexcept {
// We assume we are sitting at a key: at "key": <value>
SIMDJSON_ASSUME( _json_iter->_depth == _depth+1 );
/**
* Find the field with the given key. May be used in place of ++.
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
if (!is_open()) { return false; }
bool has_next;
do {
// Unless this is the first field, we need to advance past the , and check for }
error_code error;
bool has_value;
if (at_first_field()) {
has_value = true;
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
while (has_value) {
// Get the key
raw_json_string actual_key;
SIMDJSON_TRY( require_raw_json_string().get(actual_key) );
if (*_json_iter->advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
if ((error = field_value() )) { abandon(); return error; }
// Check if the key matches, and return if so
// Check if it matches
if (actual_key == key) {
logger::log_event(*_json_iter, "match", key);
logger::log_event(*this, "match", key, -2);
return true;
}
logger::log_event(*this, "no match", key, -2);
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Skip the value so we can look at the next key
logger::log_event(*_json_iter, "non-match", key);
SIMDJSON_TRY( skip_child() );
// Check whether the next token is , or }
SIMDJSON_TRY( has_next_field().get(has_next) );
} while (has_next);
logger::log_event(*_json_iter, "no matches", key);
// If the loop ended, we're out of fields to look at.
return false;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth+1 );
assert_at_child();
const uint8_t *key = _json_iter->advance();
if (*(key++) != '"') { return _json_iter->report_error(TAPE_ERROR, "Object key is not a string"); }
@ -89,21 +95,21 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
}
simdjson_warn_unused simdjson_really_inline error_code value_iterator::field_value() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth+1 );
assert_at_child();
if (*_json_iter->advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
return SUCCESS;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0);
assert_at_start();
if (*_json_iter->advance() != '[') { logger::log_error(*_json_iter, "Not an array"); return INCORRECT_TYPE; }
return started_array();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_start_array() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0);
assert_at_start();
if (*_json_iter->peek() != '[') { logger::log_error(*_json_iter, "Not an array"); return INCORRECT_TYPE; }
_json_iter->advance();
@ -111,8 +117,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->advance();
@ -125,7 +129,7 @@ simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array()
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
assert_at_next();
switch (*_json_iter->advance()) {
case ']':
@ -147,7 +151,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> va
return require_raw_json_string().unescape(_json_iter->string_buf_loc());
}
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::try_get_raw_json_string() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
assert_at_start();
logger::log_value(*_json_iter, "string", "", 0);
auto json = _json_iter->peek();
@ -157,7 +161,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::require_raw_json_string() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 0 );
assert_at_start();
logger::log_value(*_json_iter, "string", "", 0);
auto json = _json_iter->advance();
@ -166,7 +170,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::try_get_uint64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "uint64", "", 0);
uint64_t result;
@ -176,14 +180,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iter
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::require_uint64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "uint64", "", 0);
_json_iter->ascend_to(depth()-1);
return numberparsing::parse_unsigned(_json_iter->advance());
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::try_get_int64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "int64", "", 0);
int64_t result;
@ -193,14 +197,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_itera
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::require_int64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "int64", "", 0);
_json_iter->ascend_to(depth()-1);
return numberparsing::parse_integer(_json_iter->advance());
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::try_get_double() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "double", "", 0);
double result;
@ -210,7 +214,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterat
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::require_double() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
logger::log_value(*_json_iter, "double", "", 0);
_json_iter->ascend_to(depth()-1);
@ -225,7 +229,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_get_bool() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
bool result;
SIMDJSON_TRY( parse_bool(_json_iter->peek()).get(result) );
@ -234,7 +238,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::require_bool() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
_json_iter->ascend_to(depth()-1);
return parse_bool(_json_iter->advance());
@ -247,7 +251,7 @@ simdjson_really_inline bool value_iterator::is_null(const uint8_t *json) const n
return false;
}
simdjson_really_inline bool value_iterator::is_null() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
if (is_null(_json_iter->peek())) {
_json_iter->advance();
@ -257,7 +261,7 @@ simdjson_really_inline bool value_iterator::is_null() noexcept {
return false;
}
simdjson_really_inline bool value_iterator::require_null() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth > 1 );
assert_at_non_root_start();
_json_iter->ascend_to(depth()-1);
return is_null(_json_iter->advance());
@ -266,8 +270,6 @@ simdjson_really_inline bool value_iterator::require_null() noexcept {
constexpr const uint32_t MAX_INT_LENGTH = 1024;
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::parse_root_uint64(const uint8_t *json, uint32_t max_len) const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, "Root number more than 20 characters"); return NUMBER_ERROR; }
logger::log_value(*_json_iter, "uint64", "", 0);
@ -276,7 +278,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iter
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::try_get_root_uint64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
uint64_t result;
SIMDJSON_TRY( parse_root_uint64(_json_iter->peek(), _json_iter->peek_length()).get(result) );
@ -284,14 +286,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iter
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::require_root_uint64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
auto max_len = _json_iter->peek_length();
return parse_root_uint64(_json_iter->advance(), max_len);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::parse_root_int64(const uint8_t *json, uint32_t max_len) const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, "Root number more than 20 characters"); return NUMBER_ERROR; }
logger::log_value(*_json_iter, "int64", "", 0);
@ -300,7 +300,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_itera
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::try_get_root_int64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
int64_t result;
SIMDJSON_TRY( parse_root_int64(_json_iter->peek(), _json_iter->peek_length()).get(result) );
@ -308,14 +308,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_itera
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::require_root_int64() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
auto max_len = _json_iter->peek_length();
return parse_root_int64(_json_iter->advance(), max_len);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::parse_root_double(const uint8_t *json, uint32_t max_len) const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1];
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, "Root number more than 1082 characters"); return NUMBER_ERROR; }
@ -325,7 +323,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterat
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::try_get_root_double() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
double result;
SIMDJSON_TRY( parse_root_double(_json_iter->peek(), _json_iter->peek_length()).get(result) );
@ -333,20 +331,18 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterat
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::require_root_double() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
auto max_len = _json_iter->peek_length();
return parse_root_double(_json_iter->advance(), max_len);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::parse_root_bool(const uint8_t *json, uint32_t max_len) const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
uint8_t tmpbuf[5+1];
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, "Not a boolean"); return INCORRECT_TYPE; }
return parse_bool(tmpbuf);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_get_root_bool() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
bool result;
SIMDJSON_TRY( parse_root_bool(_json_iter->peek(), _json_iter->peek_length()).get(result) );
@ -354,44 +350,50 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
return result;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::require_root_bool() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
auto max_len = _json_iter->peek_length();
return parse_root_bool(_json_iter->advance(), max_len);
}
simdjson_really_inline bool value_iterator::is_root_null(const uint8_t *json, uint32_t max_len) const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
uint8_t tmpbuf[4+1];
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { return false; }
return is_null(tmpbuf);
}
simdjson_really_inline bool value_iterator::is_root_null() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
if (!is_root_null(_json_iter->peek(), _json_iter->peek_length())) { return false; }
_json_iter->advance();
return true;
}
simdjson_really_inline bool value_iterator::require_root_null() noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth && _depth == 1 );
assert_at_root();
auto max_len = _json_iter->peek_length();
return is_root_null(_json_iter->advance(), max_len);
}
simdjson_warn_unused simdjson_really_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token.index > _start_index );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_really_inline value_iterator value_iterator::child() const noexcept {
SIMDJSON_ASSUME( _json_iter->_depth == _depth+1 );
return { _json_iter, depth()+1 };
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.checkpoint() };
}
simdjson_really_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
simdjson_really_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index > _start_index );
return _json_iter->token.index == _start_index + 1;
}
simdjson_really_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
@ -413,6 +415,35 @@ simdjson_warn_unused simdjson_really_inline json_iterator &value_iterator::json_
return *_json_iter;
}
simdjson_really_inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index == _start_index );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_really_inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index > _start_index );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_really_inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index > _start_index );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_really_inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_really_inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

View File

@ -23,6 +23,12 @@ protected:
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*
* PERF NOTE: this is a safety check; we expect this to be elided in release builds.
*/
const uint32_t *_start_index{};
public:
simdjson_really_inline value_iterator() noexcept = default;
@ -49,6 +55,11 @@ public:
*/
simdjson_really_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_really_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
@ -152,7 +163,7 @@ public:
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> find_field_raw(const char *key) noexcept;
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/** @} */
@ -246,7 +257,7 @@ public:
/** @} */
protected:
simdjson_really_inline value_iterator(json_iterator *json_iter, depth_t depth) noexcept;
simdjson_really_inline value_iterator(json_iterator *json_iter, depth_t depth, const uint32_t *start_index) noexcept;
simdjson_really_inline bool is_null(const uint8_t *json) const noexcept;
simdjson_really_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_really_inline bool is_root_null(const uint8_t *json, uint32_t max_len) const noexcept;
@ -255,6 +266,12 @@ protected:
simdjson_really_inline simdjson_result<int64_t> parse_root_int64(const uint8_t *json, uint32_t max_len) const noexcept;
simdjson_really_inline simdjson_result<double> parse_root_double(const uint8_t *json, uint32_t max_len) const noexcept;
simdjson_really_inline void assert_at_start() const noexcept;
simdjson_really_inline void assert_at_root() const noexcept;
simdjson_really_inline void assert_at_child() const noexcept;
simdjson_really_inline void assert_at_next() const noexcept;
simdjson_really_inline void assert_at_non_root_start() const noexcept;
friend class document;
friend class object;
friend class array;

View File

@ -2,4 +2,6 @@
link_libraries(simdjson)
include_directories(..)
add_cpp_test(ondemand_basictests LABELS acceptance per_implementation)
if(HAVE_POSIX_FORK AND HAVE_POSIX_WAIT) # assert tests use fork and wait, which aren't on MSVC
add_cpp_test(ondemand_assert_out_of_order_values LABELS per_implementation explicitonly assert)
endif()

View File

@ -0,0 +1,56 @@
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "simdjson.h"
using namespace simdjson;
using namespace simdjson::builtin;
// This ensures the compiler can't rearrange them into the proper order (which causes it to work!)
simdjson_never_inline int check_point(simdjson_result<ondemand::value> xval, simdjson_result<ondemand::value> yval) {
// Verify the expected release behavior
error_code error;
uint64_t x = 0;
if ((error = xval.get(x))) { std::cerr << "error getting x: " << error << std::endl; }
else if (x != 2) { std::cerr << "expected x to (wrongly) be 2, was " << x << std::endl; }
uint64_t y = 0;
if ((error = yval.get(y))) { std::cerr << "error getting y: " << error << std::endl; }
else if (y != 3) { std::cerr << "expected y to (wrongly) be 3, was " << y << std::endl; }
return 0;
}
int test_check_point() {
auto json = R"(
{
"x": 1,
"y": 2 3
)"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
return check_point(doc["x"], doc["y"]);
}
bool fork_failed_with_assert() {
int status = 0;
wait(&status);
return WIFSIGNALED(status);
}
int main(void) {
// To verify that the program asserts (which sends a signal), we fork a new process and use wait()
// to check the resulting error code. If it's a signal error code, we consider the
// test to have passed.
// From https://stackoverflow.com/a/33694733
pid_t pid = fork();
if (pid == -1) { std::cerr << "fork failed" << std::endl; return 1; }
if (pid) {
// Parent - wait child and interpret its result
return fork_failed_with_assert() ? 0 : 1;
} else {
test_check_point();
}
}

View File

@ -887,6 +887,116 @@ namespace dom_api_tests {
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_SUCCESS( doc_result["scalar_ignore"] );
std::cout << " - After ignoring empty scalar ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_array_ignore"] );
std::cout << " - After ignoring empty array ..." << std::endl;
ASSERT_SUCCESS( doc_result["empty_object_ignore"] );
std::cout << " - After ignoring empty doc_result ..." << std::endl;
// Break after using first value in child object
{
auto value = doc_result["object_break"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
uint64_t x;
ASSERT_SUCCESS( child_field.value().get(x) );
ASSERT_EQUAL(x, 3);
break; // Break after the first value
}
std::cout << " - After using first value in child object ..." << std::endl;
}
// Break without using first value in child object
{
auto value = doc_result["object_break_unused"];
for (auto [ child_field, error ] : value.get_object()) {
ASSERT_SUCCESS(error);
ASSERT_EQUAL(child_field.key(), "x");
break;
}
std::cout << " - After reaching (but not using) first value in child object ..." << std::endl;
}
// Only look up one field in child object
{
auto value = doc_result["object_index"];
uint64_t x;
ASSERT_SUCCESS( value["x"].get(x) );
ASSERT_EQUAL( x, 5 );
std::cout << " - After looking up one field in child object ..." << std::endl;
}
// Only look up one field in child object, but don't use it
{
auto value = doc_result["object_index_unused"];
ASSERT_SUCCESS( value["x"] );
std::cout << " - After looking up (but not using) one field in child object ..." << std::endl;
}
// Break after first value in child array
{
auto value = doc_result["array_break"];
for (auto child_value : value) {
uint64_t x;
ASSERT_SUCCESS( child_value.get(x) );
ASSERT_EQUAL( x, 7 );
break;
}
std::cout << " - After using first value in child array ..." << std::endl;
}
// Break without using first value in child array
{
auto value = doc_result["array_break_unused"];
for (auto child_value : value) {
ASSERT_SUCCESS(child_value);
break;
}
std::cout << " - After reaching (but not using) first value in child array ..." << std::endl;
}
// Break out of multiple child loops
{
auto value = doc_result["quadruple_nested_break"];
for (auto child1 : value.get_object()) {
for (auto child2 : child1.value().get_array()) {
for (auto child3 : child2.get_object()) {
for (auto child4 : child3.value().get_array()) {
uint64_t x;
ASSERT_SUCCESS( child4.get(x) );
ASSERT_EQUAL( x, 9 );
break;
}
break;
}
break;
}
break;
}
std::cout << " - After breaking out of quadruply-nested arrays and objects ..." << std::endl;
}
// Test the actual value
{
auto value = doc_result["actual_value"];
uint64_t actual_value;
ASSERT_SUCCESS( value.get(actual_value) );
ASSERT_EQUAL( actual_value, 10 );
}
return true;
}));
return true;
}
@ -1061,17 +1171,35 @@ namespace dom_api_tests {
return true;
}));
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result.get_object()["a"].get_uint64().first, 1 );
simdjson_result<ondemand::object> object;
object = doc_result.get_object();
ASSERT_EQUAL( object["a"].get_uint64().first, 1 );
ASSERT_EQUAL( object["b"].get_uint64().first, 2 );
ASSERT_EQUAL( object["c/d"].get_uint64().first, 3 );
ASSERT_ERROR( object["a"], NO_SUCH_FIELD );
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_EQUAL( doc["a"].get_uint64().first, 1 );
ASSERT_EQUAL( doc["b"].get_uint64().first, 2 );
ASSERT_EQUAL( doc["c/d"].get_uint64().first, 3 );
ASSERT_ERROR( doc["a"], NO_SUCH_FIELD );
ASSERT_ERROR( doc["d"], NO_SUCH_FIELD );
return true;
}));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
ASSERT_EQUAL( doc_result["a"].get_uint64().first, 1 );
ASSERT_EQUAL( doc_result["b"].get_uint64().first, 2 );
ASSERT_EQUAL( doc_result["c/d"].get_uint64().first, 3 );
ASSERT_ERROR( doc_result["a"], NO_SUCH_FIELD );
ASSERT_ERROR( doc_result["d"], NO_SUCH_FIELD );
return true;
}));
TEST_SUCCEED();