mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22773f3c70 | |||
| 249eb2c28a | |||
| 94c429aa70 | |||
| 72e51a9a81 | |||
| 1a57afec1f | |||
| 4de7426b9f | |||
| 85cadf4074 | |||
| bc48e72070 | |||
| 8955c2c6d5 | |||
| 73e69a5e84 | |||
| fb83b114ef | |||
| 542cd7af71 | |||
| 769364abb2 | |||
| f25d5f9fc2 | |||
| 12fb30ae9c | |||
| c0a952687d | |||
| 307ddd4415 | |||
| 21657455ed | |||
| afb1c9f4b1 | |||
| 9973f7b3a3 |
@@ -22,4 +22,4 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build -j=2
|
run: cmake --build build -j=2
|
||||||
- name: Test
|
- name: Test
|
||||||
run: ctest --output-on-failure --test-dir build
|
run: ctest --output-on-failure --test-dir build
|
||||||
|
|||||||
+124
-88
@@ -12,7 +12,7 @@ endif()
|
|||||||
project(
|
project(
|
||||||
simdjson
|
simdjson
|
||||||
# The version number is modified by tools/release.py
|
# The version number is modified by tools/release.py
|
||||||
VERSION 4.5.0
|
VERSION 4.6.1
|
||||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||||
HOMEPAGE_URL "https://simdjson.org/"
|
HOMEPAGE_URL "https://simdjson.org/"
|
||||||
LANGUAGES CXX C
|
LANGUAGES CXX C
|
||||||
@@ -29,8 +29,8 @@ string(
|
|||||||
# ---- Options, variables ----
|
# ---- Options, variables ----
|
||||||
|
|
||||||
# These version numbers are modified by tools/release.py
|
# These version numbers are modified by tools/release.py
|
||||||
set(SIMDJSON_LIB_VERSION "32.0.0" CACHE STRING "simdjson library version")
|
set(SIMDJSON_LIB_VERSION "33.0.0" CACHE STRING "simdjson library version")
|
||||||
set(SIMDJSON_LIB_SOVERSION "32" CACHE STRING "simdjson library soversion")
|
set(SIMDJSON_LIB_SOVERSION "33" CACHE STRING "simdjson library soversion")
|
||||||
|
|
||||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF)
|
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF)
|
||||||
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
|
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
|
||||||
@@ -76,9 +76,12 @@ if(SIMDJSON_DEVELOPMENT_CHECKS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(is_top_project)
|
if(is_top_project)
|
||||||
|
option(SIMDJSON_INSTALL "Enable target install" ON)
|
||||||
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
||||||
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
|
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
|
||||||
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
|
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
|
||||||
|
else()
|
||||||
|
option(SIMDJSON_INSTALL "Enable target install" ${BUILD_SHARED_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(cmake/handle-deprecations.cmake)
|
include(cmake/handle-deprecations.cmake)
|
||||||
@@ -164,13 +167,37 @@ if(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(SIMDJSON_STATIC_REFLECTION)
|
if(SIMDJSON_STATIC_REFLECTION)
|
||||||
|
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION MATCHES "^21")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_CXX_COMPILER} --version
|
||||||
|
OUTPUT_VARIABLE CLANG_VERSION_OUTPUT
|
||||||
|
ERROR_VARIABLE CLANG_VERSION_ERROR
|
||||||
|
RESULT_VARIABLE CLANG_VERSION_RESULT
|
||||||
|
)
|
||||||
|
if(CLANG_VERSION_RESULT EQUAL 0 AND CLANG_VERSION_OUTPUT MATCHES "https://github.com/bloomberg/clang-p2996.git")
|
||||||
|
set(IS_BLOOMBERG_P2996_CLANG ON)
|
||||||
|
message(STATUS "Using Bloomberg P2996 Clang fork")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# We would like to require C++26, but no compiler supports that!
|
# We would like to require C++26, but no compiler supports that!
|
||||||
# This is a hack:
|
# This is a hack:
|
||||||
|
if(IS_BLOOMBERG_P2996_CLANG)
|
||||||
simdjson_add_props(
|
simdjson_add_props(
|
||||||
target_compile_options PUBLIC
|
target_compile_options PUBLIC
|
||||||
-freflection -fexpansion-statements -stdlib=libc++ -std=c++26
|
-freflection -fexpansion-statements -stdlib=libc++ -std=c++26
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
simdjson_add_props(
|
||||||
|
target_compile_options PUBLIC
|
||||||
|
-freflection -std=c++26
|
||||||
|
)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
|
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
|
||||||
endif()
|
endif()
|
||||||
@@ -225,87 +252,89 @@ endif()
|
|||||||
|
|
||||||
# ---- Install rules ----
|
# ---- Install rules ----
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
if(SIMDJSON_INSTALL)
|
||||||
include(GNUInstallDirs)
|
include(CMakePackageConfigHelpers)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
if(SIMDJSON_SINGLEHEADER)
|
if(SIMDJSON_SINGLEHEADER)
|
||||||
install(
|
install(
|
||||||
FILES singleheader/simdjson.h
|
FILES singleheader/simdjson.h
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
COMPONENT simdjson_Development
|
COMPONENT simdjson_Development
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS simdjson
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
RUNTIME COMPONENT simdjson_Runtime
|
||||||
|
LIBRARY COMPONENT simdjson_Runtime
|
||||||
|
NAMELINK_COMPONENT simdjson_Development
|
||||||
|
ARCHIVE COMPONENT simdjson_Development
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
)
|
||||||
|
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
simdjson-config-version.cmake
|
||||||
|
COMPATIBILITY SameMinorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
|
||||||
|
CACHE STRING "CMake package config location relative to the install prefix"
|
||||||
|
)
|
||||||
|
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
NAMESPACE simdjson::
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||||
|
install(
|
||||||
|
TARGETS simdjson_static
|
||||||
|
EXPORT simdjson_staticTargets
|
||||||
|
ARCHIVE COMPONENT simdjson_Development
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
EXPORT simdjson_staticTargets
|
||||||
|
NAMESPACE simdjson::
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(
|
|
||||||
TARGETS simdjson
|
|
||||||
EXPORT simdjsonTargets
|
|
||||||
RUNTIME COMPONENT simdjson_Runtime
|
|
||||||
LIBRARY COMPONENT simdjson_Runtime
|
|
||||||
NAMELINK_COMPONENT simdjson_Development
|
|
||||||
ARCHIVE COMPONENT simdjson_Development
|
|
||||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
)
|
|
||||||
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
|
||||||
|
|
||||||
write_basic_package_version_file(
|
|
||||||
simdjson-config-version.cmake
|
|
||||||
COMPATIBILITY SameMinorVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
set(
|
|
||||||
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
|
|
||||||
CACHE STRING "CMake package config location relative to the install prefix"
|
|
||||||
)
|
|
||||||
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
|
|
||||||
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
|
|
||||||
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
|
||||||
EXPORT simdjsonTargets
|
|
||||||
NAMESPACE simdjson::
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
|
|
||||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
|
||||||
install(
|
|
||||||
TARGETS simdjson_static
|
|
||||||
EXPORT simdjson_staticTargets
|
|
||||||
ARCHIVE COMPONENT simdjson_Development
|
|
||||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
)
|
|
||||||
install(
|
|
||||||
EXPORT simdjson_staticTargets
|
|
||||||
NAMESPACE simdjson::
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 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
|
# CPack
|
||||||
#
|
#
|
||||||
@@ -382,18 +411,25 @@ add_subdirectory(fuzz)
|
|||||||
#
|
#
|
||||||
# Source files should be just ASCII
|
# Source files should be just ASCII
|
||||||
#
|
#
|
||||||
find_program(FIND find)
|
find_program(FIND_CMD find)
|
||||||
find_program(FILE file)
|
find_program(FILE_CMD file)
|
||||||
find_program(GREP grep)
|
find_program(GREP_CMD grep)
|
||||||
if(FIND AND FILE AND GREP)
|
if(FIND_CMD AND FILE_CMD AND GREP_CMD)
|
||||||
add_test(
|
add_test(
|
||||||
NAME just_ascii
|
NAME just_ascii
|
||||||
COMMAND sh -c "\
|
COMMAND sh -c "\
|
||||||
${FIND} include src windows tools singleheader tests examples benchmark \
|
non_ascii=$(${FIND_CMD} include src windows tools singleheader tests examples benchmark \
|
||||||
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
|
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
|
||||||
-type f -exec ${FILE} '{}' \; | ${GREP} -qv ASCII || exit 0 && exit 1"
|
-type f -exec ${FILE_CMD} '{}' \; | ${GREP_CMD} -v ASCII); \
|
||||||
|
if [ -n \"$non_ascii\" ]; then \
|
||||||
|
echo 'The following files contain non-ASCII characters:'; \
|
||||||
|
echo \"$non_ascii\"; \
|
||||||
|
exit 1; \
|
||||||
|
fi"
|
||||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
message(WARNING "just_ascii test disabled because required tools were not found: find='${FIND_CMD}', file='${FILE_CMD}', grep='${GREP_CMD}'")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "4.5.0"
|
PROJECT_NUMBER = "4.6.1"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if (TARGET benchmark::benchmark)
|
|||||||
link_libraries(benchmark::benchmark)
|
link_libraries(benchmark::benchmark)
|
||||||
add_executable(bench_parse_call bench_parse_call.cpp)
|
add_executable(bench_parse_call bench_parse_call.cpp)
|
||||||
add_executable(bench_dom_api bench_dom_api.cpp)
|
add_executable(bench_dom_api bench_dom_api.cpp)
|
||||||
|
add_executable(bench_stream_formats bench_stream_formats.cpp)
|
||||||
if(SIMDJSON_EXCEPTIONS)
|
if(SIMDJSON_EXCEPTIONS)
|
||||||
add_executable(bench_ondemand bench_ondemand.cpp)
|
add_executable(bench_ondemand bench_ondemand.cpp)
|
||||||
if(TARGET yyjson)
|
if(TARGET yyjson)
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <string>
|
||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum class stream_case {
|
||||||
|
ndjson_small,
|
||||||
|
ndjson_large,
|
||||||
|
rfc7464_small,
|
||||||
|
rfc7464_large,
|
||||||
|
comma_delimited_small,
|
||||||
|
comma_delimited_large
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t TARGET_BYTES = 128 * 1000 * 1000;
|
||||||
|
constexpr size_t SMALL_PAYLOAD = 16;
|
||||||
|
constexpr size_t LARGE_PAYLOAD = 4096;
|
||||||
|
constexpr size_t BATCH_SIZE = 1 << 20;
|
||||||
|
|
||||||
|
struct stream_dataset {
|
||||||
|
padded_string json;
|
||||||
|
size_t count{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string make_document(size_t id, size_t payload_size) {
|
||||||
|
return std::string{"{\"id\":"} + std::to_string(id) +
|
||||||
|
",\"name\":\"aaaaaaaa\",\"payload\":\"" +
|
||||||
|
std::string(payload_size, 'x') + "\",\"flag\":true}";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_dataset build_dataset(stream_case which) {
|
||||||
|
const bool small = which == stream_case::ndjson_small ||
|
||||||
|
which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::comma_delimited_small;
|
||||||
|
const bool rfc = which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large;
|
||||||
|
const bool comma = which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large;
|
||||||
|
const size_t payload_size = small ? SMALL_PAYLOAD : LARGE_PAYLOAD;
|
||||||
|
const size_t count = TARGET_BYTES / (payload_size + 48);
|
||||||
|
std::string out;
|
||||||
|
out.reserve(count * (payload_size + 64));
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (rfc) {
|
||||||
|
out += char(0x1E);
|
||||||
|
}
|
||||||
|
if (comma && i > 0) {
|
||||||
|
out += ',';
|
||||||
|
}
|
||||||
|
out += make_document(i, payload_size);
|
||||||
|
if (!comma) {
|
||||||
|
out += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {padded_string(out), count};
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream_dataset &get_dataset(stream_case which) {
|
||||||
|
static const stream_dataset ndjson_small =
|
||||||
|
build_dataset(stream_case::ndjson_small);
|
||||||
|
static const stream_dataset ndjson_large =
|
||||||
|
build_dataset(stream_case::ndjson_large);
|
||||||
|
static const stream_dataset rfc_small =
|
||||||
|
build_dataset(stream_case::rfc7464_small);
|
||||||
|
static const stream_dataset rfc_large =
|
||||||
|
build_dataset(stream_case::rfc7464_large);
|
||||||
|
static const stream_dataset comma_small =
|
||||||
|
build_dataset(stream_case::comma_delimited_small);
|
||||||
|
static const stream_dataset comma_large =
|
||||||
|
build_dataset(stream_case::comma_delimited_large);
|
||||||
|
switch (which) {
|
||||||
|
case stream_case::ndjson_small:
|
||||||
|
return ndjson_small;
|
||||||
|
case stream_case::ndjson_large:
|
||||||
|
return ndjson_large;
|
||||||
|
case stream_case::rfc7464_small:
|
||||||
|
return rfc_small;
|
||||||
|
case stream_case::rfc7464_large:
|
||||||
|
return rfc_large;
|
||||||
|
case stream_case::comma_delimited_small:
|
||||||
|
return comma_small;
|
||||||
|
case stream_case::comma_delimited_large:
|
||||||
|
return comma_large;
|
||||||
|
}
|
||||||
|
return ndjson_small;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_counters(benchmark::State &state, const stream_dataset &dataset) {
|
||||||
|
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(dataset.json.size()));
|
||||||
|
state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(dataset.count));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <stream_case which, bool threaded = true>
|
||||||
|
static void bench_ondemand(benchmark::State &state) {
|
||||||
|
const auto &dataset = get_dataset(which);
|
||||||
|
ondemand::parser parser;
|
||||||
|
parser.threaded = threaded;
|
||||||
|
stream_format format = stream_format::whitespace_delimited;
|
||||||
|
if constexpr (which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large) {
|
||||||
|
format = stream_format::json_sequence;
|
||||||
|
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large) {
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
for (const auto _ : state) {
|
||||||
|
ondemand::document_stream docs;
|
||||||
|
auto error = parser.iterate_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||||
|
if (error) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (auto doc : docs) {
|
||||||
|
ondemand::object obj;
|
||||||
|
if ((error = doc.get_object().get(obj))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t id;
|
||||||
|
if ((error = obj["id"].get_uint64().get(id))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sum += id;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(sum);
|
||||||
|
}
|
||||||
|
set_counters(state, dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <stream_case which>
|
||||||
|
static void bench_dom(benchmark::State &state) {
|
||||||
|
const auto &dataset = get_dataset(which);
|
||||||
|
dom::parser parser;
|
||||||
|
parser.threaded = true;
|
||||||
|
stream_format format = stream_format::whitespace_delimited;
|
||||||
|
if constexpr (which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large) {
|
||||||
|
format = stream_format::json_sequence;
|
||||||
|
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large) {
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
for (const auto _ : state) {
|
||||||
|
dom::document_stream docs;
|
||||||
|
auto error = parser.parse_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||||
|
if (error) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (auto doc : docs) {
|
||||||
|
uint64_t id;
|
||||||
|
if ((error = doc["id"].get(id))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sum += id;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(sum);
|
||||||
|
}
|
||||||
|
set_counters(state, dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::ndjson_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::ndjson_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::rfc7464_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::rfc7464_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
// Non-threaded comma_delimited for comparison
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small, false>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large, false>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
BENCHMARK(bench_dom<stream_case::ndjson_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::ndjson_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::rfc7464_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::rfc7464_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::comma_delimited_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::comma_delimited_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
@@ -10,7 +10,7 @@ namespace partial_tweets {
|
|||||||
// {
|
// {
|
||||||
// "created_at": "Sun Aug 31 00:29:15 +0000 2014",
|
// "created_at": "Sun Aug 31 00:29:15 +0000 2014",
|
||||||
// "id": 505874924095815700,
|
// "id": 505874924095815700,
|
||||||
// "text": "@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖",
|
// "text": "@aym0566x ...",
|
||||||
// "in_reply_to_status_id": null,
|
// "in_reply_to_status_id": null,
|
||||||
// "user": {
|
// "user": {
|
||||||
// "id": 1186275104,
|
// "id": 1186275104,
|
||||||
|
|||||||
+93
-75
@@ -161,54 +161,77 @@ The basics: loading and parsing JSON documents
|
|||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
||||||
As required by the standard, your JSON document should be in a Unicode (UTF-8) string. The whole
|
Your JSON document should be a valid Unicode (UTF-8) string.
|
||||||
string, from the beginning to the end, needs to be valid: we do not attempt to tolerate bad
|
|
||||||
inputs before or after a document.
|
|
||||||
|
|
||||||
For efficiency reasons, simdjson requires a string with a few bytes (`simdjson::SIMDJSON_PADDING`)
|
To parse JSON, create a `ondemand::parser` and call its `iterate()` method on a padded input.
|
||||||
at the end, these bytes may be read but their content does not affect the parsing. In practice,
|
The simplest way to load a JSON file is with `padded_string::load`:
|
||||||
it means that the JSON inputs should be stored in a memory region with `simdjson::SIMDJSON_PADDING`
|
|
||||||
extra bytes at the end. You do not have to set these bytes to specific values though you may
|
|
||||||
want to if you want to avoid runtime warnings with some sanitizers. We expect the user
|
|
||||||
of the library to load the data (from disk or from the network) into a padded buffer. To make
|
|
||||||
this easy, we provide the `padded_string::load` function which loads files from disk in a padded buffer.
|
|
||||||
[You can similarly fetch a file from a URL to a padded string](https://github.com/simdjson/curltostring) using our `simdjson::padded_string_builder`. Advanced users may want to read the section Free Padding in [our performance notes](performance.md).
|
|
||||||
|
|
||||||
The simdjson library offers a tree-like [API](https://en.wikipedia.org/wiki/API), which you can
|
|
||||||
access by creating a `ondemand::parser` and calling the `iterate()` method. The iterate method
|
|
||||||
quickly indexes the input string and may detect some errors. The following example illustrates
|
|
||||||
how to get started with an input JSON file (`"twitter.json"`):
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
auto json = padded_string::load("twitter.json"); // load JSON file 'twitter.json'.
|
auto json = padded_string::load("twitter.json");
|
||||||
ondemand::document doc = parser.iterate(json); // position a pointer at the beginning of the JSON data
|
ondemand::document doc = parser.iterate(json);
|
||||||
```
|
```
|
||||||
|
|
||||||
(Windows users compiling with C++17 or better may use `wchar_t` strings to support non-ASCII
|
For inline JSON strings, use the `_padded` suffix:
|
||||||
filenames: `padded_string::load(L"twitter.json")`.)
|
|
||||||
|
|
||||||
If you prefer not to create your own `ondemand::parser` instance, you can access
|
```cpp
|
||||||
a thread-local version by calling `ondemand::parser.get_parser()`.
|
ondemand::parser parser;
|
||||||
|
auto json = "[1,2,3]"_padded;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are compiling with C++17 or better, you can use `simdjson::padded_input`
|
||||||
|
which accepts any string-like input and handles padding automatically:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
ondemand::parser parser;
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
ondemand::document doc = parser.iterate(input);
|
||||||
|
|
||||||
|
// Also works with std::string, considering reserved capacity
|
||||||
|
std::string json_str = "[1,2,3]";
|
||||||
|
json_str.reserve(100); // Reserve extra space
|
||||||
|
simdjson::padded_input input2(json_str); // May avoid copying
|
||||||
|
ondemand::document doc2 = parser.iterate(input2);
|
||||||
|
```
|
||||||
|
|
||||||
|
The simdjson library also accepts `std::string` instances directly---if the provided
|
||||||
|
reference is non-const, it will allocate padding as needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
ondemand::parser parser;
|
||||||
|
std::string json = "[1,2,3]";
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
||||||
|
|
||||||
|
|
||||||
|
### Advanced input options
|
||||||
|
|
||||||
|
This section covers additional ways to provide JSON input to simdjson, including
|
||||||
|
options for fine-grained control over padding and memory.
|
||||||
|
|
||||||
|
**Thread-local parser.** If you prefer not to create your own `ondemand::parser` instance, you can access
|
||||||
|
a thread-local version by calling `ondemand::parser.get_parser()`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::document doc = ondemand::parser.get_parser().iterate(json);
|
ondemand::document doc = ondemand::parser.get_parser().iterate(json);
|
||||||
```
|
```
|
||||||
|
|
||||||
However, you should be careful because a parser instance can only be used for one
|
A parser instance can only be used for one document at a time, so
|
||||||
document at a time, thus it is only applicable when you are only parsing one
|
the thread-local parser is only applicable when you parse one
|
||||||
document per thread at any one time.
|
document per thread at any one time.
|
||||||
|
|
||||||
You can also create a padded string---and call `iterate()`:
|
**`padded_input` details (C++17+).** The actual padding only occurs when the JSON string ends near the boundary of a memory page, which is
|
||||||
|
uncommon. Using a `simdjson::padded_input` is safe although sanitizers and tools like valgrind
|
||||||
|
might report illegal reads (which are safe in our case because they remain in the mapped page). You should avoid `simdjson::padded_input`
|
||||||
|
on systems without a page size of at least 4096: virtually all systems qualify except for
|
||||||
|
some niche embedded systems running custom operating systems. Standard Linux, Windows, macOS, Android, iOS, etc., are all fine. Note that, most times, a `simdjson::padded_input` instance will not copy the data and will only act
|
||||||
|
as a view (it does not own the memory).
|
||||||
|
|
||||||
```cpp
|
**User-managed buffers.** If you have a buffer of your own with enough padding already (`SIMDJSON_PADDING` extra bytes allocated), you can use `padded_string_view` to pass it in:
|
||||||
ondemand::parser parser;
|
|
||||||
auto json = "[1,2,3]"_padded; // The _padded suffix creates a simdjson::padded_string instance
|
|
||||||
ondemand::document doc = parser.iterate(json); // parse a string
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have a buffer of your own with enough padding already (SIMDJSON_PADDING extra bytes allocated), you can use `padded_string_view` to pass it in:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
@@ -217,73 +240,72 @@ strcpy(json, "[1]");
|
|||||||
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
|
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
|
||||||
```
|
```
|
||||||
|
|
||||||
The simdjson library will also accept `std::string` instances. If the provided
|
**Copying into a `padded_string`.** You can copy your data directly into a `simdjson::padded_string`:
|
||||||
reference is non-const, it will allocate padding as needed.
|
|
||||||
|
|
||||||
You can copy your data directly on a `simdjson::padded_string` as follows:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const char * data = "my data"; // 7 bytes
|
const char * data = "my data"; // 7 bytes
|
||||||
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
||||||
```
|
```
|
||||||
|
|
||||||
Or as follows...
|
Or from a `std::string`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string data = "my data";
|
std::string data = "my data";
|
||||||
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then parse the JSON data from the `simdjson::padded_string` instance:
|
**`std::string` and sanitizer warnings.** Whenever you pass an `std::string` reference to `parser::iterate`,
|
||||||
|
the parser may access bytes beyond the end of
|
||||||
|
|
||||||
```cpp
|
|
||||||
ondemand::document doc = parser.iterate(my_padded_data);
|
|
||||||
```
|
|
||||||
|
|
||||||
Whenever you pass an `std::string` reference to `parser::iterate`,
|
|
||||||
the parser will access the bytes beyond the end of
|
|
||||||
the string but before the end of the allocated memory (`std::string::capacity()`).
|
the string but before the end of the allocated memory (`std::string::capacity()`).
|
||||||
If you are using a sanitizer that checks for reading uninitialized bytes or `std::string`'s
|
Sanitizers that check for reading uninitialized bytes may produce warnings.
|
||||||
container-overflow checks, you may encounter sanitizer warnings.
|
You can safely ignore these warnings, or call `simdjson::pad(std::string&)` to pad the
|
||||||
You can safely ignore these warnings. Or you can call `simdjson::pad(std::string&)` to pad the
|
string explicitly:
|
||||||
string with `SIMDJSON_PADDING` spaces: this function returns a `simdjson::padding_string_view` which can be be passed to the parser's iterator function:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string json = "[1]";
|
std::string json = "[1]";
|
||||||
ondemand::document doc = parser.iterate(simdjson::pad(json));
|
ondemand::document doc = parser.iterate(simdjson::pad(json));
|
||||||
```
|
```
|
||||||
|
|
||||||
We recommend against creating many `std::string` or many `std::padding_string` instances in your application to store your JSON data.
|
We recommend against creating many `std::string` or many `std::padded_string` instances in your application to store your JSON data.
|
||||||
Consider reusing the same buffers and limiting memory allocations.
|
Consider reusing the same buffers and limiting memory allocations.
|
||||||
|
|
||||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
**Memory-file mapping (non-Windows).** You can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||||
|
from a file on disk:
|
||||||
|
|
||||||
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
|
```cpp
|
||||||
|
simdjson::padded_memory_map map(myfilename);
|
||||||
|
if (!map.is_valid()) { /* handle error */ }
|
||||||
|
simdjson::padded_string_view view = map.view();
|
||||||
|
ondemand::document doc = parser.iterate(view);
|
||||||
|
```
|
||||||
|
|
||||||
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
**Windows-specific notes.** Windows users compiling with C++17 or better may use `wchar_t` strings to support non-ASCII
|
||||||
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
filenames: `padded_string::load(L"twitter.json")`. Windows users who need to read files with
|
||||||
|
|
||||||
*Windows-specific*: Windows users who need to read files with
|
|
||||||
non-ANSI characters in the name should set their code page to
|
non-ANSI characters in the name should set their code page to
|
||||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||||
Further, they may use the AreFileApisANSI function to determine whether
|
Further, they may use the AreFileApisANSI function to determine whether
|
||||||
the filename is interpreted using the ANSI or the system default OEM
|
the filename is interpreted using the ANSI or the system default OEM
|
||||||
codepage, and they may call SetFileApisToOEM accordingly.
|
codepage, and they may call SetFileApisToOEM accordingly.
|
||||||
|
|
||||||
|
Some users may want to browse code along with the compiled assembly:
|
||||||
|
|
||||||
**Advanced feature:**
|
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
||||||
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
||||||
from a file on disk.
|
|
||||||
|
|
||||||
```cpp
|
**Summary of input types:**
|
||||||
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
|
|
||||||
// under Windows at this time.
|
|
||||||
simdjson::padded_memory_map map(myfilename);
|
| Input Type / Method | Padding Requirement | How Padding is Handled | Ownership / Copying | Notes / Warnings |
|
||||||
if (!map.is_valid()) { /* handle error */ }
|
|----------------------------------------------|-------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|----------------------------------------------|----------------------------------------------------------------------------------|
|
||||||
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
| `padded_string::load("file.json")` | Automatic (SIMDJSON_PADDING extra bytes) | Library allocates padded buffer and loads file into it | Owned by `padded_string` | Recommended for files; safest and simplest. |
|
||||||
ondemand::document doc = parser.iterate(view); // parse the JSON
|
| `"...json..."_padded` literal | Automatic (built-in padding) | Creates `padded_string` with padding | Owned by `padded_string` | Convenient for small hardcoded JSON. |
|
||||||
```
|
| `simdjson::padded_input` (C++17+) | Automatic when needed | Adds padding **only** if the string ends near a memory page boundary. For `std::string`, considers `capacity()` | Usually a non-owning view (no copy most times) | Safe on standard OS (page size ≥ 4096). May trigger sanitizer/valgrind warnings (harmless). Avoid on niche embedded systems. |
|
||||||
|
| User buffer with explicit padding | Must have at least `SIMDJSON_PADDING` extra allocated bytes after JSON content | Pass via `iterate(ptr, json_length, total_allocated_size)` or `padded_string_view` | User-owned (no copy) | Use `char buf[len + SIMDJSON_PADDING]`. Library reads (but never writes) into padding. |
|
||||||
|
| `std::string` (non-const) | Library checks `capacity()` | If insufficient, library may allocate a padded copy | May copy (depends on capacity) | Can trigger sanitizer warnings on uninitialized bytes. Use `simdjson::pad(json)` to avoid. |
|
||||||
|
| `simdjson::pad(std::string&)` | Adds padding if needed | Returns `padded_string_view` pointing to the (possibly resized) string | References original string | Recommended to silence sanitizers when using `std::string`. |
|
||||||
|
| `padded_string(data, length)` or `padded_string(std::string)` | Automatic (copies into padded buffer) | Explicit copy into owned padded buffer | Owned by `padded_string` | Safe when you want full ownership and padding guaranteed. |
|
||||||
|
| `padded_string_view` (manual) | User guarantees `SIMDJSON_PADDING` extra bytes after the viewed length | User provides pointer + length + capacity | Non-owning view | Low-level; requires careful buffer management. |
|
||||||
|
| Memory-mapped file (`padded_memory_map`) | Automatic via mapping (non-Windows only) | Creates view with sufficient padding | Non-owning (tied to map lifetime) | Advanced; efficient for large files on Linux/macOS/etc. |
|
||||||
|
|
||||||
|
|
||||||
Documents are iterators
|
Documents are iterators
|
||||||
@@ -436,7 +458,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
* **Extracting Values:** You can cast a JSON element to a native type:
|
* **Extracting Values:** You can cast a JSON element to a native type:
|
||||||
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
||||||
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
|
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
|
||||||
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
|
`get_uint64()`, `get_int64()`, `get_uint32()`, `get_int32()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
|
||||||
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
|
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
|
||||||
the cast is not possible: the error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
the cast is not possible: the error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
||||||
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
|
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
|
||||||
@@ -446,8 +468,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
pass `true` (`get_string(true)`) as a parameter to get replacement characters where errors
|
pass `true` (`get_string(true)`) as a parameter to get replacement characters where errors
|
||||||
occur. If you somehow need to access non-UTF-8 strings in a lossless manner
|
occur. If you somehow need to access non-UTF-8 strings in a lossless manner
|
||||||
(e.g., if you strings contain unpaired surrogates), you may use the `get_wobbly_string()` function to get a string in the [WTF-8 format](https://simonsapin.github.io/wtf-8).
|
(e.g., if you strings contain unpaired surrogates), you may use the `get_wobbly_string()` function to get a string in the [WTF-8 format](https://simonsapin.github.io/wtf-8).
|
||||||
When calling `get_uint64()` and `get_int64()`, if the number does not fit in a corresponding
|
When calling `get_uint64()`, `get_int64()`, `get_uint32()` or `get_int32()`, if the number does not fit in the
|
||||||
64-bit integer type, it is also considered an error. When parsing numbers or other scalar values, the library checks
|
corresponding integer type, it is also considered an error (`NUMBER_OUT_OF_RANGE`). When parsing numbers or other scalar values, the library checks
|
||||||
that the value is followed by an expected character, thus you *may* get a number parsing error when accessing the digits
|
that the value is followed by an expected character, thus you *may* get a number parsing error when accessing the digits
|
||||||
as an integer in the following strings: `{"number":12332a`, `{"number":12332\0`, `{"number":12332` (the digits appear at the end). We always abide by the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification so that, for example, numbers prefixed by the `+` sign are in error.
|
as an integer in the following strings: `{"number":12332a`, `{"number":12332\0`, `{"number":12332` (the digits appear at the end). We always abide by the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification so that, for example, numbers prefixed by the `+` sign are in error.
|
||||||
|
|
||||||
@@ -1300,10 +1322,6 @@ You can also use the custom `Car` type as part of a template such as `std::vecto
|
|||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document doc = parser.iterate(json);
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
std::vector<Car> cars(doc);
|
std::vector<Car> cars(doc);
|
||||||
// visual studio users need an explicit call:
|
|
||||||
// std::vector<Car> cars = doc.get<std::vector<Car>>();
|
|
||||||
// because the compiler does not know whether to convert
|
|
||||||
// doc to an unsigned int or to a vector.
|
|
||||||
for(Car& c : cars) {
|
for(Car& c : cars) {
|
||||||
std::cout << c.year << std::endl;
|
std::cout << c.year << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,21 @@ your code with the `SIMDJSON_STATIC_REFLECTION` macro set:
|
|||||||
The `simdjson::compile_time::parse_json` function parses a JSON document at **compile time** and returns a `constexpr` structure reflecting its content. We support the full range of JSON values, which are mapped to C++ types as in
|
The `simdjson::compile_time::parse_json` function parses a JSON document at **compile time** and returns a `constexpr` structure reflecting its content. We support the full range of JSON values, which are mapped to C++ types as in
|
||||||
the following table.
|
the following table.
|
||||||
|
|
||||||
|
For convenience, you can also use the `""_json` user-defined literal operator, which is available in the `simdjson::literals` namespace:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
|
constexpr auto cfg = R"(
|
||||||
|
{
|
||||||
|
"port": 8080,
|
||||||
|
"host": "localhost"
|
||||||
|
}
|
||||||
|
)"_json;
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use the qualified name `simdjson::literals::operator""_json`.
|
||||||
|
|
||||||
|
|
||||||
| JSON type | C++ type |
|
| JSON type | C++ type |
|
||||||
|----------------|----------------------------------|
|
|----------------|----------------------------------|
|
||||||
@@ -61,6 +76,8 @@ You can do so, at compile-time, as follows:
|
|||||||
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto cfg = R"(
|
constexpr auto cfg = R"(
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -79,6 +96,8 @@ constexpr auto cfg = R"(
|
|||||||
You can nest objects and arrays:
|
You can nest objects and arrays:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto data = R"(
|
constexpr auto data = R"(
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -98,6 +117,8 @@ constexpr auto data = R"(
|
|||||||
Top-level arrays are allowed:
|
Top-level arrays are allowed:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto arr = R"(
|
constexpr auto arr = R"(
|
||||||
|
|
||||||
[1, 2, 3]
|
[1, 2, 3]
|
||||||
@@ -116,6 +137,8 @@ want to check that it conforms to your expectation. You can do so with concepts.
|
|||||||
Let us consider this example:
|
Let us consider this example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto config = R"(
|
constexpr auto config = R"(
|
||||||
|
|
||||||
[
|
[
|
||||||
|
|||||||
+13
@@ -882,6 +882,19 @@ simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len,
|
|||||||
|
|
||||||
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
|
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
|
||||||
|
|
||||||
|
If you are compiling your project with C++17 or better, you can use a `simdjson::padded_input`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json); // Automatically pads if needed
|
||||||
|
simdjson::dom::element element = parser.parse(input);
|
||||||
|
```
|
||||||
|
|
||||||
|
The actual padding only occurs if the JSON string ends near the boundary of a memory page, which is uncommon. Using a `simdjson::padded_input` is safe although sanitizers and tools like valgrind might report illegal reads (which are safe in our case because they remain in the mapped page). You should avoid `simdjson::padded_input` on systems without a page size of at least 4096: virtually all systems qualify except for some niche embedded systems running custom operating systems. Standard Linux, Windows, macOS, Android, iOS, etc., are all fine. Note that, most times, an `simdjson::padded_input` instance will not copy the data and will only act
|
||||||
|
as a view (it does not own the memory).
|
||||||
|
|
||||||
|
|
||||||
Performance Tips
|
Performance Tips
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
+273
-29
@@ -26,6 +26,7 @@ Contents
|
|||||||
- [Tracking your position](#tracking-your-position)
|
- [Tracking your position](#tracking-your-position)
|
||||||
- [Incomplete streams](#incomplete-streams)
|
- [Incomplete streams](#incomplete-streams)
|
||||||
- [C++20 features](#c20-features)
|
- [C++20 features](#c20-features)
|
||||||
|
- [C++26 features (static reflection)](#c26-features-static-reflection)
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
-----------
|
-----------
|
||||||
@@ -132,7 +133,7 @@ E.g., `[1,2]{"32":1}` is recognized as two documents.
|
|||||||
Some official formats **(non-exhaustive list)**:
|
Some official formats **(non-exhaustive list)**:
|
||||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
|
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
|
||||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||||
|
|
||||||
API
|
API
|
||||||
@@ -278,39 +279,131 @@ Importantly, you should only call `truncated_bytes()` after iterating through al
|
|||||||
Comma-separated documents
|
Comma-separated documents
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
We also support comma-separated documents, but with some performance limitations. The `iterate_many` function takes in an option to allow parsing of comma separated documents (which defaults on false). In this mode, the entire buffer is processed in one batch. Therefore, the total size of the document should not exceed the maximal capacity of the parser (4 GB). This mode also effectively disallow multithreading. It is therefore mostly suitable for not "very large" inputs. In this mode, the batch_size parameter
|
To parse comma-separated documents like `{"a":1},{"b":2},{"c":3}`, use the `stream_format::comma_delimited` parameter:
|
||||||
is effectively ignored, as it is set to at least the document size.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream stream;
|
||||||
// We pass '32' as the batch size, but it is a bogus parameter because, since
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
// the document size.
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
for (auto doc : stream) {
|
||||||
if (error) { std::cerr << error << std::endl; return; }
|
std::cout << doc << std::endl;
|
||||||
for (auto doc : doc_stream) {
|
}
|
||||||
std::cout << doc.type() << std::endl;
|
// Prints: {"a":1}
|
||||||
}
|
// {"b":2}
|
||||||
```
|
// {"c":3}
|
||||||
|
|
||||||
This will print:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
number
|
|
||||||
number
|
Whitespace around the commas is allowed:
|
||||||
number
|
```cpp
|
||||||
number
|
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||||
string
|
|
||||||
string
|
|
||||||
string
|
|
||||||
object
|
|
||||||
array
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Nested commas inside objects and arrays are preserved:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||||
|
// Correctly parses as 2 documents, not 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Mixed document types are supported:
|
||||||
|
```cpp
|
||||||
|
auto json = R"(1, 2, 3, 4, "a", "b", "c", {"hello": "world"}, [1, 2, 3])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream doc_stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(doc_stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : doc_stream) {
|
||||||
|
std::cout << doc.type() << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: number number number number string string string object array
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra top-level separators are tolerated for compatibility with the legacy
|
||||||
|
`allow_comma_separated` behavior. For example, leading commas, trailing commas,
|
||||||
|
and repeated commas are treated as empty separators rather than documents.
|
||||||
|
|
||||||
|
### Legacy `allow_comma_separated` parameter (deprecated)
|
||||||
|
|
||||||
|
The `allow_comma_separated` boolean parameter is deprecated. When set to `true`, it now internally maps to `stream_format::comma_delimited`.
|
||||||
|
|
||||||
|
The old single-batch limitation no longer applies - comma-delimited parsing now supports multi-batch processing and threading for optimal performance on large files.
|
||||||
|
|
||||||
|
JSON Text Sequences (RFC 7464)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
```
|
||||||
|
<RS>{"name":"doc1"}<LF>
|
||||||
|
<RS>{"name":"doc2"}<LF>
|
||||||
|
<RS>{"name":"doc3"}<LF>
|
||||||
|
```
|
||||||
|
|
||||||
|
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `stream_format` enum has the following values:
|
||||||
|
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||||
|
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||||
|
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||||
|
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||||
|
|
||||||
|
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||||
|
|
||||||
|
JSON Array As A Document Stream
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// All of these work:
|
||||||
|
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||||
|
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||||
|
auto c = R"([])"_padded; // empty array → 0 docs
|
||||||
|
```
|
||||||
|
|
||||||
|
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `iterate_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||||
|
|
||||||
|
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||||
|
|
||||||
|
|
||||||
C++20 features
|
C++20 features
|
||||||
--------------------
|
--------------------
|
||||||
@@ -418,3 +511,154 @@ Otherwise you may use this longer version for explicit handling of errors:
|
|||||||
cars.push_back(c);
|
cars.push_back(c);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
C++26 features (static reflection)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
If you have a C++26 compatible compiler with [P2996](https://wg21.link/P2996)
|
||||||
|
static reflection support, you can compile the simdjson library with the
|
||||||
|
`SIMDJSON_STATIC_REFLECTION` macro set to `1`. When this is the case, simdjson
|
||||||
|
can deserialize a stream of JSON documents directly into your own structures
|
||||||
|
**without** writing any `tag_invoke` function. The library inspects the
|
||||||
|
non-static public members of your type at compile time and produces the
|
||||||
|
parsing code automatically.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define SIMDJSON_STATIC_REFLECTION 1
|
||||||
|
#include "simdjson.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Consider the same `Car` structure used in the C++20 example, but **without**
|
||||||
|
any `tag_invoke` glue:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Car {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
std::vector<double> tire_pressure;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
With C++26 static reflection enabled, you can iterate a stream of cars and
|
||||||
|
push them into a `std::vector<Car>` directly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This works for every `stream_format` value supported by `iterate_many`. The
|
||||||
|
following examples each parse the same three cars, but laid out using a
|
||||||
|
different streaming convention.
|
||||||
|
|
||||||
|
### Whitespace-delimited (default, NDJSON / JSON Lines)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::whitespace_delimited).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // throws on error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comma-delimited documents
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### A single JSON array as a stream of documents
|
||||||
|
|
||||||
|
When the input is a single JSON array, you can stream its elements one at a
|
||||||
|
time without materializing the entire array as a `std::vector` upfront:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } ] )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON Text Sequences (RFC 7464)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
auto append = [&](std::string_view doc) {
|
||||||
|
input_str += '\x1e'; input_str += doc; input_str += '\x0a';
|
||||||
|
};
|
||||||
|
append(R"({ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] })");
|
||||||
|
append(R"({ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0 ] })");
|
||||||
|
append(R"({ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0 ] })");
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In every case, the user-defined type (`Car` here) does not need a hand-written
|
||||||
|
`tag_invoke` overload: the library generates the deserialization code from the
|
||||||
|
type's public data members at compile time.
|
||||||
|
|||||||
+113
-1
@@ -132,7 +132,7 @@ Whitespace Characters:
|
|||||||
Some official formats **(non-exhaustive list)**:
|
Some official formats **(non-exhaustive list)**:
|
||||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
|
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
|
||||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||||
|
|
||||||
API
|
API
|
||||||
@@ -253,3 +253,115 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
|||||||
|
|
||||||
|
|
||||||
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
|
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
|
||||||
|
|
||||||
|
JSON Text Sequences (RFC 7464)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
```
|
||||||
|
<RS>{"name":"doc1"}<LF>
|
||||||
|
<RS>{"name":"doc2"}<LF>
|
||||||
|
<RS>{"name":"doc3"}<LF>
|
||||||
|
```
|
||||||
|
|
||||||
|
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(input, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `stream_format` enum has the following values:
|
||||||
|
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||||
|
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||||
|
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||||
|
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||||
|
|
||||||
|
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||||
|
|
||||||
|
Comma-Separated Documents
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Some systems produce JSON documents separated by commas, like `{"a":1},{"b":2},{"c":3}`. This is common when extracting elements from a JSON array or when APIs return comma-separated results.
|
||||||
|
|
||||||
|
To parse comma-separated documents, use the `stream_format::comma_delimited` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whitespace around the commas is allowed:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||||
|
```
|
||||||
|
|
||||||
|
Nested commas inside objects and arrays are preserved:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||||
|
// Correctly parses as 2 documents, not 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra top-level separators are tolerated for compatibility with the legacy
|
||||||
|
On-Demand comma-separated mode. Leading commas, trailing commas, and repeated
|
||||||
|
commas are treated as empty separators rather than documents.
|
||||||
|
|
||||||
|
Unlike the legacy `allow_comma_separated` parameter, `stream_format::comma_delimited` supports multi-batch processing and threading for optimal performance on large files.
|
||||||
|
|
||||||
|
JSON Array As A Document Stream
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// All of these work:
|
||||||
|
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||||
|
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||||
|
auto c = R"([])"_padded; // empty array → 0 docs
|
||||||
|
```
|
||||||
|
|
||||||
|
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `parse_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||||
|
|
||||||
|
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||||
|
|||||||
+20
-4
@@ -204,9 +204,25 @@ but can be significantly larger. E.g., Apple systems favour pages spanning 16 ki
|
|||||||
|
|
||||||
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
|
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
|
||||||
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
|
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
|
||||||
Nevertheless, you can still make sure of this capability in your code if you are an expert
|
You can still make sure of this capability in your code if you are an expert
|
||||||
programmer and you are willing to silence sanitizer warnings. The following code provides
|
programmer and you are willing to silence sanitizer warnings.
|
||||||
a portable example.
|
|
||||||
|
If you are building simdjson with C++17 or better, you can use `simdjson::padded_input`.
|
||||||
|
The `padded_input` struct automatically manages padding for you. It can be constructed from a `std::string_view` or a C-style string with length. If the input already has sufficient padding (up to the end of the memory page), it creates a view without copying. Otherwise, it copies the data into a `padded_string` with proper padding.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string_view json = get_json_data();
|
||||||
|
simdjson::padded_input input(json); // Automatically pads if needed
|
||||||
|
auto result = parser.parse(input);
|
||||||
|
```
|
||||||
|
|
||||||
|
This simplifies padding management compared to manually checking and allocating.
|
||||||
|
|
||||||
|
|
||||||
|
More generally, the following code provides a portable example.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
|
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
|
||||||
@@ -250,7 +266,7 @@ long page_size() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
||||||
// page boundary.
|
// page boundary. Assumes len != 0.
|
||||||
bool need_allocation(const char *buf, size_t len) {
|
bool need_allocation(const char *buf, size_t len) {
|
||||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
||||||
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
|||||||
|
|
||||||
/** The maximum document size supported by simdjson. */
|
/** The maximum document size supported by simdjson. */
|
||||||
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
|
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
|
||||||
|
/** The maximum depth of nested objects and arrays supported by simdjson.
|
||||||
|
A depth of SIMDJSON_MAXSIZE_BYTES/2 is not reasonable and would be
|
||||||
|
adversarial, but it serves as an upper bound for validation purposes. */
|
||||||
|
constexpr size_t SIMDJSON_MAX_DEPTH = SIMDJSON_MAXSIZE_BYTES/2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of padding needed in a buffer to parse JSON.
|
* The amount of padding needed in a buffer to parse JSON.
|
||||||
@@ -46,6 +50,21 @@ struct padded_string;
|
|||||||
class padded_string_view;
|
class padded_string_view;
|
||||||
enum class stage1_mode;
|
enum class stage1_mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream format for parse_many/iterate_many.
|
||||||
|
*/
|
||||||
|
enum class stream_format {
|
||||||
|
whitespace_delimited, ///< Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
|
||||||
|
json_sequence, ///< RFC 7464 JSON text sequences (RS-delimited)
|
||||||
|
comma_delimited, ///< Comma-separated JSON documents (e.g., `{...},{...},{...}`)
|
||||||
|
comma_delimited_array ///< A single JSON array whose elements are iterated as
|
||||||
|
///< comma-separated documents (e.g., `[{...},{...},{...}]`).
|
||||||
|
///< The parser strips the outer `[` / `]` plus any
|
||||||
|
///< surrounding JSON whitespace (space, tab, LF, CR)
|
||||||
|
///< and then behaves like `comma_delimited` over the
|
||||||
|
///< remaining bytes.
|
||||||
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -266,6 +266,12 @@ consteval std::pair<double, size_t> parse_double(const char *src,
|
|||||||
}
|
}
|
||||||
} // namespace number_parsing
|
} // namespace number_parsing
|
||||||
|
|
||||||
|
consteval auto make_data_member_options(auto&& name_str) {
|
||||||
|
std::meta::data_member_options options{};
|
||||||
|
options.name = std::forward<decltype(name_str)>(name_str);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
// JSON string may contain embedded nulls, and C++26 reflection does not yet
|
// JSON string may contain embedded nulls, and C++26 reflection does not yet
|
||||||
// support std::string_view as a data member type. As a workaround, we define
|
// support std::string_view as a data member type. As a workaround, we define
|
||||||
// a custom type that holds a const char* and a size.
|
// a custom type that holds a const char* and a size.
|
||||||
@@ -320,7 +326,8 @@ using class_type = type_builder<meta_info...>::constructed_type;
|
|||||||
/**
|
/**
|
||||||
* @brief Variable template for constructing instances with values
|
* @brief Variable template for constructing instances with values
|
||||||
*/
|
*/
|
||||||
template <typename T, auto... Vs> constexpr auto construct_from = T{Vs...};
|
template <typename T, auto... Vs> constexpr T construct_from = T{Vs...};
|
||||||
|
|
||||||
|
|
||||||
// in JSON, there are only a few whitespace characters that are allowed
|
// in JSON, there are only a few whitespace characters that are allowed
|
||||||
// outside of objects, arrays, strings, and numbers.
|
// outside of objects, arrays, strings, and numbers.
|
||||||
@@ -410,7 +417,7 @@ parse_number(std::string_view json,
|
|||||||
std::from_chars(json.data(), json.data() + json.size(), int_value);
|
std::from_chars(json.data(), json.data() + json.size(), int_value);
|
||||||
if (res.ec == std::errc()) {
|
if (res.ec == std::errc()) {
|
||||||
out = int_value;
|
out = int_value;
|
||||||
if ((res.ptr - json.data()) != scope) {
|
if (static_cast<std::size_t>(res.ptr - json.data()) != scope) {
|
||||||
simdjson_consteval_error(
|
simdjson_consteval_error(
|
||||||
"Internal error: cannot agree on the character range of the float");
|
"Internal error: cannot agree on the character range of the float");
|
||||||
}
|
}
|
||||||
@@ -424,7 +431,7 @@ parse_number(std::string_view json,
|
|||||||
std::from_chars(json.data(), json.data() + json.size(), uint_value);
|
std::from_chars(json.data(), json.data() + json.size(), uint_value);
|
||||||
if (res.ec == std::errc()) {
|
if (res.ec == std::errc()) {
|
||||||
out = uint_value;
|
out = uint_value;
|
||||||
if ((res.ptr - json.data()) != scope) {
|
if (static_cast<std::size_t>(res.ptr - json.data()) != scope) {
|
||||||
simdjson_consteval_error(
|
simdjson_consteval_error(
|
||||||
"Internal error: cannot agree on the character range of the float");
|
"Internal error: cannot agree on the character range of the float");
|
||||||
}
|
}
|
||||||
@@ -533,7 +540,7 @@ parse_string(std::string_view json) {
|
|||||||
// present, we have an error (isolated high surrogate), which we
|
// present, we have an error (isolated high surrogate), which we
|
||||||
// tolerate by substituting the substitution_code_point.
|
// tolerate by substituting the substitution_code_point.
|
||||||
if (end - cursor < 6 || *cursor != '\\' ||
|
if (end - cursor < 6 || *cursor != '\\' ||
|
||||||
*(cursor + 1) != 'u' > 0xFFFF) {
|
*(cursor + 1) != 'u') {
|
||||||
code_point = substitution_code_point;
|
code_point = substitution_code_point;
|
||||||
} else { // we have \u following the high surrogate
|
} else { // we have \u following the high surrogate
|
||||||
cursor += 2; // skip \u
|
cursor += 2; // skip \u
|
||||||
@@ -887,10 +894,19 @@ parse_json_array_impl(const std::string_view json) {
|
|||||||
std::size_t count = values.size() - 1;
|
std::size_t count = values.size() - 1;
|
||||||
// We assume all elements have the same type as the first element.
|
// We assume all elements have the same type as the first element.
|
||||||
// However, if the array is heterogeneous, we should use std::variant.
|
// However, if the array is heterogeneous, we should use std::variant.
|
||||||
|
auto elem_type = std::meta::type_of(values[1]);
|
||||||
|
// String literals reflected via reflect_constant_string have type const
|
||||||
|
// char[N], but when passed as template auto parameters they decay to
|
||||||
|
// const char*. Use const char* as the element type so that
|
||||||
|
// construct_from can aggregate-initialize the array.
|
||||||
|
if (std::meta::is_array_type(elem_type) &&
|
||||||
|
std::meta::remove_all_extents(elem_type) == ^^const char) {
|
||||||
|
elem_type = ^^const char *;
|
||||||
|
}
|
||||||
auto array_type = std::meta::substitute(
|
auto array_type = std::meta::substitute(
|
||||||
^^std::array,
|
^^std::array,
|
||||||
{
|
{
|
||||||
std::meta::type_of(values[1]), std::meta::reflect_constant(count)});
|
elem_type, std::meta::reflect_constant(count)});
|
||||||
|
|
||||||
// Create array instance with values
|
// Create array instance with values
|
||||||
values[0] = array_type;
|
values[0] = array_type;
|
||||||
@@ -957,8 +973,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
simdjson_consteval_error("Expected '}'");
|
simdjson_consteval_error("Expected '}'");
|
||||||
}
|
}
|
||||||
cursor += object_size;
|
cursor += object_size;
|
||||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed),
|
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
|
||||||
{.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(parsed);
|
values.push_back(parsed);
|
||||||
|
|
||||||
@@ -967,8 +982,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
case '[': {
|
case '[': {
|
||||||
std::string_view value(cursor, end);
|
std::string_view value(cursor, end);
|
||||||
auto [parsed, array_size] = parse_json_array_impl(value);
|
auto [parsed, array_size] = parse_json_array_impl(value);
|
||||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed),
|
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
|
||||||
{.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(parsed);
|
values.push_back(parsed);
|
||||||
if (*(cursor + array_size - 1) != ']') {
|
if (*(cursor + array_size - 1) != ']') {
|
||||||
@@ -989,8 +1003,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^const char *, {
|
std::meta::data_member_spec(^^const char *, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant_string(value));
|
values.push_back(std::meta::reflect_constant_string(value));
|
||||||
break;
|
break;
|
||||||
@@ -1001,8 +1014,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 4;
|
cursor += 4;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^bool, {
|
auto dms = std::meta::data_member_spec(^^bool, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(true));
|
values.push_back(std::meta::reflect_constant(true));
|
||||||
break;
|
break;
|
||||||
@@ -1013,8 +1025,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 5;
|
cursor += 5;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^bool, {
|
auto dms = std::meta::data_member_spec(^^bool, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(false));
|
values.push_back(std::meta::reflect_constant(false));
|
||||||
break;
|
break;
|
||||||
@@ -1025,9 +1036,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 4;
|
cursor += 4;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^std::nullptr_t,
|
auto dms = std::meta::data_member_spec(^^std::nullptr_t, make_data_member_options(field_name));
|
||||||
{
|
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(nullptr));
|
values.push_back(std::meta::reflect_constant(nullptr));
|
||||||
break;
|
break;
|
||||||
@@ -1051,22 +1060,19 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
if (std::holds_alternative<int64_t>(out)) {
|
if (std::holds_alternative<int64_t>(out)) {
|
||||||
int64_t int_value = std::get<int64_t>(out);
|
int64_t int_value = std::get<int64_t>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^int64_t, {
|
std::meta::data_member_spec(^^int64_t, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(int_value));
|
values.push_back(std::meta::reflect_constant(int_value));
|
||||||
} else if (std::holds_alternative<uint64_t>(out)) {
|
} else if (std::holds_alternative<uint64_t>(out)) {
|
||||||
uint64_t uint_value = std::get<uint64_t>(out);
|
uint64_t uint_value = std::get<uint64_t>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^uint64_t, {
|
std::meta::data_member_spec(^^uint64_t, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(uint_value));
|
values.push_back(std::meta::reflect_constant(uint_value));
|
||||||
} else {
|
} else {
|
||||||
double float_value = std::get<double>(out);
|
double float_value = std::get<double>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^double, {
|
std::meta::data_member_spec(^^double, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(float_value));
|
values.push_back(std::meta::reflect_constant(float_value));
|
||||||
}
|
}
|
||||||
@@ -1111,16 +1117,11 @@ template <constevalutil::fixed_string json_str> consteval auto parse_json() {
|
|||||||
"Only JSON objects and arrays are supported at the top level, this "
|
"Only JSON objects and arrays are supported at the top level, this "
|
||||||
"limitation will be lifted in the future.");*/
|
"limitation will be lifted in the future.");*/
|
||||||
|
|
||||||
constexpr auto result = json.front() == '['
|
if constexpr (json.front() == '[') {
|
||||||
? parse_json_array_impl(json)
|
return [: parse_json_array_impl(json).first :];
|
||||||
: parse_json_object_impl(json);
|
} else {
|
||||||
return [: result.first :];
|
return [: parse_json_object_impl(json).first :];
|
||||||
/*
|
}
|
||||||
if(json.front() == '[') {
|
|
||||||
return [:parse_json_array_impl(json).first:];
|
|
||||||
} else if(json.front() == '{') {
|
|
||||||
// return [:parse_json_object_impl(json).first:];
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace compile_time
|
} // namespace compile_time
|
||||||
|
|||||||
@@ -63,13 +63,16 @@ namespace compile_time {
|
|||||||
template <constevalutil::fixed_string json_str> consteval auto parse_json();
|
template <constevalutil::fixed_string json_str> consteval auto parse_json();
|
||||||
|
|
||||||
} // namespace compile_time
|
} // namespace compile_time
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
|
inline namespace literals {
|
||||||
|
|
||||||
template <simdjson::constevalutil::fixed_string str>
|
template <simdjson::constevalutil::fixed_string str>
|
||||||
consteval auto operator ""_json() {
|
consteval auto operator ""_json() {
|
||||||
return simdjson::compile_time::parse_json<str>();
|
return simdjson::compile_time::parse_json<str>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace literals
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_STATIC_REFLECTION
|
#endif // SIMDJSON_STATIC_REFLECTION
|
||||||
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ struct fixed_string {
|
|||||||
data[i] = str[i];
|
data[i] = str[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr fixed_string(const unsigned char (&str)[N]) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
data[i] = static_cast<char>(str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
char data[N];
|
char data[N];
|
||||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||||
constexpr size_t size() const { return N ; }
|
constexpr size_t size() const { return N ; }
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ inline error_code document::allocate(size_t capacity) noexcept {
|
|||||||
allocated_capacity = 0;
|
allocated_capacity = 0;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
if (capacity > SIMDJSON_MAXSIZE_BYTES) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
// a pathological input like "[[[[..." would generate capacity tape elements, so
|
// a pathological input like "[[[[..." would generate capacity tape elements, so
|
||||||
// need a capacity of at least capacity + 1, but it is also possible to do
|
// need a capacity of at least capacity + 1, but it is also possible to do
|
||||||
|
|||||||
@@ -89,12 +89,14 @@ simdjson_inline document_stream::document_stream(
|
|||||||
dom::parser &_parser,
|
dom::parser &_parser,
|
||||||
const uint8_t *_buf,
|
const uint8_t *_buf,
|
||||||
size_t _len,
|
size_t _len,
|
||||||
size_t _batch_size
|
size_t _batch_size,
|
||||||
|
stream_format _format
|
||||||
) noexcept
|
) noexcept
|
||||||
: parser{&_parser},
|
: parser{&_parser},
|
||||||
buf{_buf},
|
buf{_buf},
|
||||||
len{_len},
|
len{_len},
|
||||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||||
|
format{_format},
|
||||||
error{SUCCESS}
|
error{SUCCESS}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||||
@@ -112,6 +114,7 @@ simdjson_inline document_stream::document_stream() noexcept
|
|||||||
buf{nullptr},
|
buf{nullptr},
|
||||||
len{0},
|
len{0},
|
||||||
batch_size{0},
|
batch_size{0},
|
||||||
|
format{stream_format::whitespace_delimited},
|
||||||
error{UNINITIALIZED}
|
error{UNINITIALIZED}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(false)
|
, use_thread(false)
|
||||||
@@ -224,7 +227,14 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
} else {
|
} else {
|
||||||
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
||||||
size_t svlen = next_doc_index - current_index();
|
size_t svlen = next_doc_index - current_index();
|
||||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464 json_sequence
|
||||||
|
// mode the scanner classifies RS as a scalar character, so an RS-prefixed
|
||||||
|
// scalar document (number/true/false/null/string) has no closing structural
|
||||||
|
// index and the slice runs all the way up to the next document's RS. RS
|
||||||
|
// cannot legally appear in a JSON value at the source level (control
|
||||||
|
// characters in strings must be escaped as \u001E), so stripping it is
|
||||||
|
// safe in every stream_format.
|
||||||
|
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||||
svlen--;
|
svlen--;
|
||||||
}
|
}
|
||||||
return std::string_view(start, svlen);
|
return std::string_view(start, svlen);
|
||||||
@@ -274,10 +284,35 @@ inline size_t document_stream::next_batch_start() const noexcept {
|
|||||||
|
|
||||||
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
|
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
|
||||||
size_t remaining = len - _batch_start;
|
size_t remaining = len - _batch_start;
|
||||||
|
stage1_mode mode;
|
||||||
if (remaining <= batch_size) {
|
if (remaining <= batch_size) {
|
||||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
// Final batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_final;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_final;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_final;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||||
} else {
|
} else {
|
||||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
// Partial batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_partial;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_partial;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_partial;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,12 +206,14 @@ private:
|
|||||||
* @param buf is the raw byte buffer we need to process
|
* @param buf is the raw byte buffer we need to process
|
||||||
* @param len is the length of the raw byte buffer in bytes
|
* @param len is the length of the raw byte buffer in bytes
|
||||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||||
|
* @param format is the stream format
|
||||||
*/
|
*/
|
||||||
simdjson_inline document_stream(
|
simdjson_inline document_stream(
|
||||||
dom::parser &parser,
|
dom::parser &parser,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t batch_size
|
size_t batch_size,
|
||||||
|
stream_format format = stream_format::whitespace_delimited
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,6 +263,8 @@ private:
|
|||||||
const uint8_t *buf;
|
const uint8_t *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t batch_size;
|
size_t batch_size;
|
||||||
|
/** The stream format. */
|
||||||
|
stream_format format;
|
||||||
/** The error (or lack thereof) from the current document. */
|
/** The error (or lack thereof) from the current document. */
|
||||||
error_code error;
|
error_code error;
|
||||||
size_t batch_start{0};
|
size_t batch_start{0};
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
|
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
|
||||||
|
const std::string path_copy(path);
|
||||||
// Open the file
|
// Open the file
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = std::fopen(path.data(), "rb");
|
std::FILE *fp = std::fopen(path_copy.c_str(), "rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -169,12 +170,7 @@ simdjson_inline simdjson_result<element> parser::parse(const padded_string_view
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
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; }
|
return parse_many(buf, len, batch_size, stream_format::whitespace_delimited);
|
||||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
|
||||||
buf += 3;
|
|
||||||
len -= 3;
|
|
||||||
}
|
|
||||||
return document_stream(*this, buf, len, batch_size);
|
|
||||||
}
|
}
|
||||||
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||||
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||||
@@ -186,6 +182,42 @@ inline simdjson_result<document_stream> parser::parse_many(const padded_string &
|
|||||||
return parse_many(s.data(), s.length(), batch_size);
|
return parse_many(s.data(), s.length(), batch_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
if (format == stream_format::comma_delimited_array) {
|
||||||
|
// Strip leading JSON whitespace.
|
||||||
|
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||||
|
buf++; len--;
|
||||||
|
}
|
||||||
|
// Expect the opening '['.
|
||||||
|
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||||
|
buf++; len--;
|
||||||
|
// Strip trailing JSON whitespace.
|
||||||
|
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
// Expect the closing ']'.
|
||||||
|
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||||
|
len--;
|
||||||
|
// Fall through to comma_delimited over the array contents.
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline size_t parser::capacity() const noexcept {
|
simdjson_inline size_t parser::capacity() const noexcept {
|
||||||
return implementation ? implementation->capacity() : 0;
|
return implementation ? implementation->capacity() : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -494,6 +494,23 @@ public:
|
|||||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||||
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
|
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a stream of JSON documents with explicit format specification.
|
||||||
|
*
|
||||||
|
* @param buf The concatenated JSON documents.
|
||||||
|
* @param len The length of the buffer.
|
||||||
|
* @param batch_size The batch size to use.
|
||||||
|
* @param format The stream format.
|
||||||
|
* @return A stream of documents, or an error.
|
||||||
|
*/
|
||||||
|
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||||
* and `max_depth` depth.
|
* and `max_depth` depth.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace SIMDJSON_IMPLEMENTATION {
|
|||||||
namespace builder {
|
namespace builder {
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
|
requires(concepts::container_but_not_string<T> && ! concepts::optional_type<T> && !require_custom_serialization<T>)
|
||||||
constexpr void atom(string_builder &b, const T &t) {
|
constexpr void atom(string_builder &b, const T &t) {
|
||||||
auto it = t.begin();
|
auto it = t.begin();
|
||||||
auto end = t.end();
|
auto end = t.end();
|
||||||
@@ -130,7 +130,7 @@ template <typename T>
|
|||||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||||
void atom(string_builder &b, const T &e) {
|
void atom(string_builder &b, const T &e) {
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||||
template for (constexpr auto enum_val : enumerators) {
|
template for (constexpr auto enum_val : enumerators) {
|
||||||
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val)));
|
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val)));
|
||||||
if (e == [:enum_val:]) {
|
if (e == [:enum_val:]) {
|
||||||
@@ -294,30 +294,23 @@ string_builder& operator<<(string_builder& b, const Z& z) {
|
|||||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||||
void extract_from(string_builder &b, const T &obj) {
|
void extract_from(string_builder &b, const T &obj) {
|
||||||
// Helper to check if a field name matches any of the requested fields
|
|
||||||
auto should_extract = [](std::string_view field_name) constexpr -> bool {
|
|
||||||
return ((FieldNames.view() == field_name) || ...);
|
|
||||||
};
|
|
||||||
|
|
||||||
b.append('{');
|
b.append('{');
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
// Iterate through all members of T using reflection
|
// Iterate through all members of T using reflection
|
||||||
template for (constexpr auto mem : std::define_static_array(
|
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
|
||||||
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
template for (constexpr auto mem : members) {
|
||||||
|
|
||||||
if constexpr (std::meta::is_public(mem)) {
|
if constexpr (std::meta::is_public(mem)) {
|
||||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||||
|
|
||||||
// Only serialize this field if it's in our list of requested fields
|
// Only serialize this field if it's in our list of requested fields
|
||||||
if constexpr (should_extract(key)) {
|
if constexpr (((FieldNames.view() == key) || ...)) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
b.append(',');
|
b.append(',');
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
// Serialize the key
|
// Serialize the key
|
||||||
constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
|
static constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
|
||||||
b.append_raw(quoted_key);
|
b.append_raw(quoted_key);
|
||||||
b.append(':');
|
b.append(':');
|
||||||
|
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
|
|||||||
return is_valid;
|
return is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) {
|
inline void string_builder::grow_buffer(size_t desired_capacity) {
|
||||||
if (!is_valid) {
|
if (!is_valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -842,7 +842,7 @@ simdjson_inline void string_builder::append(const T &value) {
|
|||||||
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
// Support for range-based appending (std::ranges::view, etc.)
|
// Support for range-based appending (std::ranges::view, etc.)
|
||||||
template <std::ranges::range R>
|
template <std::ranges::range R>
|
||||||
requires(!std::is_convertible<R, std::string_view>::value && !require_custom_serialization<R>)
|
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
|
||||||
simdjson_inline void string_builder::append(const R &range) noexcept {
|
simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||||
auto it = std::ranges::begin(range);
|
auto it = std::ranges::begin(range);
|
||||||
auto end = std::ranges::end(range);
|
auto end = std::ranges::end(range);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public:
|
|||||||
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
// Support for range-based appending (std::ranges::view, etc.)
|
// Support for range-based appending (std::ranges::view, etc.)
|
||||||
template <std::ranges::range R>
|
template <std::ranges::range R>
|
||||||
requires (!std::is_convertible<R, std::string_view>::value && !require_custom_serialization<R>)
|
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
|
||||||
simdjson_inline void append(const R &range) noexcept;
|
simdjson_inline void append(const R &range) noexcept;
|
||||||
#endif
|
#endif
|
||||||
/**
|
/**
|
||||||
@@ -249,7 +249,7 @@ private:
|
|||||||
* If the allocation fails, is_valid is set to false. We expect
|
* If the allocation fails, is_valid is set to false. We expect
|
||||||
* that this function would not be repeatedly called.
|
* that this function would not be repeatedly called.
|
||||||
*/
|
*/
|
||||||
simdjson_inline void grow_buffer(size_t desired_capacity);
|
inline void grow_buffer(size_t desired_capacity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We use this helper function to make sure that is_valid is kept consistent.
|
* We use this helper function to make sure that is_valid is kept consistent.
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(s
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
|
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
|
||||||
|
if(max_depth > SIMDJSON_MAX_DEPTH) { return CAPACITY; }
|
||||||
// Stage 2 stacks
|
// Stage 2 stacks
|
||||||
open_containers.reset(new (std::nothrow) open_container[max_depth]);
|
open_containers.reset(new (std::nothrow) open_container[max_depth]);
|
||||||
is_array.reset(new (std::nothrow) bool[max_depth]);
|
is_array.reset(new (std::nothrow) bool[max_depth]);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
#include "simdjson/generic/ondemand/deserialize.h"
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
@@ -131,6 +133,18 @@ simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
|
|||||||
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
|
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
|
||||||
return get_root_value_iterator().get_root_int64_in_string(true);
|
return get_root_value_iterator().get_root_int64_in_string(true);
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(get_uint64().get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(get_int64().get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> document::get_double() noexcept {
|
simdjson_inline simdjson_result<double> document::get_double() noexcept {
|
||||||
return get_root_value_iterator().get_root_double(true);
|
return get_root_value_iterator().get_root_double(true);
|
||||||
}
|
}
|
||||||
@@ -164,6 +178,8 @@ template<> simdjson_inline simdjson_result<std::string_view> document::get() & n
|
|||||||
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
||||||
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
|
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
|
||||||
|
|
||||||
@@ -174,6 +190,8 @@ template<> simdjson_warn_unused simdjson_inline error_code document::get(std::st
|
|||||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
||||||
|
|
||||||
@@ -504,6 +522,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
@@ -627,27 +653,27 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::op
|
|||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator uint64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator uint64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_uint64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator int64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator int64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_int64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator double() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator double() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_double();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator std::string_view() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator std::string_view() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_raw_json_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator bool() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator bool() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_bool();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
@@ -731,6 +757,18 @@ simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexc
|
|||||||
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
|
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
|
||||||
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
|
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
|
||||||
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
|
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
|
||||||
|
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_uint64(false).get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_int64(false).get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||||
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||||
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
|
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
|
||||||
@@ -748,6 +786,8 @@ template<> simdjson_inline simdjson_result<std::string_view> document_reference:
|
|||||||
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
|
||||||
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
|
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -879,6 +919,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
@@ -994,27 +1042,27 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_refe
|
|||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator uint64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator uint64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_uint64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator int64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator int64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_int64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator double() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator double() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_double();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator std::string_view() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator std::string_view() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_raw_json_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator bool() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator bool() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_bool();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
|
|||||||
@@ -76,6 +76,26 @@ public:
|
|||||||
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
|
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* Calls get_uint64() and checks that the result fits in a uint32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit unsigned integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit signed integer.
|
||||||
|
*
|
||||||
|
* Calls get_int64() and checks that the result fits in an int32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit signed integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
@@ -299,21 +319,21 @@ public:
|
|||||||
* @returns A signed 64-bit integer.
|
* @returns A signed 64-bit integer.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a signed integer.
|
* Cast this JSON value to a signed integer.
|
||||||
*
|
*
|
||||||
* @returns A signed 64-bit integer.
|
* @returns A signed 64-bit integer.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
* @returns A double.
|
* @returns A double.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a string.
|
* Cast this JSON value to a string.
|
||||||
*
|
*
|
||||||
@@ -323,7 +343,7 @@ public:
|
|||||||
* time it parses a document or when it is destroyed.
|
* time it parses a document or when it is destroyed.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
|
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a raw_json_string.
|
* Cast this JSON value to a raw_json_string.
|
||||||
*
|
*
|
||||||
@@ -332,14 +352,14 @@ public:
|
|||||||
* @returns A pointer to the raw JSON for the given string.
|
* @returns A pointer to the raw JSON for the given string.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
|
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a bool.
|
* Cast this JSON value to a bool.
|
||||||
*
|
*
|
||||||
* @returns A bool value.
|
* @returns A bool value.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a value when the document is an object or an array.
|
* Cast this JSON value to a value when the document is an object or an array.
|
||||||
*
|
*
|
||||||
@@ -829,6 +849,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -923,12 +945,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator array() & noexcept(false);
|
simdjson_inline operator array() & noexcept(false);
|
||||||
simdjson_inline operator object() & noexcept(false);
|
simdjson_inline operator object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator raw_json_string() noexcept(false);
|
explicit simdjson_inline operator raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator value() noexcept(false);
|
simdjson_inline operator value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
@@ -982,6 +1004,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -1006,12 +1030,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
explicit simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
@@ -1069,6 +1093,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -1090,12 +1116,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
explicit simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
|
|||||||
@@ -95,13 +95,15 @@ simdjson_inline document_stream::document_stream(
|
|||||||
const uint8_t *_buf,
|
const uint8_t *_buf,
|
||||||
size_t _len,
|
size_t _len,
|
||||||
size_t _batch_size,
|
size_t _batch_size,
|
||||||
bool _allow_comma_separated
|
bool _allow_comma_separated,
|
||||||
|
stream_format _format
|
||||||
) noexcept
|
) noexcept
|
||||||
: parser{&_parser},
|
: parser{&_parser},
|
||||||
buf{_buf},
|
buf{_buf},
|
||||||
len{_len},
|
len{_len},
|
||||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||||
allow_comma_separated{_allow_comma_separated},
|
allow_comma_separated{_allow_comma_separated},
|
||||||
|
format{_format},
|
||||||
error{SUCCESS}
|
error{SUCCESS}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||||
@@ -120,6 +122,7 @@ simdjson_inline document_stream::document_stream() noexcept
|
|||||||
len{0},
|
len{0},
|
||||||
batch_size{0},
|
batch_size{0},
|
||||||
allow_comma_separated{false},
|
allow_comma_separated{false},
|
||||||
|
format{stream_format::whitespace_delimited},
|
||||||
error{UNINITIALIZED}
|
error{UNINITIALIZED}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(false)
|
, use_thread(false)
|
||||||
@@ -219,7 +222,10 @@ inline void document_stream::start() noexcept {
|
|||||||
error = run_stage1(*parser, batch_start);
|
error = run_stage1(*parser, batch_start);
|
||||||
}
|
}
|
||||||
if (error) { return; }
|
if (error) { return; }
|
||||||
doc_index = batch_start;
|
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
|
||||||
|
// after the RS delimiter and any following whitespace. For regular mode, it is
|
||||||
|
// the offset from batch_start to the first document in the batch.
|
||||||
|
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||||
doc = document(json_iterator(&buf[batch_start], parser));
|
doc = document(json_iterator(&buf[batch_start], parser));
|
||||||
doc.iter._streaming = true;
|
doc.iter._streaming = true;
|
||||||
|
|
||||||
@@ -300,7 +306,7 @@ inline void document_stream::next() noexcept {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
|
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
|
||||||
doc_index = batch_start;
|
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,10 +335,35 @@ inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch
|
|||||||
// This code only updates the structural index in the parser, it does not update any json_iterator
|
// This code only updates the structural index in the parser, it does not update any json_iterator
|
||||||
// instance.
|
// instance.
|
||||||
size_t remaining = len - _batch_start;
|
size_t remaining = len - _batch_start;
|
||||||
|
stage1_mode mode;
|
||||||
if (remaining <= batch_size) {
|
if (remaining <= batch_size) {
|
||||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
// Final batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_final;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_final;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_final;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||||
} else {
|
} else {
|
||||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
// Partial batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_partial;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_partial;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_partial;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +384,21 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
depth--;
|
depth--;
|
||||||
break;
|
break;
|
||||||
default: // Scalar value document
|
default: // Scalar value document
|
||||||
// TODO: We could remove trailing whitespaces
|
|
||||||
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
||||||
{
|
{
|
||||||
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
||||||
// normally the length would be next_index - current_index() - 1, except for the last document
|
// normally the length would be next_index - current_index() - 1, except for the last document
|
||||||
size_t svlen = next_index - current_index();
|
size_t svlen = next_index - current_index();
|
||||||
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
||||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
|
||||||
|
// json_sequence mode the scanner classifies RS as a scalar
|
||||||
|
// character, so an RS-prefixed scalar document (number / true /
|
||||||
|
// false / null / string) has no closing structural index and the
|
||||||
|
// slice runs all the way up to the next document's RS. RS cannot
|
||||||
|
// legally appear in a JSON value at the source level (control
|
||||||
|
// characters in strings must be escaped as \u001E), so stripping
|
||||||
|
// it is safe in every stream_format.
|
||||||
|
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||||
svlen--;
|
svlen--;
|
||||||
}
|
}
|
||||||
return std::string_view(start, svlen);
|
return std::string_view(start, svlen);
|
||||||
@@ -441,4 +479,4 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_stre
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
|
||||||
|
|||||||
@@ -229,13 +229,16 @@ private:
|
|||||||
* @param buf is the raw byte buffer we need to process
|
* @param buf is the raw byte buffer we need to process
|
||||||
* @param len is the length of the raw byte buffer in bytes
|
* @param len is the length of the raw byte buffer in bytes
|
||||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||||
|
* @param allow_comma_separated whether to allow comma-separated documents
|
||||||
|
* @param format the stream format
|
||||||
*/
|
*/
|
||||||
simdjson_inline document_stream(
|
simdjson_inline document_stream(
|
||||||
ondemand::parser &parser,
|
ondemand::parser &parser,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t batch_size,
|
size_t batch_size,
|
||||||
bool allow_comma_separated
|
bool allow_comma_separated,
|
||||||
|
stream_format format = stream_format::whitespace_delimited
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -284,6 +287,7 @@ private:
|
|||||||
size_t len;
|
size_t len;
|
||||||
size_t batch_size;
|
size_t batch_size;
|
||||||
bool allow_comma_separated;
|
bool allow_comma_separated;
|
||||||
|
stream_format format;
|
||||||
/**
|
/**
|
||||||
* We are going to use just one document instance. The document owns
|
* We are going to use just one document instance. The document owns
|
||||||
* the json_iterator. It implies that we only ever pass a reference
|
* the json_iterator. It implies that we only ever pass a reference
|
||||||
|
|||||||
@@ -133,6 +133,34 @@ simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iter
|
|||||||
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
|
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
|
||||||
|
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
return iterate_many(s.data(), s.length(), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(pad(s), batch_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
// Warning: no check is done on the buffer padding. We trust the user.
|
// Warning: no check is done on the buffer padding. We trust the user.
|
||||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
@@ -140,8 +168,11 @@ inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf,
|
|||||||
buf += 3;
|
buf += 3;
|
||||||
len -= 3;
|
len -= 3;
|
||||||
}
|
}
|
||||||
if(allow_comma_separated && batch_size < len) { batch_size = len; }
|
// Map allow_comma_separated to stream_format::comma_delimited
|
||||||
return document_stream(*this, buf, len, batch_size, allow_comma_separated);
|
if (allow_comma_separated) {
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
@@ -161,6 +192,48 @@ inline simdjson_result<document_stream> parser::iterate_many(const std::string &
|
|||||||
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
return iterate_many(pad(s), batch_size, allow_comma_separated);
|
return iterate_many(pad(s), batch_size, allow_comma_separated);
|
||||||
}
|
}
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
if (format == stream_format::comma_delimited_array) {
|
||||||
|
// Strip leading JSON whitespace.
|
||||||
|
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||||
|
buf++; len--;
|
||||||
|
}
|
||||||
|
// Expect the opening '['.
|
||||||
|
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||||
|
buf++; len--;
|
||||||
|
// Strip trailing JSON whitespace.
|
||||||
|
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
// Expect the closing ']'.
|
||||||
|
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||||
|
len--;
|
||||||
|
// Fall through to comma_delimited over the array contents.
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
return iterate_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size, format);
|
||||||
|
}
|
||||||
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
@@ -172,7 +245,7 @@ simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
|
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
|
||||||
if(max_capacity < dom::MINIMAL_DOCUMENT_CAPACITY) {
|
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
|
||||||
_max_capacity = max_capacity;
|
_max_capacity = max_capacity;
|
||||||
} else {
|
} else {
|
||||||
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
|
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
|
||||||
|
|||||||
@@ -244,32 +244,66 @@ public:
|
|||||||
* spot is cache-related: small enough to fit in cache, yet big enough to
|
* spot is cache-related: small enough to fit in cache, yet big enough to
|
||||||
* parse as many documents as possible in one tight loop.
|
* parse as many documents as possible in one tight loop.
|
||||||
* Defaults to 10MB, which has been a reasonable sweet spot in our tests.
|
* Defaults to 10MB, which has been a reasonable sweet spot in our tests.
|
||||||
* @param allow_comma_separated (defaults on false) This allows a mode where the documents are
|
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
|
||||||
* separated by commas instead of whitespace. It comes with a performance
|
* When true, maps internally to stream_format::comma_delimited.
|
||||||
* penalty because the entire document is indexed at once (and the document must be
|
* Defaults to false.
|
||||||
* less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter
|
|
||||||
* is effectively ignored, as it is set to at least the document size.
|
|
||||||
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
|
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
|
||||||
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
|
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
|
||||||
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
|
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
|
||||||
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
|
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
|
||||||
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
|
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
|
||||||
*/
|
*/
|
||||||
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
|
||||||
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
|
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
|
||||||
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||||
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
/**
|
||||||
|
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
|
||||||
|
*/
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a stream of JSON documents with explicit format specification.
|
||||||
|
*
|
||||||
|
* @param buf The concatenated JSON documents.
|
||||||
|
* @param len The length of the buffer.
|
||||||
|
* @param batch_size The batch size to use.
|
||||||
|
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
|
||||||
|
* @return A stream of documents, or an error.
|
||||||
|
*/
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
|
||||||
/** The capacity of this parser (the largest document it can process). */
|
/** The capacity of this parser (the largest document it can process). */
|
||||||
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||||
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
||||||
@@ -425,4 +459,4 @@ public:
|
|||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
|||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
std::string_view str;
|
std::string_view str;
|
||||||
SIMDJSON_TRY(val.get_string().get(str));
|
SIMDJSON_TRY(val.get_string().get(str));
|
||||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||||
template for (constexpr auto enum_val : enumerators) {
|
template for (constexpr auto enum_val : enumerators) {
|
||||||
if (str == std::meta::identifier_of(enum_val)) {
|
if (str == std::meta::identifier_of(enum_val)) {
|
||||||
out = [:enum_val:];
|
out = [:enum_val:];
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "simdjson/generic/ondemand/value.h"
|
#include "simdjson/generic/ondemand/value.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
@@ -72,6 +74,18 @@ simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
|
|||||||
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
|
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
|
||||||
return iter.get_int64_in_string();
|
return iter.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(get_uint64().get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(get_int64().get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
|
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
|
||||||
return iter.get_bool();
|
return iter.get_bool();
|
||||||
}
|
}
|
||||||
@@ -87,6 +101,8 @@ template<> simdjson_inline simdjson_result<number> value::get() noexcept { retur
|
|||||||
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
||||||
|
|
||||||
|
|
||||||
@@ -98,6 +114,8 @@ template<> simdjson_warn_unused simdjson_inline error_code value::get(number& ou
|
|||||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -401,6 +419,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
|
|||||||
@@ -161,6 +161,28 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* Calls get_uint64() and checks that the result fits in a uint32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit unsigned integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit signed integer.
|
||||||
|
*
|
||||||
|
* Calls get_int64() and checks that the result fits in an int32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit signed integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
@@ -746,6 +768,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ class document;
|
|||||||
* 3) The stream_final mode allows us to truncate final
|
* 3) The stream_final mode allows us to truncate final
|
||||||
* unterminated strings. It is useful in conjunction with streaming_partial.
|
* unterminated strings. It is useful in conjunction with streaming_partial.
|
||||||
*/
|
*/
|
||||||
enum class stage1_mode { regular, streaming_partial, streaming_final};
|
enum class stage1_mode {
|
||||||
|
regular,
|
||||||
|
streaming_partial, streaming_final,
|
||||||
|
json_sequence_partial, json_sequence_final,
|
||||||
|
comma_delimited_partial, comma_delimited_final
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if mode == streaming_partial or mode == streaming_final
|
* Returns true if mode == streaming_partial or mode == streaming_final
|
||||||
@@ -31,7 +36,6 @@ inline bool is_streaming(stage1_mode mode) {
|
|||||||
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
|
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -168,10 +168,13 @@ inline padded_string::operator padded_string_view() const noexcept simdjson_life
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
|
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
|
||||||
|
// std::string_view is not guaranteed to be null-terminated, but std::fopen requires
|
||||||
|
// a null-terminated C string. Construct a temporary std::string to ensure null-termination.
|
||||||
|
const std::string null_terminated_filename(filename);
|
||||||
// Open the file
|
// Open the file
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = std::fopen(filename.data(), "rb");
|
std::FILE *fp = std::fopen(null_terminated_filename.c_str(), "rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -223,10 +226,13 @@ inline simdjson_result<padded_string> padded_string::load(std::string_view filen
|
|||||||
|
|
||||||
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
|
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
|
||||||
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
|
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
|
||||||
|
// std::wstring_view is not guaranteed to be null-terminated, but _wfopen requires
|
||||||
|
// a null-terminated wide C string. Construct a temporary std::wstring to ensure null-termination.
|
||||||
|
const std::wstring null_terminated_filename(filename);
|
||||||
// Open the file using the wide characters
|
// Open the file using the wide characters
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = _wfopen(filename.data(), L"rb");
|
std::FILE *fp = _wfopen(null_terminated_filename.c_str(), L"rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -349,6 +355,9 @@ inline padded_string padded_string_builder::convert() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
||||||
|
if (simdjson_unlikely(additional + size < size)) {
|
||||||
|
return false; // overflow: cannot satisfy request
|
||||||
|
}
|
||||||
size_t needed = size + additional;
|
size_t needed = size + additional;
|
||||||
if (needed <= capacity) {
|
if (needed <= capacity) {
|
||||||
return true;
|
return true;
|
||||||
@@ -358,8 +367,9 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
|||||||
// repeated allocations.
|
// repeated allocations.
|
||||||
if (new_capacity < 4096) {
|
if (new_capacity < 4096) {
|
||||||
new_capacity *= 2;
|
new_capacity *= 2;
|
||||||
} else {
|
// overflow guard: ensure new_capacity + new_capacity/2 does not overflow
|
||||||
new_capacity += new_capacity/2; // grow by 1.5x
|
} else if (new_capacity + new_capacity / 2 > new_capacity) {
|
||||||
|
new_capacity += new_capacity / 2; // grow by 1.5x
|
||||||
}
|
}
|
||||||
char *new_data = internal::allocate_padded_buffer(new_capacity);
|
char *new_data = internal::allocate_padded_buffer(new_capacity);
|
||||||
if (new_data == nullptr) {
|
if (new_data == nullptr) {
|
||||||
|
|||||||
@@ -6,6 +6,15 @@
|
|||||||
|
|
||||||
#include <cstring> /* memcmp */
|
#include <cstring> /* memcmp */
|
||||||
|
|
||||||
|
// for page size computation.
|
||||||
|
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||||
|
#include <unistd.h>
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
|
|
||||||
inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept
|
inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept
|
||||||
@@ -95,7 +104,83 @@ inline padded_string_view pad_with_reserve(std::string& s) noexcept {
|
|||||||
return padded_string_view(s.data(), s.size(), s.capacity());
|
return padded_string_view(s.data(), s.size(), s.capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint32_t get_page_size() noexcept {
|
||||||
|
#if defined(_WINDOWS_) // if and only if someone loaded Windows.h, we can get the page size from there.
|
||||||
|
// Otherwise, we assume 4096.
|
||||||
|
static const uint32_t cached = []() -> uint32_t {
|
||||||
|
SYSTEM_INFO si;
|
||||||
|
GetSystemInfo(&si);
|
||||||
|
return static_cast<std::uint32_t>(si.dwPageSize);
|
||||||
|
}();
|
||||||
|
return cached;
|
||||||
|
#elif defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||||
|
static const uint32_t cached = []() -> uint32_t {
|
||||||
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
if (page_size > 0) {
|
||||||
|
return static_cast<uint32_t>(page_size);
|
||||||
|
}
|
||||||
|
return 4096; // fallback
|
||||||
|
}();
|
||||||
|
return cached;
|
||||||
|
#else
|
||||||
|
return 4096; // fallback
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
|
inline padded_input::padded_input(std::string_view sv)
|
||||||
|
: storage(simdjson::padded_string_view{}) {
|
||||||
|
if (needs_allocation(sv.data(), sv.size())) {
|
||||||
|
storage = simdjson::padded_string(sv);
|
||||||
|
} else {
|
||||||
|
storage = simdjson::padded_string_view(
|
||||||
|
sv.data(), sv.size(), sv.size() + simdjson::SIMDJSON_PADDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline padded_input::padded_input(const char *data, size_t length)
|
||||||
|
: storage(simdjson::padded_string_view{}) {
|
||||||
|
if (needs_allocation(data, length)) {
|
||||||
|
storage = simdjson::padded_string(data, length);
|
||||||
|
} else {
|
||||||
|
storage = simdjson::padded_string_view(
|
||||||
|
data, length, length + simdjson::SIMDJSON_PADDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline padded_input::padded_input(const std::string &s)
|
||||||
|
: storage(simdjson::padded_string_view{}) {
|
||||||
|
const size_t len = s.size();
|
||||||
|
const size_t cap = s.capacity();
|
||||||
|
// Here we have the string content from data() to data() + size(),
|
||||||
|
// but the memory is accessible from data() to data() + capacity().
|
||||||
|
const size_t needed_padding = (cap - len) < simdjson::SIMDJSON_PADDING
|
||||||
|
? simdjson::SIMDJSON_PADDING - (cap - len) : 0;
|
||||||
|
if (needed_padding > 0 && needs_allocation(s.data(), cap, needed_padding)) {
|
||||||
|
storage = simdjson::padded_string(s);
|
||||||
|
} else {
|
||||||
|
storage = simdjson::padded_string_view(
|
||||||
|
s.data(), len, len + simdjson::SIMDJSON_PADDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool padded_input::is_view() const noexcept {
|
||||||
|
return std::holds_alternative<simdjson::padded_string_view>(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline padded_input::operator simdjson::padded_string_view() const noexcept {
|
||||||
|
return std::visit([](const auto& p) -> simdjson::padded_string_view {
|
||||||
|
return p;
|
||||||
|
}, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool padded_input::needs_allocation(const char* buf, size_t len, size_t padding) noexcept {
|
||||||
|
if(len == 0) { return false; }
|
||||||
|
const auto page_size = get_page_size();
|
||||||
|
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size)
|
||||||
|
+ padding >= static_cast<uintptr_t>(page_size));
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
#include <variant>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
|
|
||||||
@@ -73,6 +76,68 @@ public:
|
|||||||
|
|
||||||
}; // padded_string_view
|
}; // padded_string_view
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the system's memory page size. By default, we return
|
||||||
|
* 4096 bytes, which is the most common page size. On systems
|
||||||
|
* where the page size is not a multiple of 4096 bytes, and not
|
||||||
|
* a unix-like system, nor Windows, this function may return an
|
||||||
|
* incorrect value.
|
||||||
|
*
|
||||||
|
* @return The page size in bytes.
|
||||||
|
*/
|
||||||
|
inline uint32_t get_page_size() noexcept;
|
||||||
|
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
/**
|
||||||
|
* A padded_input is a wrapper around either a padded_string_view or a padded_string.
|
||||||
|
* It will automatically pad a string_view if it does not have sufficient padding
|
||||||
|
* up to the end of the memory page. Note that a requirement for this method to
|
||||||
|
* make sense is to be on a system with a page size of at least 4096 (which is
|
||||||
|
* universal except on some embedded systems).
|
||||||
|
*/
|
||||||
|
struct padded_input {
|
||||||
|
/**
|
||||||
|
* Construct a padded_input from a string_view. If the string_view does not have sufficient padding,
|
||||||
|
* the data will be copied into a padded_string and the padded_string_view will point to the
|
||||||
|
* padded_string's data. Otherwise, the padded_string_view will point to the original string_view's data.
|
||||||
|
*/
|
||||||
|
inline explicit padded_input(std::string_view sv);
|
||||||
|
/**
|
||||||
|
* Construct a padded_input from a C-style string (length specified). If the string does not have sufficient padding,
|
||||||
|
* the data will be copied into a padded_string and the padded_string_view will point to the
|
||||||
|
* padded_string's data. Otherwise, the padded_string_view will point to the original string's data.
|
||||||
|
*/
|
||||||
|
inline explicit padded_input(const char *data, size_t length);
|
||||||
|
/**
|
||||||
|
* Construct a padded_input from a std::string. If the string does not have sufficient padding
|
||||||
|
* (considering its capacity), the data will be copied into a padded_string and the padded_string_view
|
||||||
|
* will point to the padded_string's data. Otherwise, the padded_string_view will point to the
|
||||||
|
* original string's data.
|
||||||
|
*/
|
||||||
|
inline explicit padded_input(const std::string &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the padded_input is a view.
|
||||||
|
*
|
||||||
|
* @return true if the padded_input is a view, false otherwise.
|
||||||
|
*/
|
||||||
|
inline bool is_view() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the padded_input to a padded_string_view.
|
||||||
|
*
|
||||||
|
* @return The padded_string_view.
|
||||||
|
*/
|
||||||
|
inline operator simdjson::padded_string_view() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<simdjson::padded_string_view, simdjson::padded_string> storage;
|
||||||
|
// whether we cross a page boundary and need to allocate a new padded string.
|
||||||
|
static inline bool needs_allocation(const char* buf, size_t len, size_t padding = SIMDJSON_PADDING) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
/**
|
/**
|
||||||
* Send padded_string instance to an output stream.
|
* Send padded_string instance to an output stream.
|
||||||
@@ -105,6 +170,7 @@ inline padded_string_view pad(std::string& s) noexcept;
|
|||||||
* @return The padded string.
|
* @return The padded string.
|
||||||
*/
|
*/
|
||||||
inline padded_string_view pad_with_reserve(std::string& s) noexcept;
|
inline padded_string_view pad_with_reserve(std::string& s) noexcept;
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_PADDED_STRING_VIEW_H
|
#endif // SIMDJSON_PADDED_STRING_VIEW_H
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||||
|
|
||||||
/** The version of simdjson being used (major.minor.revision) */
|
/** The version of simdjson being used (major.minor.revision) */
|
||||||
#define SIMDJSON_VERSION "4.5.0"
|
#define SIMDJSON_VERSION "4.6.1"
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
enum {
|
enum {
|
||||||
@@ -15,11 +15,11 @@ enum {
|
|||||||
/**
|
/**
|
||||||
* The minor version (major.MINOR.revision) of simdjson being used.
|
* The minor version (major.MINOR.revision) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_MINOR = 5,
|
SIMDJSON_VERSION_MINOR = 6,
|
||||||
/**
|
/**
|
||||||
* The revision (major.minor.REVISION) of simdjson being used.
|
* The revision (major.minor.REVISION) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_REVISION = 0
|
SIMDJSON_VERSION_REVISION = 1
|
||||||
};
|
};
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"app_name": "MyApp",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"port": 8080,
|
||||||
|
"debug": true,
|
||||||
|
"features": ["logging", "caching"],
|
||||||
|
"database": {
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 5432
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -268,7 +268,21 @@ class SimdjsonFile:
|
|||||||
print(f" Adding include: {self} includes {include}")
|
print(f" Adding include: {self} includes {include}")
|
||||||
if self.is_conditional_include:
|
if self.is_conditional_include:
|
||||||
# If I have a dependency file, I can only include something that has a dependency file.
|
# If I have a dependency file, I can only include something that has a dependency file.
|
||||||
assert include.is_conditional_include, f"Error: Amalgamated file '{self}' is trying to include '{include}', but '{include}' is not an amalgamated file. Amalgamated files can only include other amalgamated files to maintain conditional inclusion structure. Check the inclusion rules in the script's 'rules' variable. {rules}"
|
if not include.is_conditional_include:
|
||||||
|
dep = self.dependency_file
|
||||||
|
dep_hint = f" and add it to the dependency file '{dep}'" if dep else ""
|
||||||
|
raise AssertionError(
|
||||||
|
f"Error: Amalgamated file '{self}' is trying to include '{include}', "
|
||||||
|
f"but '{include}' is not an amalgamated file.\n\n"
|
||||||
|
f"FIX: Wrap the #include \"{include}\" in a conditional block:\n\n"
|
||||||
|
f" #ifndef SIMDJSON_CONDITIONAL_INCLUDE\n"
|
||||||
|
f" #include \"{include}\"\n"
|
||||||
|
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
|
||||||
|
f"This makes the include editor-only (skipped during amalgamation){dep_hint}.\n\n"
|
||||||
|
f"During amalgamation, '{include}' is already included earlier in the "
|
||||||
|
f"amalgamated output, so it does not need to be included again.\n\n"
|
||||||
|
f"{rules}"
|
||||||
|
)
|
||||||
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
||||||
# if include.amalgamator_file:
|
# if include.amalgamator_file:
|
||||||
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
||||||
@@ -425,8 +439,10 @@ class Amalgamator:
|
|||||||
self.implementation = "SIMDJSON_BUILTIN_IMPLEMENTATION"
|
self.implementation = "SIMDJSON_BUILTIN_IMPLEMENTATION"
|
||||||
|
|
||||||
assert not self.editor_only_region, f"Error: Already in an editor-only region when starting to write '{file}'. Ensure proper nesting of conditional blocks."
|
assert not self.editor_only_region, f"Error: Already in an editor-only region when starting to write '{file}'. Ensure proper nesting of conditional blocks."
|
||||||
|
editor_only_start_line = None
|
||||||
|
bare_endif_lines = []
|
||||||
with open(file.absolute_path, 'r') as fid2:
|
with open(file.absolute_path, 'r') as fid2:
|
||||||
for line in fid2:
|
for line_number, line in enumerate(fid2, 1):
|
||||||
line = line.rstrip('\n')
|
line = line.rstrip('\n')
|
||||||
|
|
||||||
# Ignore #pragma once, it causes warnings if it ends up in a .cpp file
|
# Ignore #pragma once, it causes warnings if it ends up in a .cpp file
|
||||||
@@ -439,6 +455,7 @@ class Amalgamator:
|
|||||||
assert self.in_conditional_include_block, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' without a prior '#define SIMDJSON_CONDITIONAL_INCLUDE'. Ensure the define comes first. Stack: {self.include_stack}. {rules}"
|
assert self.in_conditional_include_block, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' without a prior '#define SIMDJSON_CONDITIONAL_INCLUDE'. Ensure the define comes first. Stack: {self.include_stack}. {rules}"
|
||||||
assert not self.editor_only_region, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' twice in a row. Ensure conditional blocks are properly nested and closed. {rules}"
|
assert not self.editor_only_region, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' twice in a row. Ensure conditional blocks are properly nested and closed. {rules}"
|
||||||
self.editor_only_region = True
|
self.editor_only_region = True
|
||||||
|
editor_only_start_line = line_number
|
||||||
|
|
||||||
# Handle ignored lines (and ending ignore blocks)
|
# Handle ignored lines (and ending ignore blocks)
|
||||||
end_ignore = endif_conditional_re.search(line)
|
end_ignore = endif_conditional_re.search(line)
|
||||||
@@ -453,6 +470,12 @@ class Amalgamator:
|
|||||||
file.add_editor_only_include(included_file)
|
file.add_editor_only_include(included_file)
|
||||||
if end_ignore:
|
if end_ignore:
|
||||||
self.editor_only_region = False
|
self.editor_only_region = False
|
||||||
|
editor_only_start_line = None
|
||||||
|
else:
|
||||||
|
# Track bare #endif lines that might be the intended closer
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped == '#endif' or (stripped.startswith('#endif') and 'SIMDJSON_CONDITIONAL_INCLUDE' not in stripped):
|
||||||
|
bare_endif_lines.append((line_number, line.strip()))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
assert not end_ignore, f"Error: File '{file}' has '#endif // SIMDJSON_CONDITIONAL_INCLUDE' without a matching '#ifndef'. Ensure proper conditional block structure. {rules}"
|
assert not end_ignore, f"Error: File '{file}' has '#endif // SIMDJSON_CONDITIONAL_INCLUDE' without a matching '#ifndef'. Ensure proper conditional block structure. {rules}"
|
||||||
@@ -501,7 +524,35 @@ class Amalgamator:
|
|||||||
|
|
||||||
self.write(line)
|
self.write(line)
|
||||||
|
|
||||||
assert not self.editor_only_region, f"Error: File '{file}' ended without closing the '#endif // SIMDJSON_CONDITIONAL_INCLUDE'. Ensure all conditional blocks are properly closed. {rules}"
|
if self.editor_only_region:
|
||||||
|
msg = (
|
||||||
|
f"Error: File '{file}' ended without closing the "
|
||||||
|
f"'#endif // SIMDJSON_CONDITIONAL_INCLUDE' block "
|
||||||
|
f"(opened at line {editor_only_start_line}).\n\n"
|
||||||
|
)
|
||||||
|
if bare_endif_lines:
|
||||||
|
msg += (
|
||||||
|
f"HINT: Found #endif line(s) inside the block that are missing "
|
||||||
|
f"the required comment. The amalgamation script looks for exactly:\n\n"
|
||||||
|
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
|
||||||
|
f"but found:\n"
|
||||||
|
)
|
||||||
|
for ln, text in bare_endif_lines:
|
||||||
|
msg += f" line {ln}: {text}\n"
|
||||||
|
msg += (
|
||||||
|
f"\nFIX: Change the #endif to:\n\n"
|
||||||
|
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
|
||||||
|
f"The '// SIMDJSON_CONDITIONAL_INCLUDE' comment is required "
|
||||||
|
f"for the amalgamation script to recognize it as the closing "
|
||||||
|
f"of the conditional block.\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
msg += (
|
||||||
|
f"FIX: Add '#endif // SIMDJSON_CONDITIONAL_INCLUDE' to close "
|
||||||
|
f"the '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block.\n"
|
||||||
|
)
|
||||||
|
msg += f"\n{rules}"
|
||||||
|
raise AssertionError(msg)
|
||||||
|
|
||||||
self.write(f"/* end file {self.file_to_str(file)} */")
|
self.write(f"/* end file {self.file_to_str(file)} */")
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* auto-generated on 2026-03-25 17:25:37 -0400. version 4.5.0 Do not edit! */
|
/* auto-generated on 2026-04-02 19:14:16 -0400. version 4.6.1 Do not edit! */
|
||||||
/* including simdjson.cpp: */
|
/* including simdjson.cpp: */
|
||||||
/* begin file simdjson.cpp */
|
/* begin file simdjson.cpp */
|
||||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||||
@@ -3168,6 +3168,11 @@ struct fixed_string {
|
|||||||
data[i] = str[i];
|
data[i] = str[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr fixed_string(const unsigned char (&str)[N]) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
data[i] = static_cast<char>(str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
char data[N];
|
char data[N];
|
||||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||||
constexpr size_t size() const { return N ; }
|
constexpr size_t size() const { return N ; }
|
||||||
|
|||||||
+1617
-547
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+58
-2
@@ -226,9 +226,15 @@ simdjson_warn_unused simdjson_inline error_code scan() {
|
|||||||
add_structural();
|
add_structural();
|
||||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||||
} else {
|
} else {
|
||||||
// Anything else, add the structural and go until we find the next one
|
// Anything else, add the structural and go until we find the next one.
|
||||||
|
// We also stop on RS (0x1E) so that RFC 7464 json_sequence inputs
|
||||||
|
// like `\x1e"a"\x1e"b"` produce a separate structural for each RS
|
||||||
|
// rather than being absorbed into a single primitive run. RS is a
|
||||||
|
// control character that is invalid in normal JSON, so breaking
|
||||||
|
// the run here has no effect on well-formed non-json_sequence
|
||||||
|
// inputs.
|
||||||
add_structural();
|
add_structural();
|
||||||
while (idx+1<len && !char_is_space_or_operator(buf[idx+1])) {
|
while (idx+1<len && !char_is_space_or_operator(buf[idx+1]) && buf[idx+1] != 0x1e) {
|
||||||
idx++;
|
idx++;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -283,6 +289,56 @@ simdjson_warn_unused simdjson_inline error_code scan() {
|
|||||||
// doing.
|
// doing.
|
||||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||||
if (parser.n_structural_indexes == 0) { return EMPTY; }
|
if (parser.n_structural_indexes == 0) { return EMPTY; }
|
||||||
|
} else if (partial == stage1_mode::json_sequence_partial) {
|
||||||
|
// RFC 7464: use RS positions for batch boundaries
|
||||||
|
if(unclosed_string) {
|
||||||
|
parser.n_structural_indexes--;
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
|
||||||
|
}
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
auto new_structural_indexes = find_next_document_index_json_sequence(parser, len, false, next_batch_start);
|
||||||
|
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
if (new_structural_indexes == 0) {
|
||||||
|
parser.n_structural_indexes = 0;
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
parser.n_structural_indexes = new_structural_indexes;
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||||
|
} else if (partial == stage1_mode::json_sequence_final) {
|
||||||
|
// RFC 7464: final batch, last document extends to EOF
|
||||||
|
if(unclosed_string) { parser.n_structural_indexes--; }
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
parser.n_structural_indexes = find_next_document_index_json_sequence(parser, len, true, next_batch_start);
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; }
|
||||||
|
} else if (partial == stage1_mode::comma_delimited_partial) {
|
||||||
|
// Comma-delimited: filter root-level commas, use comma positions for batch boundaries
|
||||||
|
if(unclosed_string) {
|
||||||
|
parser.n_structural_indexes--;
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
|
||||||
|
}
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
auto new_structural_indexes = filter_comma_delimited(parser, len, false, next_batch_start);
|
||||||
|
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
if (new_structural_indexes == 0) {
|
||||||
|
parser.n_structural_indexes = 0;
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
parser.n_structural_indexes = new_structural_indexes;
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||||
|
} else if (partial == stage1_mode::comma_delimited_final) {
|
||||||
|
// Comma-delimited: final batch, last document extends to EOF
|
||||||
|
if(unclosed_string) { parser.n_structural_indexes--; }
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
parser.n_structural_indexes = filter_comma_delimited(parser, len, true, next_batch_start);
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; }
|
||||||
} else if(unclosed_string) { error = UNCLOSED_STRING; }
|
} else if(unclosed_string) { error = UNCLOSED_STRING; }
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,9 +97,280 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sentinel value returned to indicate a document started but didn't fit
|
||||||
|
* (CAPACITY error), as opposed to 0 which means no document content found
|
||||||
|
* (EMPTY).
|
||||||
|
*/
|
||||||
|
constexpr uint32_t DOCUMENT_TOO_LARGE = UINT32_MAX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For RFC 7464 JSON text sequences, filter RS from structural indexes and
|
||||||
|
* find batch boundaries.
|
||||||
|
*
|
||||||
|
* In JSON sequence mode, RS (0x1E) marks the start of each JSON text.
|
||||||
|
* RS bytes appear in structural_indexes as they are classified as scalars.
|
||||||
|
* This function:
|
||||||
|
* 1. Scans structural_indexes to find and count RS positions
|
||||||
|
* 2. Filters RS out of structural_indexes in-place
|
||||||
|
* 3. Determines batch boundaries based on RS positions
|
||||||
|
*
|
||||||
|
* @param parser The parser with structural_indexes and buf.
|
||||||
|
* @param len The length of the current batch buffer.
|
||||||
|
* @param is_final True if this is the final batch (no more data coming).
|
||||||
|
* @param next_batch_start Output: offset where the next batch should start.
|
||||||
|
* @return The number of structural indexes to keep (after RS filtering),
|
||||||
|
* 0 if no document content found (EMPTY),
|
||||||
|
* or DOCUMENT_TOO_LARGE if a document started but didn't fit (CAPACITY).
|
||||||
|
*/
|
||||||
|
simdjson_inline uint32_t find_next_document_index_json_sequence(
|
||||||
|
dom_parser_implementation &parser,
|
||||||
|
size_t len,
|
||||||
|
bool is_final,
|
||||||
|
uint32_t &next_batch_start) {
|
||||||
|
// Default: next batch starts at end of buffer
|
||||||
|
next_batch_start = uint32_t(len);
|
||||||
|
|
||||||
|
if (parser.n_structural_indexes == 0) { return 0; }
|
||||||
|
|
||||||
|
// Phase 1: Scan structural_indexes to find RS positions and handle them.
|
||||||
|
// RS marks the start of a JSON text. For objects/arrays, the '{' or '[' after RS
|
||||||
|
// is already in structural_indexes (it's an operator). For scalars like numbers,
|
||||||
|
// the digit following RS is NOT in structural_indexes because the scanner sees
|
||||||
|
// RS as a scalar, making the digit a scalar continuation, not a start.
|
||||||
|
// We must: (1) remove RS from structural_indexes, and (2) for scalars, add the
|
||||||
|
// actual value start position.
|
||||||
|
uint32_t write_idx = 0;
|
||||||
|
uint32_t last_rs_pos = 0;
|
||||||
|
uint32_t rs_count = 0;
|
||||||
|
|
||||||
|
for (uint32_t read_idx = 0; read_idx < parser.n_structural_indexes; read_idx++) {
|
||||||
|
const uint32_t pos = parser.structural_indexes[read_idx];
|
||||||
|
if (parser.buf[pos] == 0x1E) {
|
||||||
|
// This is an RS character - find the actual JSON value start.
|
||||||
|
last_rs_pos = pos;
|
||||||
|
rs_count++;
|
||||||
|
// Skip past this RS and any whitespace *and any additional RSes*
|
||||||
|
// to locate the real value. Consecutive RSes are degenerate
|
||||||
|
// "empty records" per RFC 7464; we collapse them here. They do
|
||||||
|
// not always appear as separate entries in structural_indexes
|
||||||
|
// because the scanner groups runs of adjacent non-whitespace
|
||||||
|
// scalar bytes (including RS) into a single scalar start.
|
||||||
|
uint32_t value_start = pos + 1;
|
||||||
|
while (value_start < len) {
|
||||||
|
const uint8_t c = parser.buf[value_start];
|
||||||
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||||
|
value_start++;
|
||||||
|
} else if (c == 0x1E) {
|
||||||
|
// Collapsed empty record. Still count it so rs_count reflects
|
||||||
|
// the true number of record markers and last_rs_pos tracks
|
||||||
|
// the final one.
|
||||||
|
last_rs_pos = value_start;
|
||||||
|
rs_count++;
|
||||||
|
value_start++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the scanner emitted additional structurals inside the
|
||||||
|
// whitespace+RS run we just walked over (i.e., isolated RSes
|
||||||
|
// separated by whitespace), skip past them so we do not
|
||||||
|
// double-count or double-emit.
|
||||||
|
while (read_idx + 1 < parser.n_structural_indexes &&
|
||||||
|
parser.structural_indexes[read_idx + 1] < value_start) {
|
||||||
|
read_idx++;
|
||||||
|
}
|
||||||
|
// Check if the value start is an operator (always present in
|
||||||
|
// scanner structural_indexes) or a scalar-like start (which may
|
||||||
|
// be missing from structural_indexes and must be added here).
|
||||||
|
// Note: '"' is NOT always in structural_indexes. The scanner
|
||||||
|
// classifies '"' as a scalar character and emits it as a
|
||||||
|
// structural only when it is a *scalar start* (preceded by
|
||||||
|
// whitespace or an operator). When '"' immediately follows an
|
||||||
|
// RS (which the scanner also classifies as scalar), it is
|
||||||
|
// treated as a scalar continuation and not emitted - so we
|
||||||
|
// must add it here just like any other scalar value.
|
||||||
|
if (value_start < len) {
|
||||||
|
const uint8_t c = parser.buf[value_start];
|
||||||
|
const bool is_operator =
|
||||||
|
(c == '{' || c == '}' || c == '[' || c == ']' ||
|
||||||
|
c == ':' || c == ',');
|
||||||
|
// If the next scanner structural is exactly at value_start,
|
||||||
|
// the scanner already emitted it (it followed whitespace) and
|
||||||
|
// we must not add a duplicate - a subsequent iteration will
|
||||||
|
// copy it into write_idx.
|
||||||
|
const bool already_emitted =
|
||||||
|
(read_idx + 1 < parser.n_structural_indexes &&
|
||||||
|
parser.structural_indexes[read_idx + 1] == value_start);
|
||||||
|
if (!is_operator && !already_emitted) {
|
||||||
|
// Scalar value (number/true/false/null/string) - add its
|
||||||
|
// position since scanner missed it.
|
||||||
|
parser.structural_indexes[write_idx++] = value_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not RS, copy to output
|
||||||
|
parser.structural_indexes[write_idx++] = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update structural index count
|
||||||
|
parser.n_structural_indexes = write_idx;
|
||||||
|
|
||||||
|
if (parser.n_structural_indexes == 0) { return 0; }
|
||||||
|
if (rs_count == 0) {
|
||||||
|
// No RS found; for final batch, try generic boundary detection
|
||||||
|
return is_final ? find_next_document_index(parser) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Determine batch boundaries based on RS positions
|
||||||
|
|
||||||
|
if (is_final) {
|
||||||
|
// Final batch: all documents are complete (last one ends at EOF).
|
||||||
|
// In json_sequence mode, RS markers define document boundaries, so all
|
||||||
|
// remaining structurals form complete documents. Return them all directly.
|
||||||
|
// (Calling find_next_document_index() would fail for scalar documents.)
|
||||||
|
return parser.n_structural_indexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial batch: need to find complete documents only.
|
||||||
|
// A document starting at an RS is complete if there is another RS after it.
|
||||||
|
next_batch_start = last_rs_pos;
|
||||||
|
|
||||||
|
if (rs_count < 2) {
|
||||||
|
// Only one RS, so we have at most one document that may be incomplete.
|
||||||
|
// We cannot confirm it is complete without another RS.
|
||||||
|
// Return DOCUMENT_TOO_LARGE if content was found (write_idx > 0), 0 if only separators.
|
||||||
|
return (parser.n_structural_indexes > 0) ? DOCUMENT_TOO_LARGE : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have at least 2 RS markers. The last complete document ends before last_rs_pos.
|
||||||
|
|
||||||
|
// Find the structural index cutoff: keep only structurals < last_rs_pos.
|
||||||
|
// Since we already filtered RS, all remaining structurals are valid.
|
||||||
|
// We iterate backward to find the last structural before last_rs_pos.
|
||||||
|
uint32_t keep_count = 0;
|
||||||
|
for (uint32_t i = parser.n_structural_indexes; i > 0; i--) {
|
||||||
|
if (parser.structural_indexes[i - 1] < last_rs_pos) {
|
||||||
|
keep_count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No structurals before the last RS - no complete documents
|
||||||
|
if (keep_count == 0) { return 0; }
|
||||||
|
|
||||||
|
// All documents before the last RS are complete by definition (the next RS
|
||||||
|
// confirms their end). No need to call find_next_document_index() which
|
||||||
|
// would fail for scalar documents like `1` or `"hello"`.
|
||||||
|
return keep_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter comma-delimited documents by removing root-level commas from
|
||||||
|
* structural indexes.
|
||||||
|
*
|
||||||
|
* For comma-delimited format like `{...},{...},{...}`, we need to remove
|
||||||
|
* the commas that separate documents (depth 0) while preserving commas
|
||||||
|
* inside arrays and objects (depth > 0).
|
||||||
|
*
|
||||||
|
* After filtering, the structural indexes look like whitespace-delimited
|
||||||
|
* documents, so find_next_document_index() works unchanged.
|
||||||
|
*
|
||||||
|
* @param parser The parser with structural_indexes and buf.
|
||||||
|
* @param len The length of the current batch buffer.
|
||||||
|
* @param is_final True if this is the final batch (no more data coming).
|
||||||
|
* @param next_batch_start Output: offset where the next batch should start.
|
||||||
|
* @return The number of structural indexes to keep,
|
||||||
|
* 0 if no document content found (EMPTY),
|
||||||
|
* or DOCUMENT_TOO_LARGE if a document started but didn't fit (CAPACITY).
|
||||||
|
*/
|
||||||
|
simdjson_inline uint32_t filter_comma_delimited(
|
||||||
|
dom_parser_implementation &parser,
|
||||||
|
size_t len,
|
||||||
|
bool is_final,
|
||||||
|
uint32_t &next_batch_start) {
|
||||||
|
// Default: next batch starts at end of buffer
|
||||||
|
next_batch_start = uint32_t(len);
|
||||||
|
|
||||||
|
if (parser.n_structural_indexes == 0) { return 0; }
|
||||||
|
|
||||||
|
// Track depth to identify root-level commas (depth 0)
|
||||||
|
int depth = 0;
|
||||||
|
uint32_t write_idx = 0;
|
||||||
|
uint32_t last_root_comma_pos = 0;
|
||||||
|
uint32_t root_comma_count = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parser.n_structural_indexes; i++) {
|
||||||
|
uint32_t idx = parser.structural_indexes[i];
|
||||||
|
uint8_t c = parser.buf[idx];
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '{': case '[':
|
||||||
|
depth++;
|
||||||
|
parser.structural_indexes[write_idx++] = idx;
|
||||||
|
break;
|
||||||
|
case '}': case ']':
|
||||||
|
depth--;
|
||||||
|
parser.structural_indexes[write_idx++] = idx;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
if (depth == 0) {
|
||||||
|
// Root-level comma = document boundary, skip it
|
||||||
|
last_root_comma_pos = idx;
|
||||||
|
root_comma_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parser.structural_indexes[write_idx++] = idx;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Colons, scalars, etc.
|
||||||
|
parser.structural_indexes[write_idx++] = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update structural index count
|
||||||
|
parser.n_structural_indexes = write_idx;
|
||||||
|
|
||||||
|
if (parser.n_structural_indexes == 0) { return 0; }
|
||||||
|
|
||||||
|
if (is_final) {
|
||||||
|
// Final batch: use standard boundary detection on filtered indexes
|
||||||
|
return find_next_document_index(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial batch: need to find complete documents only.
|
||||||
|
// A document ending with a root comma is complete.
|
||||||
|
if (root_comma_count == 0) {
|
||||||
|
// No root commas found; we cannot confirm any document is complete.
|
||||||
|
// The whole batch might be one incomplete document.
|
||||||
|
// Return DOCUMENT_TOO_LARGE if content was found (write_idx > 0), 0 if only commas.
|
||||||
|
return (parser.n_structural_indexes > 0) ? DOCUMENT_TOO_LARGE : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have at least one root comma. Documents before the last comma are complete.
|
||||||
|
next_batch_start = last_root_comma_pos + 1;
|
||||||
|
|
||||||
|
// Find the structural index cutoff: keep only structurals < last_root_comma_pos
|
||||||
|
uint32_t keep_count = 0;
|
||||||
|
for (uint32_t i = parser.n_structural_indexes; i > 0; i--) {
|
||||||
|
if (parser.structural_indexes[i - 1] < last_root_comma_pos) {
|
||||||
|
keep_count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep_count == 0) { return 0; }
|
||||||
|
|
||||||
|
// Use standard boundary detection on the complete portion
|
||||||
|
parser.n_structural_indexes = keep_count;
|
||||||
|
return find_next_document_index(parser);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace stage1
|
} // namespace stage1
|
||||||
} // unnamed namespace
|
} // unnamed namespace
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H
|
#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H
|
||||||
|
|||||||
@@ -314,7 +314,6 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.n_structural_indexes = new_structural_indexes;
|
parser.n_structural_indexes = new_structural_indexes;
|
||||||
} else if (partial == stage1_mode::streaming_final) {
|
} else if (partial == stage1_mode::streaming_final) {
|
||||||
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||||
@@ -342,6 +341,56 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
// the trailing garbage.
|
// the trailing garbage.
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
|
} else if (partial == stage1_mode::json_sequence_partial) {
|
||||||
|
// RFC 7464: use RS positions for batch boundaries
|
||||||
|
if(have_unclosed_string) {
|
||||||
|
parser.n_structural_indexes--;
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
|
||||||
|
}
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
auto new_structural_indexes = find_next_document_index_json_sequence(parser, len, false, next_batch_start);
|
||||||
|
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
if (new_structural_indexes == 0) {
|
||||||
|
parser.n_structural_indexes = 0;
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
parser.n_structural_indexes = new_structural_indexes;
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||||
|
} else if (partial == stage1_mode::json_sequence_final) {
|
||||||
|
// RFC 7464: final batch, last document extends to EOF
|
||||||
|
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
parser.n_structural_indexes = find_next_document_index_json_sequence(parser, len, true, next_batch_start);
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return EMPTY; }
|
||||||
|
} else if (partial == stage1_mode::comma_delimited_partial) {
|
||||||
|
// Comma-delimited: filter root-level commas, use comma positions for batch boundaries
|
||||||
|
if(have_unclosed_string) {
|
||||||
|
parser.n_structural_indexes--;
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
|
||||||
|
}
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
auto new_structural_indexes = filter_comma_delimited(parser, len, false, next_batch_start);
|
||||||
|
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
if (new_structural_indexes == 0) {
|
||||||
|
parser.n_structural_indexes = 0;
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
parser.n_structural_indexes = new_structural_indexes;
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||||
|
} else if (partial == stage1_mode::comma_delimited_final) {
|
||||||
|
// Comma-delimited: final batch, last document extends to EOF
|
||||||
|
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||||
|
uint32_t next_batch_start = uint32_t(len);
|
||||||
|
parser.n_structural_indexes = filter_comma_delimited(parser, len, true, next_batch_start);
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||||
|
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||||
|
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return EMPTY; }
|
||||||
}
|
}
|
||||||
checker.check_eof();
|
checker.check_eof();
|
||||||
return checker.errors();
|
return checker.errors();
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ include(${PROJECT_SOURCE_DIR}/cmake/add_cpp_test.cmake)
|
|||||||
add_subdirectory(dom)
|
add_subdirectory(dom)
|
||||||
add_subdirectory(ondemand)
|
add_subdirectory(ondemand)
|
||||||
|
|
||||||
|
# compilation_failure_tests is added before the global link_libraries(simdjson) so that
|
||||||
|
# multiple_include/myexe does not receive a duplicate simdjson entry alongside the one
|
||||||
|
# already propagated by mylib PUBLIC simdjson::simdjson.
|
||||||
|
add_subdirectory(compilation_failure_tests)
|
||||||
|
|
||||||
# All remaining tests link with simdjson proper
|
# All remaining tests link with simdjson proper
|
||||||
link_libraries(simdjson)
|
link_libraries(simdjson)
|
||||||
@@ -27,6 +31,5 @@ endif()
|
|||||||
# SIMDJSON_FORCE_IMPLEMENTATION, so we know we're testing what we think we're testing
|
# SIMDJSON_FORCE_IMPLEMENTATION, so we know we're testing what we think we're testing
|
||||||
add_cpp_test(checkimplementation LABELS other per_implementation)
|
add_cpp_test(checkimplementation LABELS other per_implementation)
|
||||||
|
|
||||||
add_subdirectory(compilation_failure_tests)
|
|
||||||
add_subdirectory(builder)
|
add_subdirectory(builder)
|
||||||
add_subdirectory(compile_time)
|
add_subdirectory(compile_time)
|
||||||
@@ -6,40 +6,40 @@
|
|||||||
|
|
||||||
using namespace simdjson;
|
using namespace simdjson;
|
||||||
struct Car {
|
struct Car {
|
||||||
std::string make;
|
std::string make{};
|
||||||
std::string model;
|
std::string model{};
|
||||||
int64_t year;
|
int64_t year{};
|
||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kid {
|
struct kid {
|
||||||
int age;
|
int age{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
std::vector<std::string> toys;
|
std::vector<std::string> toys{};
|
||||||
bool operator<=> (const kid&) const = default;
|
bool operator<=> (const kid&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Z {
|
struct Z {
|
||||||
int x;
|
int x{};
|
||||||
bool operator<=> (const Z&) const = default;
|
bool operator<=> (const Z&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Y {
|
struct Y {
|
||||||
int g;
|
int g{};
|
||||||
std::string h;
|
std::string h{};
|
||||||
std::vector<int> i;
|
std::vector<int> i{};
|
||||||
Z z;
|
Z z{};
|
||||||
bool operator<=> (const Y&) const = default;
|
bool operator<=> (const Y&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct X {
|
struct X {
|
||||||
char a;
|
char a{};
|
||||||
int b;
|
int b{};
|
||||||
int c;
|
int c{};
|
||||||
std::string d;
|
std::string d{};
|
||||||
std::vector<int> e;
|
std::vector<int> e{};
|
||||||
std::vector<std::string> f;
|
std::vector<std::string> f{};
|
||||||
Y y;
|
Y y{};
|
||||||
bool operator<=> (const X&) const = default;
|
bool operator<=> (const X&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace builder_tests {
|
|||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
// Custom type for testing tag_invoke with extract_into
|
// Custom type for testing tag_invoke with extract_into
|
||||||
struct Price {
|
struct Price {
|
||||||
double amount;
|
double amount{};
|
||||||
std::string currency;
|
std::string currency{};
|
||||||
|
|
||||||
// Custom deserializer that applies currency conversion
|
// Custom deserializer that applies currency conversion
|
||||||
friend error_code tag_invoke(deserialize_tag,
|
friend error_code tag_invoke(deserialize_tag,
|
||||||
@@ -46,17 +46,37 @@ namespace builder_tests {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int year{};
|
||||||
|
double price{};
|
||||||
|
bool electric{};
|
||||||
|
};
|
||||||
|
struct CarOpt {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
double price;
|
||||||
|
std::optional<std::string> color;
|
||||||
|
};
|
||||||
|
struct CarColor {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
double price;
|
||||||
|
std::string color;
|
||||||
|
};
|
||||||
struct Product {
|
struct Product {
|
||||||
std::string name;
|
std::string name{};
|
||||||
Price price; // Custom deserializable type
|
Price price{}; // Custom deserializable type
|
||||||
int stock;
|
int stock{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Another custom type for testing
|
// Another custom type for testing
|
||||||
struct Dimensions {
|
struct Dimensions {
|
||||||
double value;
|
double value{};
|
||||||
std::string unit;
|
std::string unit{};
|
||||||
|
|
||||||
// Custom deserializer that converts to metric
|
// Custom deserializer that converts to metric
|
||||||
friend error_code tag_invoke(deserialize_tag,
|
friend error_code tag_invoke(deserialize_tag,
|
||||||
@@ -89,6 +109,13 @@ namespace builder_tests {
|
|||||||
Dimensions weight;
|
Dimensions weight;
|
||||||
Dimensions length;
|
Dimensions length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Person {
|
||||||
|
std::string name;
|
||||||
|
int age;
|
||||||
|
std::optional<std::string> email;
|
||||||
|
std::optional<std::string> phone;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool test_primitive_types() {
|
bool test_primitive_types() {
|
||||||
@@ -132,8 +159,8 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct StringTypes {
|
struct StringTypes {
|
||||||
std::string string_val;
|
std::string string_val{};
|
||||||
std::string_view string_view_val;
|
std::string_view string_view_val{};
|
||||||
};
|
};
|
||||||
|
|
||||||
StringTypes test{"hello world", "test_view"};
|
StringTypes test{"hello world", "test_view"};
|
||||||
@@ -159,10 +186,10 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct OptionalTypes {
|
struct OptionalTypes {
|
||||||
std::optional<int> opt_int_with_value;
|
std::optional<int> opt_int_with_value{};
|
||||||
std::optional<std::string> opt_string_with_value;
|
std::optional<std::string> opt_string_with_value{};
|
||||||
std::optional<int> opt_int_null;
|
std::optional<int> opt_int_null{};
|
||||||
std::optional<std::string> opt_string_null;
|
std::optional<std::string> opt_string_null{};
|
||||||
};
|
};
|
||||||
|
|
||||||
OptionalTypes test;
|
OptionalTypes test;
|
||||||
@@ -200,11 +227,11 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct SmartPointerTypes {
|
struct SmartPointerTypes {
|
||||||
std::unique_ptr<int> unique_int_with_value;
|
std::unique_ptr<int> unique_int_with_value{};
|
||||||
std::shared_ptr<std::string> shared_string_with_value;
|
std::shared_ptr<std::string> shared_string_with_value{};
|
||||||
std::unique_ptr<bool> unique_bool_with_value;
|
std::unique_ptr<bool> unique_bool_with_value{};
|
||||||
std::unique_ptr<int> unique_int_null;
|
std::unique_ptr<int> unique_int_null{};
|
||||||
std::shared_ptr<std::string> shared_string_null;
|
std::shared_ptr<std::string> shared_string_null{};
|
||||||
};
|
};
|
||||||
|
|
||||||
SmartPointerTypes test;
|
SmartPointerTypes test;
|
||||||
@@ -245,9 +272,9 @@ namespace builder_tests {
|
|||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
// Test basic container types
|
// Test basic container types
|
||||||
struct ContainerTypes {
|
struct ContainerTypes {
|
||||||
std::vector<int> int_vector;
|
std::vector<int> int_vector{};
|
||||||
std::set<std::string> string_set;
|
std::set<std::string> string_set{};
|
||||||
std::map<std::string, int> string_map;
|
std::map<std::string, int> string_map{};
|
||||||
};
|
};
|
||||||
|
|
||||||
ContainerTypes test;
|
ContainerTypes test;
|
||||||
@@ -278,8 +305,8 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test std::list with iterator-based serialization
|
// Test std::list with iterator-based serialization
|
||||||
struct ListContainer {
|
struct ListContainer {
|
||||||
std::list<int> int_list;
|
std::list<int> int_list{};
|
||||||
std::list<std::string> string_list;
|
std::list<std::string> string_list{};
|
||||||
};
|
};
|
||||||
|
|
||||||
ListContainer list_test;
|
ListContainer list_test;
|
||||||
@@ -319,13 +346,7 @@ namespace builder_tests {
|
|||||||
bool test_extract_into() {
|
bool test_extract_into() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct Car {
|
|
||||||
std::string make;
|
|
||||||
std::string model;
|
|
||||||
int year;
|
|
||||||
double price;
|
|
||||||
std::optional<std::string> color;
|
|
||||||
};
|
|
||||||
|
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
|
|
||||||
@@ -341,7 +362,7 @@ namespace builder_tests {
|
|||||||
"transmission": "Automatic"
|
"transmission": "Automatic"
|
||||||
})"_padded;
|
})"_padded;
|
||||||
|
|
||||||
Car car{};
|
CarOpt car{};
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
ASSERT_SUCCESS( parser.iterate(padded).get(doc) );
|
ASSERT_SUCCESS( parser.iterate(padded).get(doc) );
|
||||||
|
|
||||||
@@ -377,7 +398,7 @@ namespace builder_tests {
|
|||||||
"color": "Red"
|
"color": "Red"
|
||||||
})"_padded;
|
})"_padded;
|
||||||
|
|
||||||
Car car{};
|
CarOpt car{};
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||||
|
|
||||||
@@ -402,7 +423,7 @@ namespace builder_tests {
|
|||||||
"price": 35999.99
|
"price": 35999.99
|
||||||
})"_padded;
|
})"_padded;
|
||||||
|
|
||||||
Car car{};
|
CarOpt car{};
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||||
|
|
||||||
@@ -495,13 +516,6 @@ namespace builder_tests {
|
|||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
// Test 1: Extract specific fields from Car struct
|
// Test 1: Extract specific fields from Car struct
|
||||||
{
|
{
|
||||||
struct Car {
|
|
||||||
std::string make;
|
|
||||||
std::string model;
|
|
||||||
int year;
|
|
||||||
double price;
|
|
||||||
bool electric;
|
|
||||||
};
|
|
||||||
|
|
||||||
Car car{"Tesla", "Model 3", 2023, 42000.0, true};
|
Car car{"Tesla", "Model 3", 2023, 42000.0, true};
|
||||||
|
|
||||||
@@ -533,13 +547,7 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test 2: Extract different field combination
|
// Test 2: Extract different field combination
|
||||||
{
|
{
|
||||||
struct Car {
|
|
||||||
std::string make;
|
|
||||||
std::string model;
|
|
||||||
int year;
|
|
||||||
double price;
|
|
||||||
bool electric;
|
|
||||||
};
|
|
||||||
|
|
||||||
Car car{"Ford", "F-150", 2024, 55000.0, false};
|
Car car{"Ford", "F-150", 2024, 55000.0, false};
|
||||||
|
|
||||||
@@ -566,13 +574,6 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test 3: Extract from struct with optional fields
|
// Test 3: Extract from struct with optional fields
|
||||||
{
|
{
|
||||||
struct Person {
|
|
||||||
std::string name;
|
|
||||||
int age;
|
|
||||||
std::optional<std::string> email;
|
|
||||||
std::optional<std::string> phone;
|
|
||||||
};
|
|
||||||
|
|
||||||
Person person{"John Doe", 30, "john@example.com", std::nullopt};
|
Person person{"John Doe", 30, "john@example.com", std::nullopt};
|
||||||
|
|
||||||
// Extract name and email
|
// Extract name and email
|
||||||
@@ -594,13 +595,6 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test 4: Extract with optional that has value
|
// Test 4: Extract with optional that has value
|
||||||
{
|
{
|
||||||
struct Person {
|
|
||||||
std::string name;
|
|
||||||
int age;
|
|
||||||
std::optional<std::string> email;
|
|
||||||
std::optional<std::string> phone;
|
|
||||||
};
|
|
||||||
|
|
||||||
Person person{"Jane Smith", 25, "jane@example.com", "555-1234"};
|
Person person{"Jane Smith", 25, "jane@example.com", "555-1234"};
|
||||||
|
|
||||||
// Extract name, age, and phone
|
// Extract name, age, and phone
|
||||||
@@ -629,21 +623,21 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test 5: Round-trip test - serialize with extract_from, deserialize with extract_into
|
// Test 5: Round-trip test - serialize with extract_from, deserialize with extract_into
|
||||||
{
|
{
|
||||||
struct Product {
|
struct MyProduct {
|
||||||
std::string id;
|
std::string id{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
double price;
|
double price{};
|
||||||
int stock;
|
int stock{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Product original{"P123", "Widget", 19.99, 100};
|
MyProduct original{"P123", "Widget", 19.99, 100};
|
||||||
|
|
||||||
// Extract specific fields to JSON
|
// Extract specific fields to JSON
|
||||||
std::string json;
|
std::string json;
|
||||||
ASSERT_SUCCESS((extract_from<"id", "name", "price">(original).get(json)));
|
ASSERT_SUCCESS((extract_from<"id", "name", "price">(original).get(json)));
|
||||||
|
|
||||||
// Parse and extract back
|
// Parse and extract back
|
||||||
Product restored{"", "", 0.0, 0};
|
MyProduct restored{"", "", 0.0, 0};
|
||||||
auto padded = pad(json);
|
auto padded = pad(json);
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
auto doc = parser.iterate(padded);
|
auto doc = parser.iterate(padded);
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ using namespace simdjson;
|
|||||||
// Suppose that we want to serialize/deserialize Car using
|
// Suppose that we want to serialize/deserialize Car using
|
||||||
// strings for the year
|
// strings for the year
|
||||||
struct Car {
|
struct Car {
|
||||||
std::string make;
|
std::string make{};
|
||||||
std::string model;
|
std::string model{};
|
||||||
int64_t year;
|
int64_t year{};
|
||||||
std::vector<float> tire_pressure;
|
std::vector<float> tire_pressure{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct EmptyValues {
|
struct EmptyValues {
|
||||||
std::string empty_string;
|
std::string empty_string{};
|
||||||
std::vector<int> empty_vector;
|
std::vector<int> empty_vector{};
|
||||||
std::optional<int> null_optional;
|
std::optional<int> null_optional{};
|
||||||
std::unique_ptr<int> null_unique_ptr;
|
std::unique_ptr<int> null_unique_ptr{};
|
||||||
std::shared_ptr<std::string> null_shared_ptr;
|
std::shared_ptr<std::string> null_shared_ptr{};
|
||||||
};
|
};
|
||||||
|
|
||||||
EmptyValues test;
|
EmptyValues test;
|
||||||
@@ -56,18 +56,18 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct SpecialChars {
|
struct SpecialChars {
|
||||||
std::string quotes;
|
std::string quotes{};
|
||||||
std::string backslashes;
|
std::string backslashes{};
|
||||||
std::string newlines;
|
std::string newlines{};
|
||||||
std::string unicode;
|
std::string unicode{};
|
||||||
char null_char;
|
char null_char{};
|
||||||
};
|
};
|
||||||
|
|
||||||
SpecialChars test;
|
SpecialChars test;
|
||||||
test.quotes = "He said \"Hello\"";
|
test.quotes = "He said \"Hello\"";
|
||||||
test.backslashes = "Path\\to\\file";
|
test.backslashes = "Path\\to\\file";
|
||||||
test.newlines = "Line1\nLine2\tTabbed";
|
test.newlines = "Line1\nLine2\tTabbed";
|
||||||
test.unicode = "Café résumé";
|
test.unicode = "Caf\xc3\xa9 r\xc3\xa9sum\xc3\xa9";
|
||||||
test.null_char = '\0';
|
test.null_char = '\0';
|
||||||
|
|
||||||
std::string json;
|
std::string json;
|
||||||
@@ -82,10 +82,10 @@ namespace builder_tests {
|
|||||||
|
|
||||||
// Test round-trip (excluding null char which has special handling)
|
// Test round-trip (excluding null char which has special handling)
|
||||||
struct SpecialCharsNoNull {
|
struct SpecialCharsNoNull {
|
||||||
std::string quotes;
|
std::string quotes{};
|
||||||
std::string backslashes;
|
std::string backslashes{};
|
||||||
std::string newlines;
|
std::string newlines{};
|
||||||
std::string unicode;
|
std::string unicode{};
|
||||||
};
|
};
|
||||||
|
|
||||||
SpecialCharsNoNull test_no_null;
|
SpecialCharsNoNull test_no_null;
|
||||||
@@ -157,15 +157,15 @@ namespace builder_tests {
|
|||||||
TEST_START();
|
TEST_START();
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
struct Inner {
|
struct Inner {
|
||||||
int value;
|
int value{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Outer {
|
struct Outer {
|
||||||
Inner inner_obj;
|
Inner inner_obj{};
|
||||||
std::vector<Inner> inner_vector;
|
std::vector<Inner> inner_vector{};
|
||||||
std::optional<Inner> optional_inner;
|
std::optional<Inner> optional_inner{};
|
||||||
std::unique_ptr<Inner> unique_inner;
|
std::unique_ptr<Inner> unique_inner{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Outer test;
|
Outer test;
|
||||||
@@ -213,4 +213,4 @@ namespace builder_tests {
|
|||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
return test_main(argc, argv, builder_tests::run);
|
return test_main(argc, argv, builder_tests::run);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ namespace builder_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct StatusStruct {
|
struct StatusStruct {
|
||||||
Status status;
|
Status status{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test deserialization of different enum values with string representation
|
// Test deserialization of different enum values with string representation
|
||||||
@@ -104,9 +104,9 @@ namespace builder_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Task {
|
struct Task {
|
||||||
Priority priority;
|
Priority priority{};
|
||||||
std::string description;
|
std::string description{};
|
||||||
int id;
|
int id{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Task original{Priority::High, "Important task", 123};
|
Task original{Priority::High, "Important task", 123};
|
||||||
@@ -144,8 +144,8 @@ namespace builder_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Response {
|
struct Response {
|
||||||
ErrorCode error;
|
ErrorCode error{};
|
||||||
std::string message;
|
std::string message{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Response test{ErrorCode::NotFound, "Resource not found"};
|
Response test{ErrorCode::NotFound, "Resource not found"};
|
||||||
|
|||||||
@@ -8,26 +8,26 @@ using namespace simdjson;
|
|||||||
|
|
||||||
// Test structures for FracturedJson builder integration
|
// Test structures for FracturedJson builder integration
|
||||||
struct SimpleUser {
|
struct SimpleUser {
|
||||||
int id;
|
int id{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
bool active;
|
bool active{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UserWithEmail {
|
struct UserWithEmail {
|
||||||
int id;
|
int id{};
|
||||||
std::string name;
|
std::string name{};
|
||||||
std::string email;
|
std::string email{};
|
||||||
bool active;
|
bool active{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NestedData {
|
struct NestedData {
|
||||||
std::string title;
|
std::string title{};
|
||||||
std::vector<SimpleUser> users;
|
std::vector<SimpleUser> users{};
|
||||||
int count;
|
int count{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TableTestData {
|
struct TableTestData {
|
||||||
std::vector<SimpleUser> records;
|
std::vector<SimpleUser> records{};
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace fractured_json_builder_tests {
|
namespace fractured_json_builder_tests {
|
||||||
|
|||||||
@@ -14,6 +14,17 @@ function(add_dual_compile_test TEST_NAME)
|
|||||||
endfunction(add_dual_compile_test)
|
endfunction(add_dual_compile_test)
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
|
# Add multiple_include BEFORE link_libraries(simdjson) so that myexe only receives
|
||||||
|
# simdjson once (through mylib PUBLIC simdjson::simdjson) and not a second time from
|
||||||
|
# a directory-wide link entry.
|
||||||
|
add_subdirectory(multiple_include)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Dual-compile tests build executables that reference simdjson symbols, so they need
|
||||||
|
# the library linked in.
|
||||||
|
link_libraries(simdjson)
|
||||||
|
|
||||||
add_dual_compile_test(example_compiletest)
|
add_dual_compile_test(example_compiletest)
|
||||||
# These don't compile with exceptions off
|
# These don't compile with exceptions off
|
||||||
if (SIMDJSON_EXCEPTIONS)
|
if (SIMDJSON_EXCEPTIONS)
|
||||||
@@ -24,9 +35,4 @@ if (SIMDJSON_EXCEPTIONS)
|
|||||||
add_dual_compile_test(dangling_parser_parse_stdstring)
|
add_dual_compile_test(dangling_parser_parse_stdstring)
|
||||||
add_dual_compile_test(dangling_parser_parse_padstring)
|
add_dual_compile_test(dangling_parser_parse_padstring)
|
||||||
add_dual_compile_test(unsafe_parse_many)
|
add_dual_compile_test(unsafe_parse_many)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
|
||||||
# We only check that it builds
|
|
||||||
add_subdirectory(multiple_include)
|
|
||||||
endif()
|
endif()
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
using namespace simdjson;
|
using namespace simdjson;
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
namespace compile_time_json_tests {
|
namespace compile_time_json_tests {
|
||||||
/**
|
/**
|
||||||
@@ -666,27 +667,27 @@ bool test_array_of_objects_with_concept() {
|
|||||||
/**
|
/**
|
||||||
* Test: #embed support for external JSON files (C++26)
|
* Test: #embed support for external JSON files (C++26)
|
||||||
*/
|
*/
|
||||||
bool test_embed_twitter_json() {
|
bool test_embed_example_config_json() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
|
|
||||||
// C++26 #embed allows embedding files directly into the binary at compile time
|
// C++26 #embed allows embedding files directly into the binary at compile time
|
||||||
// This creates a const char array with the file contents plus null terminator
|
// This creates a const char array with the file contents plus null terminator
|
||||||
constexpr const char twitter_json[] = {
|
constexpr const unsigned char example_config_json[] = {
|
||||||
#embed TWITTER_JSON
|
#embed "../../jsonexamples/example_config.json"
|
||||||
, 0
|
, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse the embedded JSON at compile time
|
// Parse the embedded JSON at compile time
|
||||||
constexpr auto parsed_twitter = simdjson::compile_time::parse_json<twitter_json>();
|
constexpr auto parsed_example_config = simdjson::compile_time::parse_json<example_config_json>();
|
||||||
|
|
||||||
// Verify the structure - twitter.json should have a "statuses" array
|
// Verify the structure - example_config.json should have an "app_name" field
|
||||||
static_assert(parsed_twitter.statuses.size() > 0);
|
static_assert(std::string_view(parsed_example_config.app_name) == "MyApp");
|
||||||
|
|
||||||
// Runtime verification
|
// Runtime verification
|
||||||
ASSERT_TRUE(parsed_twitter.statuses.size() > 0);
|
ASSERT_TRUE(std::string_view(parsed_example_config.app_name) == "MyApp");
|
||||||
|
|
||||||
std::cout << "Successfully parsed embedded twitter.json with "
|
std::cout << "Successfully parsed embedded example_config.json with "
|
||||||
<< parsed_twitter.statuses.size() << " statuses" << std::endl;
|
<< parsed_example_config.app_name << " as app_name" << std::endl;
|
||||||
|
|
||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -718,11 +719,10 @@ bool run() {
|
|||||||
test_top_level_array_example() &&
|
test_top_level_array_example() &&
|
||||||
test_array_of_objects_with_concept()
|
test_array_of_objects_with_concept()
|
||||||
#ifdef TEST_EMBED_SUPPORTED
|
#ifdef TEST_EMBED_SUPPORTED
|
||||||
&& test_embed_twitter_json()
|
&& test_embed_example_config_json()
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace compile_time_json_tests
|
} // namespace compile_time_json_tests
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
#ifndef SIMDJSON_TESTS_DOCUMENT_STREAM_FUZZ_TEST_COMMON_H
|
||||||
|
#define SIMDJSON_TESTS_DOCUMENT_STREAM_FUZZ_TEST_COMMON_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
|
namespace document_stream_fuzz {
|
||||||
|
|
||||||
|
constexpr uint64_t fixed_seed = 0x5eed1234ULL;
|
||||||
|
constexpr size_t batch_size = 512;
|
||||||
|
constexpr size_t minimum_total_bytes = 4096;
|
||||||
|
constexpr size_t minimum_document_count = 128;
|
||||||
|
|
||||||
|
struct stream_case {
|
||||||
|
const char *name;
|
||||||
|
simdjson::stream_format format;
|
||||||
|
std::string input;
|
||||||
|
std::vector<std::string> expected_documents;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline char random_char(std::mt19937_64 &rng) {
|
||||||
|
static constexpr char alphabet[] =
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"0123456789 _-/.";
|
||||||
|
std::uniform_int_distribution<size_t> dist(0, sizeof(alphabet) - 2);
|
||||||
|
return alphabet[dist(rng)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_ascii_text(std::mt19937_64 &rng, size_t min_length, size_t max_length) {
|
||||||
|
std::uniform_int_distribution<size_t> length_dist(min_length, max_length);
|
||||||
|
const size_t length = length_dist(rng);
|
||||||
|
std::string text;
|
||||||
|
text.reserve(length);
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
text.push_back(random_char(rng));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string quote_json_string(const std::string &text) {
|
||||||
|
std::string quoted;
|
||||||
|
quoted.reserve(text.size() + 2);
|
||||||
|
quoted.push_back('"');
|
||||||
|
for (char ch : text) {
|
||||||
|
if (ch == '\\' || ch == '"') {
|
||||||
|
quoted.push_back('\\');
|
||||||
|
}
|
||||||
|
quoted.push_back(ch);
|
||||||
|
}
|
||||||
|
quoted.push_back('"');
|
||||||
|
return quoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_integer_literal(std::mt19937_64 &rng) {
|
||||||
|
std::uniform_int_distribution<int64_t> dist(-1000000, 1000000);
|
||||||
|
return std::to_string(dist(rng));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_float_literal(std::mt19937_64 &rng) {
|
||||||
|
std::uniform_int_distribution<int64_t> whole_dist(-250000, 250000);
|
||||||
|
std::uniform_int_distribution<int64_t> fraction_dist(1, 9999);
|
||||||
|
return std::to_string(whole_dist(rng)) + "." + std::to_string(fraction_dist(rng));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_scalar(std::mt19937_64 &rng) {
|
||||||
|
std::uniform_int_distribution<int> type_dist(0, 4);
|
||||||
|
switch (type_dist(rng)) {
|
||||||
|
case 0: return quote_json_string(make_ascii_text(rng, 0, 20));
|
||||||
|
case 1: return make_integer_literal(rng);
|
||||||
|
case 2: return make_float_literal(rng);
|
||||||
|
case 3: return (rng() & 1) ? "true" : "false";
|
||||||
|
default: return "null";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_value(std::mt19937_64 &rng, int depth);
|
||||||
|
|
||||||
|
inline std::string make_array(std::mt19937_64 &rng, int depth) {
|
||||||
|
std::uniform_int_distribution<int> count_dist(0, depth == 0 ? 5 : 3);
|
||||||
|
const int count = count_dist(rng);
|
||||||
|
std::string out = "[";
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
out.push_back(',');
|
||||||
|
}
|
||||||
|
out += make_value(rng, depth + 1);
|
||||||
|
}
|
||||||
|
out.push_back(']');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_object(std::mt19937_64 &rng, int depth) {
|
||||||
|
std::uniform_int_distribution<int> count_dist(0, depth == 0 ? 5 : 3);
|
||||||
|
const int count = count_dist(rng);
|
||||||
|
std::string out = "{";
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
out.push_back(',');
|
||||||
|
}
|
||||||
|
std::string key = "k";
|
||||||
|
key += std::to_string(depth);
|
||||||
|
key += '_';
|
||||||
|
key += std::to_string(i);
|
||||||
|
key += '_';
|
||||||
|
key += make_ascii_text(rng, 1, 6);
|
||||||
|
out += quote_json_string(key);
|
||||||
|
out.push_back(':');
|
||||||
|
out += make_value(rng, depth + 1);
|
||||||
|
}
|
||||||
|
out.push_back('}');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_value(std::mt19937_64 &rng, int depth) {
|
||||||
|
if (depth >= 3) {
|
||||||
|
return make_scalar(rng);
|
||||||
|
}
|
||||||
|
std::uniform_int_distribution<int> type_dist(0, 6);
|
||||||
|
switch (type_dist(rng)) {
|
||||||
|
case 0: return quote_json_string(make_ascii_text(rng, 0, 20));
|
||||||
|
case 1: return make_integer_literal(rng);
|
||||||
|
case 2: return make_float_literal(rng);
|
||||||
|
case 3: return (rng() & 1) ? "true" : "false";
|
||||||
|
case 4: return "null";
|
||||||
|
case 5: return make_array(rng, depth);
|
||||||
|
default: return make_object(rng, depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<std::string> make_documents() {
|
||||||
|
std::vector<std::string> docs = {
|
||||||
|
"0",
|
||||||
|
"-17",
|
||||||
|
"3.125",
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"null",
|
||||||
|
"\"alpha beta\"",
|
||||||
|
"[]",
|
||||||
|
"[1,true,\"x\"]",
|
||||||
|
"{}",
|
||||||
|
"{\"a\":1,\"b\":[2,3],\"c\":{\"d\":false}}"
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t total_bytes = 0;
|
||||||
|
for (const auto &doc : docs) {
|
||||||
|
total_bytes += doc.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mt19937_64 rng(fixed_seed);
|
||||||
|
while (docs.size() < minimum_document_count || total_bytes < minimum_total_bytes) {
|
||||||
|
docs.push_back(make_value(rng, 0));
|
||||||
|
total_bytes += docs.back().size();
|
||||||
|
}
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<std::string> make_wrapped_documents(const std::vector<std::string> &docs) {
|
||||||
|
std::vector<std::string> wrapped;
|
||||||
|
wrapped.reserve(docs.size());
|
||||||
|
for (size_t i = 0; i < docs.size(); i++) {
|
||||||
|
wrapped.push_back("{\"id\":" + std::to_string(i) + ",\"value\":" + docs[i] + "}");
|
||||||
|
}
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string build_whitespace_input(const std::vector<std::string> &docs) {
|
||||||
|
static const char *separators[] = {" ", "\n", "\r\n", "\t", " \n\t", "\r\t "};
|
||||||
|
std::string out = " \n\t";
|
||||||
|
for (size_t i = 0; i < docs.size(); i++) {
|
||||||
|
out += docs[i];
|
||||||
|
if (i + 1 < docs.size()) {
|
||||||
|
out += separators[i % (sizeof(separators) / sizeof(separators[0]))];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += "\n\t ";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string build_json_sequence_input(const std::vector<std::string> &docs) {
|
||||||
|
std::string out;
|
||||||
|
for (size_t i = 0; i < docs.size(); i++) {
|
||||||
|
out.push_back('\x1e');
|
||||||
|
out += docs[i];
|
||||||
|
out.push_back('\n');
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string build_comma_delimited_input(const std::vector<std::string> &docs) {
|
||||||
|
std::string out;
|
||||||
|
for (size_t i = 0; i < docs.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
out += (i % 3 == 0) ? ",\n" : ", ";
|
||||||
|
}
|
||||||
|
out += docs[i];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string build_comma_delimited_array_input(const std::vector<std::string> &docs) {
|
||||||
|
std::string out = " \t\n[";
|
||||||
|
for (size_t i = 0; i < docs.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
out += (i % 4 == 0) ? ",\n" : ", ";
|
||||||
|
}
|
||||||
|
out += docs[i];
|
||||||
|
}
|
||||||
|
out += "]\r\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<stream_case> make_stream_cases(const std::vector<std::string> &docs) {
|
||||||
|
std::vector<std::string> whitespace_docs = make_wrapped_documents(docs);
|
||||||
|
return {
|
||||||
|
{"whitespace_delimited", simdjson::stream_format::whitespace_delimited, build_whitespace_input(whitespace_docs), whitespace_docs},
|
||||||
|
{"json_sequence", simdjson::stream_format::json_sequence, build_json_sequence_input(whitespace_docs), whitespace_docs},
|
||||||
|
{"comma_delimited", simdjson::stream_format::comma_delimited, build_comma_delimited_input(whitespace_docs), whitespace_docs},
|
||||||
|
{"comma_delimited_array", simdjson::stream_format::comma_delimited_array, build_comma_delimited_array_input(whitespace_docs), whitespace_docs}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace document_stream_fuzz
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,6 +7,7 @@ if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
|
|||||||
endif()
|
endif()
|
||||||
add_cpp_test(basictests LABELS dom acceptance per_implementation)
|
add_cpp_test(basictests LABELS dom acceptance per_implementation)
|
||||||
add_cpp_test(document_stream_tests LABELS dom acceptance per_implementation)
|
add_cpp_test(document_stream_tests LABELS dom acceptance per_implementation)
|
||||||
|
add_cpp_test(document_stream_fuzz_tests LABELS dom acceptance per_implementation)
|
||||||
add_cpp_test(document_tests LABELS dom acceptance per_implementation)
|
add_cpp_test(document_tests LABELS dom acceptance per_implementation)
|
||||||
add_cpp_test(errortests LABELS dom acceptance per_implementation)
|
add_cpp_test(errortests LABELS dom acceptance per_implementation)
|
||||||
add_cpp_test(extracting_values_example LABELS dom acceptance per_implementation)
|
add_cpp_test(extracting_values_example LABELS dom acceptance per_implementation)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ namespace big_integer_tests {
|
|||||||
std::string output = simdjson::to_string(doc);
|
std::string output = simdjson::to_string(doc);
|
||||||
// Raw digits should appear
|
// Raw digits should appear
|
||||||
ASSERT_TRUE(output.find("123456789012345678901") != std::string::npos);
|
ASSERT_TRUE(output.find("123456789012345678901") != std::string::npos);
|
||||||
// Must not be quoted — it's a number, not a string
|
// Must not be quoted - it's a number, not a string
|
||||||
ASSERT_EQUAL(output.find("\"123456789012345678901\""), std::string::npos);
|
ASSERT_EQUAL(output.find("\"123456789012345678901\""), std::string::npos);
|
||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_macros.h"
|
||||||
|
#include "test_main.h"
|
||||||
|
#include "document_stream_fuzz_test_common.h"
|
||||||
|
|
||||||
|
namespace document_stream_fuzz_tests {
|
||||||
|
|
||||||
|
std::string strip_stream_artifacts(simdjson::stream_format format, std::string_view text) {
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = text.size();
|
||||||
|
while (start < end && (text[start] == ' ' || text[start] == '\t' || text[start] == '\n' || text[start] == '\r' || text[start] == '\x1e')) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
while (end > start && (text[end - 1] == ' ' || text[end - 1] == '\t' || text[end - 1] == '\n' || text[end - 1] == '\r' || text[end - 1] == '\x1e')) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
std::string cleaned(text.substr(start, end - start));
|
||||||
|
if (format == simdjson::stream_format::comma_delimited || format == simdjson::stream_format::comma_delimited_array) {
|
||||||
|
while (!cleaned.empty() && cleaned.back() == ',') {
|
||||||
|
cleaned.pop_back();
|
||||||
|
while (!cleaned.empty() && (cleaned.back() == ' ' || cleaned.back() == '\t' || cleaned.back() == '\n' || cleaned.back() == '\r')) {
|
||||||
|
cleaned.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string canonicalize_document(simdjson::stream_format format, std::string_view text) {
|
||||||
|
std::string cleaned = strip_stream_artifacts(format, text);
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::element doc;
|
||||||
|
if (parser.parse(cleaned).get(doc)) {
|
||||||
|
return std::string("PARSE_ERROR:") + cleaned;
|
||||||
|
}
|
||||||
|
return simdjson::minify(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &expected_documents() {
|
||||||
|
static const std::vector<std::string> docs = document_stream_fuzz::make_documents();
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<document_stream_fuzz::stream_case> &stream_cases() {
|
||||||
|
static const std::vector<document_stream_fuzz::stream_case> cases =
|
||||||
|
document_stream_fuzz::make_stream_cases(expected_documents());
|
||||||
|
return cases;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_case(const document_stream_fuzz::stream_case &test_case) {
|
||||||
|
TEST_START();
|
||||||
|
const auto &expected = test_case.expected_documents;
|
||||||
|
ASSERT_TRUE(test_case.input.size() > document_stream_fuzz::batch_size * 4);
|
||||||
|
|
||||||
|
simdjson::padded_string input(test_case.input);
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
|
||||||
|
for (int pass = 0; pass < 2; pass++) {
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
ASSERT_SUCCESS(parser.parse_many(input, document_stream_fuzz::batch_size, test_case.format).get(stream));
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
ASSERT_SUCCESS(doc.error());
|
||||||
|
ASSERT_TRUE(index < expected.size());
|
||||||
|
simdjson::dom::element el;
|
||||||
|
ASSERT_SUCCESS(doc.get(el));
|
||||||
|
ASSERT_EQUAL(
|
||||||
|
canonicalize_document(test_case.format, simdjson::minify(el)),
|
||||||
|
canonicalize_document(test_case.format, expected[index])
|
||||||
|
);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
ASSERT_EQUAL(index, expected.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
for (const auto &test_case : stream_cases()) {
|
||||||
|
std::cout << "Running fuzz corpus against stream format: " << test_case.name << std::endl;
|
||||||
|
if (!verify_case(test_case)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace document_stream_fuzz_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, document_stream_fuzz_tests::run);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -114,6 +114,20 @@ namespace parser_load {
|
|||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool parser_load_string_view_subpath() {
|
||||||
|
TEST_START();
|
||||||
|
const std::string valid_path(TWITTER_JSON);
|
||||||
|
const std::string backing = valid_path + ".extra";
|
||||||
|
const std::string_view view(backing.data(), valid_path.size());
|
||||||
|
uint64_t count_from_string = 0;
|
||||||
|
uint64_t count_from_view = 0;
|
||||||
|
dom::parser parser;
|
||||||
|
ASSERT_SUCCESS(parser.load(valid_path)["search_metadata"]["count"].get(count_from_string));
|
||||||
|
ASSERT_SUCCESS(parser.load(view)["search_metadata"]["count"].get(count_from_view));
|
||||||
|
ASSERT_EQUAL(count_from_view, count_from_string);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
bool parser_load_chain() {
|
bool parser_load_chain() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
@@ -136,6 +150,7 @@ namespace parser_load {
|
|||||||
&& parser_load_nonexistent()
|
&& parser_load_nonexistent()
|
||||||
&& parser_load_many_nonexistent()
|
&& parser_load_many_nonexistent()
|
||||||
&& padded_string_load_nonexistent()
|
&& padded_string_load_nonexistent()
|
||||||
|
&& parser_load_string_view_subpath()
|
||||||
&& parser_load_chain()
|
&& parser_load_chain()
|
||||||
&& parser_load_many_chain()
|
&& parser_load_many_chain()
|
||||||
&& parser_parse_many_documents_error_in_the_middle()
|
&& parser_parse_many_documents_error_in_the_middle()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
|
#include "simdjson/padded_string_view.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace simdjson;
|
using namespace simdjson;
|
||||||
@@ -531,6 +532,35 @@ void simplepad() {
|
|||||||
if(error) { exit(-1); }
|
if(error) { exit(-1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
void simpleinputpad_dom1() {
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
dom::parser parser;
|
||||||
|
dom::element doc;
|
||||||
|
auto error = parser.parse(input).get(doc);
|
||||||
|
if(error) { exit(-1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void simpleinputpad_dom2() {
|
||||||
|
const char *jsonpointer = R"(
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
size_t len = strlen(jsonpointer);
|
||||||
|
simdjson::padded_input input(jsonpointer, len);
|
||||||
|
dom::parser parser;
|
||||||
|
dom::element doc;
|
||||||
|
auto error = parser.parse(input).get(doc);
|
||||||
|
if(error) { exit(-1); }
|
||||||
|
std::string_view val;
|
||||||
|
error = doc["key"].get(val);
|
||||||
|
if(error) { exit(-1); }
|
||||||
|
if(val != "value") { exit(-1); }
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
void jsondollar() {
|
void jsondollar() {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
|
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
|
||||||
|
|||||||
@@ -475,11 +475,11 @@ bool unicode_test() {
|
|||||||
std::cout << "Running " << __func__ << std::endl;
|
std::cout << "Running " << __func__ << std::endl;
|
||||||
|
|
||||||
const char* json = R"({
|
const char* json = R"({
|
||||||
"greeting": "Hello, 世界!",
|
"greeting": "Hello, \u4e16\u754c!",
|
||||||
"emoji": "🎉🚀✨",
|
"emoji": "\ud83c\udf89\ud83d\ude80\u2728",
|
||||||
"arabic": "مرحبا",
|
"arabic": "\u0645\u0631\u062d\u0628\u0627",
|
||||||
"russian": "Привет",
|
"russian": "\u041f\u0440\u0438\u0432\u0435\u0442",
|
||||||
"mixed": "café résumé naïve"
|
"mixed": "caf\u00e9 r\u00e9sum\u00e9 na\u00efve"
|
||||||
})";
|
})";
|
||||||
|
|
||||||
simdjson::dom::parser parser;
|
simdjson::dom::parser parser;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ add_cpp_test(ondemand_array_tests LABELS ondemand acceptance
|
|||||||
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_document_stream_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_document_stream_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_document_stream_fuzz_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_error_location_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_error_location_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
|
||||||
@@ -40,6 +41,7 @@ add_cpp_test(ondemand_convert_tests LABELS ondemand acceptance
|
|||||||
add_cpp_test(ondemand_unknown_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_unknown_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_wildcard_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_wildcard_tests LABELS ondemand acceptance per_implementation)
|
||||||
if(NOT SIMDJSON_SANITIZE)
|
if(NOT SIMDJSON_SANITIZE)
|
||||||
|
add_cpp_test(ondemand_padded_input LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ long page_size() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
||||||
// page boundary.
|
// page boundary. Assumes len != 0.
|
||||||
bool need_allocation(const char *buf, size_t len) {
|
bool need_allocation(const char *buf, size_t len) {
|
||||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
||||||
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
return date_str;
|
return date_str;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::string date_str;
|
std::string date_str{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ auto tag_invoke(deserialize_tag, simdjson_value &val, MyDate& date) {
|
|||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
struct complicated_weather_data {
|
struct complicated_weather_data {
|
||||||
std::vector<MyDate> time;
|
std::vector<MyDate> time{};
|
||||||
std::vector<float> temperature;
|
std::vector<float> temperature{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -152,19 +152,19 @@ simdjson::padded_string json_cars =
|
|||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
struct Player {
|
struct Player {
|
||||||
std::string username;
|
std::string username{};
|
||||||
int level;
|
int level{};
|
||||||
double health;
|
double health{};
|
||||||
};
|
};
|
||||||
struct BadPlayer {
|
struct BadPlayer {
|
||||||
int username; // Oops, should be string!
|
int username{}; // Oops, should be string!
|
||||||
int level;
|
int level{};
|
||||||
double health;
|
double health{};
|
||||||
};
|
};
|
||||||
struct OptionalPlayer {
|
struct OptionalPlayer {
|
||||||
std::string username;
|
std::string username{};
|
||||||
std::optional<int> level;
|
std::optional<int> level{};
|
||||||
double health;
|
double health{};
|
||||||
};
|
};
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
bool missing_key_player() {
|
bool missing_key_player() {
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
#include "document_stream_fuzz_test_common.h"
|
||||||
|
|
||||||
|
namespace document_stream_fuzz_tests {
|
||||||
|
|
||||||
|
std::string strip_stream_artifacts(simdjson::stream_format format, std::string_view text) {
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = text.size();
|
||||||
|
while (start < end && (text[start] == ' ' || text[start] == '\t' || text[start] == '\n' || text[start] == '\r' || text[start] == '\x1e')) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
while (end > start && (text[end - 1] == ' ' || text[end - 1] == '\t' || text[end - 1] == '\n' || text[end - 1] == '\r' || text[end - 1] == '\x1e')) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
std::string cleaned(text.substr(start, end - start));
|
||||||
|
if (format == simdjson::stream_format::comma_delimited || format == simdjson::stream_format::comma_delimited_array) {
|
||||||
|
while (!cleaned.empty() && cleaned.back() == ',') {
|
||||||
|
cleaned.pop_back();
|
||||||
|
while (!cleaned.empty() && (cleaned.back() == ' ' || cleaned.back() == '\t' || cleaned.back() == '\n' || cleaned.back() == '\r')) {
|
||||||
|
cleaned.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string canonicalize_document(simdjson::stream_format format, std::string_view text) {
|
||||||
|
std::string cleaned = strip_stream_artifacts(format, text);
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::element doc;
|
||||||
|
if (parser.parse(cleaned).get(doc)) {
|
||||||
|
return std::string("PARSE_ERROR:") + cleaned;
|
||||||
|
}
|
||||||
|
return simdjson::minify(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &expected_documents() {
|
||||||
|
static const std::vector<std::string> docs = document_stream_fuzz::make_documents();
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<document_stream_fuzz::stream_case> &stream_cases() {
|
||||||
|
static const std::vector<document_stream_fuzz::stream_case> cases =
|
||||||
|
document_stream_fuzz::make_stream_cases(expected_documents());
|
||||||
|
return cases;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_case(const document_stream_fuzz::stream_case &test_case) {
|
||||||
|
TEST_START();
|
||||||
|
const auto &expected = test_case.expected_documents;
|
||||||
|
ASSERT_TRUE(test_case.input.size() > document_stream_fuzz::batch_size * 4);
|
||||||
|
|
||||||
|
simdjson::padded_string input(test_case.input);
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
|
||||||
|
for (int pass = 0; pass < 2; pass++) {
|
||||||
|
simdjson::ondemand::document_stream stream;
|
||||||
|
ASSERT_SUCCESS(parser.iterate_many(input, document_stream_fuzz::batch_size, test_case.format).get(stream));
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (auto it = stream.begin(); it != stream.end(); ++it) {
|
||||||
|
auto doc_result = *it;
|
||||||
|
ASSERT_SUCCESS(doc_result.error());
|
||||||
|
ASSERT_TRUE(index < expected.size());
|
||||||
|
|
||||||
|
simdjson::ondemand::document_reference doc;
|
||||||
|
ASSERT_SUCCESS(doc_result.get(doc));
|
||||||
|
|
||||||
|
std::string_view actual;
|
||||||
|
ASSERT_SUCCESS(simdjson::to_json_string(doc).get(actual));
|
||||||
|
ASSERT_EQUAL(
|
||||||
|
canonicalize_document(test_case.format, actual),
|
||||||
|
canonicalize_document(test_case.format, expected[index])
|
||||||
|
);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
ASSERT_EQUAL(index, expected.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
for (const auto &test_case : stream_cases()) {
|
||||||
|
std::cout << "Running fuzz corpus against stream format: " << test_case.name << std::endl;
|
||||||
|
if (!verify_case(test_case)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace document_stream_fuzz_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, document_stream_fuzz_tests::run);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -171,6 +171,17 @@ namespace error_tests {
|
|||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool parser_set_max_capacity() {
|
||||||
|
TEST_START();
|
||||||
|
ondemand::parser parser;
|
||||||
|
parser.set_max_capacity(1024 * 1024);
|
||||||
|
ASSERT_EQUAL(parser.max_capacity(), size_t(1024 * 1024));
|
||||||
|
auto json = R"({"k":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"})"_padded;
|
||||||
|
ondemand::document doc;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
bool simple_error_example() {
|
bool simple_error_example() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
@@ -212,7 +223,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -235,7 +250,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -257,7 +276,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -279,7 +302,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -306,7 +333,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -329,7 +360,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -350,7 +385,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -371,7 +410,11 @@ namespace error_tests {
|
|||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
|
||||||
|
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
|
||||||
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
|
||||||
@@ -432,6 +475,7 @@ namespace error_tests {
|
|||||||
raw_json_string_error() &&
|
raw_json_string_error() &&
|
||||||
empty_document_error() &&
|
empty_document_error() &&
|
||||||
parser_max_capacity() &&
|
parser_max_capacity() &&
|
||||||
|
parser_set_max_capacity() &&
|
||||||
get_fail_then_succeed_bool() &&
|
get_fail_then_succeed_bool() &&
|
||||||
get_fail_then_succeed_null() &&
|
get_fail_then_succeed_null() &&
|
||||||
simple_error_example() &&
|
simple_error_example() &&
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ bool normal() {
|
|||||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
|
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), stream_format::comma_delimited).get(doc_stream));
|
||||||
|
|
||||||
for (auto doc : doc_stream)
|
for (auto doc : doc_stream)
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ bool small_batch_size() {
|
|||||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
ASSERT_SUCCESS(parser.iterate_many(json, 32, true).get(doc_stream));
|
ASSERT_SUCCESS(parser.iterate_many(json, 32, stream_format::comma_delimited).get(doc_stream));
|
||||||
|
|
||||||
for (auto doc : doc_stream)
|
for (auto doc : doc_stream)
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,7 @@ bool trailing_comma() {
|
|||||||
auto json = R"(1,)"_padded;
|
auto json = R"(1,)"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
|
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), stream_format::comma_delimited).get(doc_stream));
|
||||||
|
|
||||||
for (auto doc : doc_stream)
|
for (auto doc : doc_stream)
|
||||||
{
|
{
|
||||||
@@ -59,7 +59,7 @@ bool check_parsed_values() {
|
|||||||
auto json = R"( 1 , "a" , [100, 1] , {"hello" : "world"} , )"_padded;
|
auto json = R"( 1 , "a" , [100, 1] , {"hello" : "world"} , )"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
|
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), stream_format::comma_delimited).get(doc_stream));
|
||||||
|
|
||||||
auto begin = doc_stream.begin();
|
auto begin = doc_stream.begin();
|
||||||
auto end = doc_stream.end();
|
auto end = doc_stream.end();
|
||||||
@@ -127,7 +127,7 @@ bool leading_comma() {
|
|||||||
auto json = R"(,1)"_padded;
|
auto json = R"(,1)"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), true).get(doc_stream));
|
ASSERT_SUCCESS(parser.iterate_many(json, json.size(), stream_format::comma_delimited).get(doc_stream));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto begin = doc_stream.begin();
|
auto begin = doc_stream.begin();
|
||||||
|
|||||||
@@ -545,8 +545,111 @@ namespace number_tests {
|
|||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get_int32_values() {
|
||||||
|
TEST_START();
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
int32_t val;
|
||||||
|
padded_string docdata;
|
||||||
|
|
||||||
|
// Valid int32 values
|
||||||
|
docdata = "0"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 0);
|
||||||
|
|
||||||
|
docdata = "42"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 42);
|
||||||
|
|
||||||
|
docdata = "-1"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, -1);
|
||||||
|
|
||||||
|
docdata = "2147483647"_padded; // INT32_MAX
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 2147483647);
|
||||||
|
|
||||||
|
docdata = "-2147483648"_padded; // INT32_MIN
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, -2147483648);
|
||||||
|
|
||||||
|
// Out of range
|
||||||
|
docdata = "2147483648"_padded; // INT32_MAX + 1
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
docdata = "-2147483649"_padded; // INT32_MIN - 1
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
docdata = "9999999999"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
// Test via value path
|
||||||
|
auto json = R"({"x": 42})"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc["x"].get_int32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 42);
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_uint32_values() {
|
||||||
|
TEST_START();
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
uint32_t val;
|
||||||
|
padded_string docdata;
|
||||||
|
|
||||||
|
// Valid uint32 values
|
||||||
|
docdata = "0"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_uint32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 0u);
|
||||||
|
|
||||||
|
docdata = "42"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_uint32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 42u);
|
||||||
|
|
||||||
|
docdata = "4294967295"_padded; // UINT32_MAX
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc.get_uint32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 4294967295u);
|
||||||
|
|
||||||
|
// Out of range
|
||||||
|
docdata = "4294967296"_padded; // UINT32_MAX + 1
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_uint32(), NUMBER_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
docdata = "9999999999"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_uint32(), NUMBER_OUT_OF_RANGE);
|
||||||
|
|
||||||
|
// Negative values should fail (get_uint64 will fail with INCORRECT_TYPE)
|
||||||
|
docdata = "-1"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
|
||||||
|
ASSERT_ERROR(doc.get_uint32(), INCORRECT_TYPE);
|
||||||
|
|
||||||
|
// Test via value path
|
||||||
|
auto json = R"({"x": 42})"_padded;
|
||||||
|
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||||
|
ASSERT_SUCCESS(doc["x"].get_uint32().get(val));
|
||||||
|
ASSERT_EQUAL(val, 42u);
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
return minus_zero() &&
|
return get_int32_values() &&
|
||||||
|
get_uint32_values() &&
|
||||||
|
minus_zero() &&
|
||||||
gigantic_big_int() &&
|
gigantic_big_int() &&
|
||||||
big_int_not_zero() &&
|
big_int_not_zero() &&
|
||||||
negative_big_int() &&
|
negative_big_int() &&
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "simdjson/padded_string_view.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
|
bool test_padded_input_from_string_view() {
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
auto error = parser.iterate(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_from_string_view (iterate error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t count = 0;
|
||||||
|
for (auto val : doc) {
|
||||||
|
int64_t v;
|
||||||
|
error = val.get_int64().get(v);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_from_string_view (get_int64 error)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count != 3) {
|
||||||
|
printf("FAILED: test_padded_input_from_string_view (expected 3 elements, got %zu)\n", count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_from_string_view\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_from_cstring() {
|
||||||
|
const char *json = R"({"key": "value"})";
|
||||||
|
size_t len = std::strlen(json);
|
||||||
|
simdjson::padded_input input(json, len);
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
auto error = parser.iterate(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_from_cstring (iterate error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string_view val;
|
||||||
|
error = doc["key"].get_string().get(val);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_from_cstring (get_string error)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (val != "value") {
|
||||||
|
printf("FAILED: test_padded_input_from_cstring (expected 'value', got '%.*s')\n",
|
||||||
|
(int)val.size(), val.data());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_from_cstring\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_is_view() {
|
||||||
|
// A short string at a "safe" position should typically be a view (no copy).
|
||||||
|
// We can't guarantee this in all environments, but we can at least test
|
||||||
|
// that is_view() returns a valid boolean and parsing works either way.
|
||||||
|
std::string_view json = "[1]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
bool view = input.is_view();
|
||||||
|
printf(" is_view() = %s\n", view ? "true" : "false");
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
auto error = parser.iterate(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_is_view (iterate error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_is_view\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_nested_json() {
|
||||||
|
std::string_view json = R"({"a":{"b":[1,2,{"c":true}]}})";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
auto error = parser.iterate(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_nested_json (iterate error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool c_val;
|
||||||
|
error = doc["a"]["b"].at(2)["c"].get_bool().get(c_val);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_nested_json (get_bool error)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!c_val) {
|
||||||
|
printf("FAILED: test_padded_input_nested_json (expected true)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_nested_json\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_dom() {
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::element doc;
|
||||||
|
auto error = parser.parse(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_dom (parse error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_dom\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_dom_object() {
|
||||||
|
const char *json = R"({"name":"simdjson","version":42})";
|
||||||
|
size_t len = std::strlen(json);
|
||||||
|
simdjson::padded_input input(json, len);
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::element doc;
|
||||||
|
auto error = parser.parse(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_dom_object (parse error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string_view name;
|
||||||
|
error = doc["name"].get(name);
|
||||||
|
if (error || name != "simdjson") {
|
||||||
|
printf("FAILED: test_padded_input_dom_object (name mismatch)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_dom_object\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_padded_input_from_std_string() {
|
||||||
|
std::string json = R"({"key": "value"})";
|
||||||
|
// Reserve extra space to test capacity handling
|
||||||
|
json.reserve(json.size() + 100);
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
auto error = parser.iterate(input).get(doc);
|
||||||
|
if (error) {
|
||||||
|
printf("FAILED: test_padded_input_from_std_string (iterate error: %s)\n",
|
||||||
|
simdjson::error_message(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string_view key;
|
||||||
|
error = doc["key"].get(key);
|
||||||
|
if (error || key != "value") {
|
||||||
|
printf("FAILED: test_padded_input_from_std_string (key/value mismatch)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("OK: test_padded_input_from_std_string\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
bool ok = true;
|
||||||
|
ok = test_padded_input_from_string_view() && ok;
|
||||||
|
ok = test_padded_input_from_cstring() && ok;
|
||||||
|
ok = test_padded_input_from_std_string() && ok;
|
||||||
|
ok = test_padded_input_is_view() && ok;
|
||||||
|
ok = test_padded_input_nested_json() && ok;
|
||||||
|
ok = test_padded_input_dom() && ok;
|
||||||
|
ok = test_padded_input_dom_object() && ok;
|
||||||
|
if (ok) {
|
||||||
|
printf("\nAll padded_input tests passed.\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
} else {
|
||||||
|
printf("\nSome padded_input tests FAILED.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("padded_input requires C++17 or better. Skipping.\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SIMDJSON_CPLUSPLUS17
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
|
#include "simdjson/padded_string_view.h"
|
||||||
#include "test_ondemand.h"
|
#include "test_ondemand.h"
|
||||||
#if __cpp_lib_optional >= 201606L
|
#if __cpp_lib_optional >= 201606L
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -49,6 +50,33 @@ bool simplepad() {
|
|||||||
return error == SUCCESS;
|
return error == SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if SIMDJSON_CPLUSPLUS17
|
||||||
|
bool simpleinputpad1() {
|
||||||
|
std::string json = "[1]";
|
||||||
|
simdjson::padded_input padded_json(json);
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
auto error = parser.iterate(padded_json).get(doc);
|
||||||
|
return error == SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool simpleinputpad2() {
|
||||||
|
const char *jsonpointer = R"(
|
||||||
|
{
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
size_t len = strlen(jsonpointer);
|
||||||
|
|
||||||
|
simdjson::padded_input padded_json(jsonpointer, len);
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
auto error = parser.iterate(padded_json).get(doc);
|
||||||
|
return error == SUCCESS;
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_CPLUSPLUS17
|
||||||
|
|
||||||
bool string1() {
|
bool string1() {
|
||||||
const char * data = "my data"; // 7 bytes
|
const char * data = "my data"; // 7 bytes
|
||||||
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
||||||
@@ -1678,10 +1706,7 @@ bool allow_comma_separated_example() {
|
|||||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream doc_stream;
|
||||||
// We pass '32' as the batch size, but it is a bogus parameter because, since
|
auto error = parser.iterate_many(json, 32, simdjson::stream_format::comma_delimited).get(doc_stream);
|
||||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
|
||||||
// the document size.
|
|
||||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
|
||||||
if(error) { std::cerr << error << std::endl; return false; }
|
if(error) { std::cerr << error << std::endl; return false; }
|
||||||
for (auto doc : doc_stream) {
|
for (auto doc : doc_stream) {
|
||||||
std::cout << doc.type() << std::endl;
|
std::cout << doc.type() << std::endl;
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ namespace wrong_type_error_tests {
|
|||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
{ \
|
{ \
|
||||||
padded_string a_json(std::string(R"({ "a": )") + JSON + " }"); \
|
std::string a_json(R"({ "a": )"); a_json += JSON; a_json += " }"; \
|
||||||
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
|
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
|
||||||
if (!test_ondemand_doc(a_json, [&](auto doc_result) { \
|
if (!test_ondemand_doc(simdjson::pad(a_json), [&](auto doc_result) { \
|
||||||
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \
|
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \
|
||||||
return true; \
|
return true; \
|
||||||
})) { \
|
})) { \
|
||||||
|
|||||||
Reference in New Issue
Block a user