From 1d055261b4144dbf86b2658437015b15d4dd9bff Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 4 Sep 2022 00:32:56 +0100 Subject: initial --- .gitignore | 2 + CMakeLists.txt | 203 + LICENCE | 513 ++ Plugin.cmake | 10 + README.md | 381 ++ cmake/CompilerOptimizations.cmake | 53 + cmake/Modules/FindCsound.cmake | 62 + examples/example1.csd | 133 + examples/example2.csd | 120 + examples/example3.csd | 54 + examples/example4.csd | 57 + examples/example5.csd | 112 + examples/supplement.json | 10 + include/handling.h | 53 + include/jsoncons/LICENSE | 28 + include/jsoncons/allocator_holder.hpp | 38 + include/jsoncons/basic_json.hpp | 5839 ++++++++++++++++++++ include/jsoncons/bigint.hpp | 1611 ++++++ include/jsoncons/byte_string.hpp | 820 +++ include/jsoncons/config/binary_config.hpp | 226 + include/jsoncons/config/compiler_support.hpp | 389 ++ include/jsoncons/config/jsoncons_config.hpp | 308 ++ include/jsoncons/config/version.hpp | 40 + include/jsoncons/conv_error.hpp | 218 + include/jsoncons/converter.hpp | 296 + include/jsoncons/decode_json.hpp | 209 + include/jsoncons/decode_traits.hpp | 651 +++ include/jsoncons/detail/endian.hpp | 44 + include/jsoncons/detail/grisu3.hpp | 312 ++ include/jsoncons/detail/optional.hpp | 483 ++ include/jsoncons/detail/parse_number.hpp | 1133 ++++ include/jsoncons/detail/span.hpp | 188 + include/jsoncons/detail/string_view.hpp | 537 ++ include/jsoncons/detail/string_wrapper.hpp | 370 ++ include/jsoncons/detail/write_number.hpp | 567 ++ include/jsoncons/encode_json.hpp | 315 ++ include/jsoncons/encode_traits.hpp | 378 ++ include/jsoncons/json.hpp | 18 + include/jsoncons/json_array.hpp | 324 ++ include/jsoncons/json_content_handler.hpp | 12 + include/jsoncons/json_cursor.hpp | 448 ++ include/jsoncons/json_decoder.hpp | 420 ++ include/jsoncons/json_encoder.hpp | 1587 ++++++ include/jsoncons/json_error.hpp | 156 + include/jsoncons/json_exception.hpp | 241 + include/jsoncons/json_filter.hpp | 653 +++ include/jsoncons/json_fwd.hpp | 23 + include/jsoncons/json_object.hpp | 1772 ++++++ include/jsoncons/json_options.hpp | 862 +++ include/jsoncons/json_parser.hpp | 2871 ++++++++++ include/jsoncons/json_reader.hpp | 731 +++ include/jsoncons/json_traits_macros.hpp | 1072 ++++ include/jsoncons/json_traits_macros_deprecated.hpp | 144 + include/jsoncons/json_type.hpp | 206 + include/jsoncons/json_type_traits.hpp | 1829 ++++++ include/jsoncons/json_visitor.hpp | 1560 ++++++ include/jsoncons/json_visitor2.hpp | 2079 +++++++ include/jsoncons/more_type_traits.hpp | 874 +++ include/jsoncons/pretty_print.hpp | 89 + include/jsoncons/ser_context.hpp | 57 + include/jsoncons/sink.hpp | 289 + include/jsoncons/source.hpp | 777 +++ include/jsoncons/source_adaptor.hpp | 148 + include/jsoncons/staj2_cursor.hpp | 1178 ++++ include/jsoncons/staj_cursor.hpp | 1233 +++++ include/jsoncons/staj_iterator.hpp | 449 ++ include/jsoncons/tag_type.hpp | 245 + include/jsoncons/text_source_adaptor.hpp | 144 + include/jsoncons/typed_array_view.hpp | 250 + include/jsoncons/unicode_traits.hpp | 1330 +++++ include/jsoncons/uri.hpp | 635 +++ include/jsoncons_ext/LICENSE | 28 + include/jsoncons_ext/bson/bson.hpp | 23 + include/jsoncons_ext/bson/bson_cursor.hpp | 320 ++ include/jsoncons_ext/bson/bson_decimal128.hpp | 865 +++ include/jsoncons_ext/bson/bson_decimal128.hpp.bak | 816 +++ include/jsoncons_ext/bson/bson_encoder.hpp | 585 ++ include/jsoncons_ext/bson/bson_error.hpp | 103 + include/jsoncons_ext/bson/bson_oid.hpp | 245 + include/jsoncons_ext/bson/bson_options.hpp | 75 + include/jsoncons_ext/bson/bson_parser.hpp | 645 +++ include/jsoncons_ext/bson/bson_reader.hpp | 92 + include/jsoncons_ext/bson/bson_type.hpp | 44 + include/jsoncons_ext/bson/decode_bson.hpp | 201 + include/jsoncons_ext/bson/encode_bson.hpp | 144 + include/jsoncons_ext/cbor/cbor.hpp | 26 + include/jsoncons_ext/cbor/cbor_cursor.hpp | 351 ++ include/jsoncons_ext/cbor/cbor_cursor2.hpp | 265 + include/jsoncons_ext/cbor/cbor_detail.hpp | 93 + include/jsoncons_ext/cbor/cbor_encoder.hpp | 1766 ++++++ include/jsoncons_ext/cbor/cbor_error.hpp | 105 + include/jsoncons_ext/cbor/cbor_options.hpp | 113 + include/jsoncons_ext/cbor/cbor_parser.hpp | 1942 +++++++ include/jsoncons_ext/cbor/cbor_reader.hpp | 116 + include/jsoncons_ext/cbor/decode_cbor.hpp | 203 + include/jsoncons_ext/cbor/encode_cbor.hpp | 151 + include/jsoncons_ext/csv/csv.hpp | 17 + include/jsoncons_ext/csv/csv_cursor.hpp | 358 ++ include/jsoncons_ext/csv/csv_encoder.hpp | 954 ++++ include/jsoncons_ext/csv/csv_error.hpp | 85 + include/jsoncons_ext/csv/csv_options.hpp | 973 ++++ include/jsoncons_ext/csv/csv_parser.hpp | 2097 +++++++ include/jsoncons_ext/csv/csv_reader.hpp | 348 ++ include/jsoncons_ext/csv/csv_serializer.hpp | 12 + include/jsoncons_ext/csv/decode_csv.hpp | 208 + include/jsoncons_ext/csv/encode_csv.hpp | 122 + include/jsoncons_ext/jmespath/jmespath.hpp | 5215 +++++++++++++++++ include/jsoncons_ext/jmespath/jmespath_error.hpp | 215 + include/jsoncons_ext/jsonpatch/jsonpatch.hpp | 579 ++ include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp | 121 + include/jsoncons_ext/jsonpath/expression.hpp | 3329 +++++++++++ include/jsoncons_ext/jsonpath/flatten.hpp | 432 ++ include/jsoncons_ext/jsonpath/json_location.hpp | 445 ++ include/jsoncons_ext/jsonpath/json_query.hpp | 115 + include/jsoncons_ext/jsonpath/jsonpath.hpp | 13 + include/jsoncons_ext/jsonpath/jsonpath_error.hpp | 240 + .../jsoncons_ext/jsonpath/jsonpath_expression.hpp | 2612 +++++++++ .../jsoncons_ext/jsonpath/jsonpath_selector.hpp | 1322 +++++ include/jsoncons_ext/jsonpointer/jsonpointer.hpp | 1577 ++++++ .../jsoncons_ext/jsonpointer/jsonpointer_error.hpp | 119 + .../jsoncons_ext/jsonschema/format_validator.hpp | 968 ++++ include/jsoncons_ext/jsonschema/json_validator.hpp | 120 + include/jsoncons_ext/jsonschema/jsonschema.hpp | 13 + .../jsoncons_ext/jsonschema/jsonschema_error.hpp | 105 + .../jsoncons_ext/jsonschema/jsonschema_version.hpp | 18 + .../jsoncons_ext/jsonschema/keyword_validator.hpp | 1745 ++++++ .../jsonschema/keyword_validator_factory.hpp | 556 ++ include/jsoncons_ext/jsonschema/schema_draft7.hpp | 198 + .../jsoncons_ext/jsonschema/schema_location.hpp | 200 + include/jsoncons_ext/jsonschema/schema_version.hpp | 35 + include/jsoncons_ext/jsonschema/subschema.hpp | 144 + include/jsoncons_ext/mergepatch/mergepatch.hpp | 103 + include/jsoncons_ext/msgpack/decode_msgpack.hpp | 202 + include/jsoncons_ext/msgpack/encode_msgpack.hpp | 142 + include/jsoncons_ext/msgpack/msgpack.hpp | 24 + include/jsoncons_ext/msgpack/msgpack_cursor.hpp | 343 ++ include/jsoncons_ext/msgpack/msgpack_cursor2.hpp | 259 + include/jsoncons_ext/msgpack/msgpack_encoder.hpp | 753 +++ include/jsoncons_ext/msgpack/msgpack_error.hpp | 94 + include/jsoncons_ext/msgpack/msgpack_options.hpp | 74 + include/jsoncons_ext/msgpack/msgpack_parser.hpp | 748 +++ include/jsoncons_ext/msgpack/msgpack_reader.hpp | 116 + include/jsoncons_ext/msgpack/msgpack_type.hpp | 63 + include/jsoncons_ext/ubjson/decode_ubjson.hpp | 201 + include/jsoncons_ext/ubjson/encode_ubjson.hpp | 142 + include/jsoncons_ext/ubjson/ubjson.hpp | 23 + include/jsoncons_ext/ubjson/ubjson_cursor.hpp | 307 + include/jsoncons_ext/ubjson/ubjson_encoder.hpp | 502 ++ include/jsoncons_ext/ubjson/ubjson_error.hpp | 100 + include/jsoncons_ext/ubjson/ubjson_options.hpp | 87 + include/jsoncons_ext/ubjson/ubjson_parser.hpp | 880 +++ include/jsoncons_ext/ubjson/ubjson_reader.hpp | 92 + include/jsoncons_ext/ubjson/ubjson_type.hpp | 43 + src/opcodes.cpp | 976 ++++ 154 files changed, 82670 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENCE create mode 100644 Plugin.cmake create mode 100644 README.md create mode 100644 cmake/CompilerOptimizations.cmake create mode 100644 cmake/Modules/FindCsound.cmake create mode 100644 examples/example1.csd create mode 100644 examples/example2.csd create mode 100644 examples/example3.csd create mode 100644 examples/example4.csd create mode 100644 examples/example5.csd create mode 100644 examples/supplement.json create mode 100644 include/handling.h create mode 100644 include/jsoncons/LICENSE create mode 100644 include/jsoncons/allocator_holder.hpp create mode 100644 include/jsoncons/basic_json.hpp create mode 100644 include/jsoncons/bigint.hpp create mode 100644 include/jsoncons/byte_string.hpp create mode 100644 include/jsoncons/config/binary_config.hpp create mode 100644 include/jsoncons/config/compiler_support.hpp create mode 100644 include/jsoncons/config/jsoncons_config.hpp create mode 100644 include/jsoncons/config/version.hpp create mode 100644 include/jsoncons/conv_error.hpp create mode 100644 include/jsoncons/converter.hpp create mode 100644 include/jsoncons/decode_json.hpp create mode 100644 include/jsoncons/decode_traits.hpp create mode 100644 include/jsoncons/detail/endian.hpp create mode 100644 include/jsoncons/detail/grisu3.hpp create mode 100644 include/jsoncons/detail/optional.hpp create mode 100644 include/jsoncons/detail/parse_number.hpp create mode 100644 include/jsoncons/detail/span.hpp create mode 100644 include/jsoncons/detail/string_view.hpp create mode 100644 include/jsoncons/detail/string_wrapper.hpp create mode 100644 include/jsoncons/detail/write_number.hpp create mode 100644 include/jsoncons/encode_json.hpp create mode 100644 include/jsoncons/encode_traits.hpp create mode 100644 include/jsoncons/json.hpp create mode 100644 include/jsoncons/json_array.hpp create mode 100644 include/jsoncons/json_content_handler.hpp create mode 100644 include/jsoncons/json_cursor.hpp create mode 100644 include/jsoncons/json_decoder.hpp create mode 100644 include/jsoncons/json_encoder.hpp create mode 100644 include/jsoncons/json_error.hpp create mode 100644 include/jsoncons/json_exception.hpp create mode 100644 include/jsoncons/json_filter.hpp create mode 100644 include/jsoncons/json_fwd.hpp create mode 100644 include/jsoncons/json_object.hpp create mode 100644 include/jsoncons/json_options.hpp create mode 100644 include/jsoncons/json_parser.hpp create mode 100644 include/jsoncons/json_reader.hpp create mode 100644 include/jsoncons/json_traits_macros.hpp create mode 100644 include/jsoncons/json_traits_macros_deprecated.hpp create mode 100644 include/jsoncons/json_type.hpp create mode 100644 include/jsoncons/json_type_traits.hpp create mode 100644 include/jsoncons/json_visitor.hpp create mode 100644 include/jsoncons/json_visitor2.hpp create mode 100644 include/jsoncons/more_type_traits.hpp create mode 100644 include/jsoncons/pretty_print.hpp create mode 100644 include/jsoncons/ser_context.hpp create mode 100644 include/jsoncons/sink.hpp create mode 100644 include/jsoncons/source.hpp create mode 100644 include/jsoncons/source_adaptor.hpp create mode 100644 include/jsoncons/staj2_cursor.hpp create mode 100644 include/jsoncons/staj_cursor.hpp create mode 100644 include/jsoncons/staj_iterator.hpp create mode 100644 include/jsoncons/tag_type.hpp create mode 100644 include/jsoncons/text_source_adaptor.hpp create mode 100644 include/jsoncons/typed_array_view.hpp create mode 100644 include/jsoncons/unicode_traits.hpp create mode 100644 include/jsoncons/uri.hpp create mode 100644 include/jsoncons_ext/LICENSE create mode 100644 include/jsoncons_ext/bson/bson.hpp create mode 100644 include/jsoncons_ext/bson/bson_cursor.hpp create mode 100644 include/jsoncons_ext/bson/bson_decimal128.hpp create mode 100644 include/jsoncons_ext/bson/bson_decimal128.hpp.bak create mode 100644 include/jsoncons_ext/bson/bson_encoder.hpp create mode 100644 include/jsoncons_ext/bson/bson_error.hpp create mode 100644 include/jsoncons_ext/bson/bson_oid.hpp create mode 100644 include/jsoncons_ext/bson/bson_options.hpp create mode 100644 include/jsoncons_ext/bson/bson_parser.hpp create mode 100644 include/jsoncons_ext/bson/bson_reader.hpp create mode 100644 include/jsoncons_ext/bson/bson_type.hpp create mode 100644 include/jsoncons_ext/bson/decode_bson.hpp create mode 100644 include/jsoncons_ext/bson/encode_bson.hpp create mode 100644 include/jsoncons_ext/cbor/cbor.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_cursor.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_cursor2.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_detail.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_encoder.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_error.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_options.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_parser.hpp create mode 100644 include/jsoncons_ext/cbor/cbor_reader.hpp create mode 100644 include/jsoncons_ext/cbor/decode_cbor.hpp create mode 100644 include/jsoncons_ext/cbor/encode_cbor.hpp create mode 100644 include/jsoncons_ext/csv/csv.hpp create mode 100644 include/jsoncons_ext/csv/csv_cursor.hpp create mode 100644 include/jsoncons_ext/csv/csv_encoder.hpp create mode 100644 include/jsoncons_ext/csv/csv_error.hpp create mode 100644 include/jsoncons_ext/csv/csv_options.hpp create mode 100644 include/jsoncons_ext/csv/csv_parser.hpp create mode 100644 include/jsoncons_ext/csv/csv_reader.hpp create mode 100644 include/jsoncons_ext/csv/csv_serializer.hpp create mode 100644 include/jsoncons_ext/csv/decode_csv.hpp create mode 100644 include/jsoncons_ext/csv/encode_csv.hpp create mode 100644 include/jsoncons_ext/jmespath/jmespath.hpp create mode 100644 include/jsoncons_ext/jmespath/jmespath_error.hpp create mode 100644 include/jsoncons_ext/jsonpatch/jsonpatch.hpp create mode 100644 include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp create mode 100644 include/jsoncons_ext/jsonpath/expression.hpp create mode 100644 include/jsoncons_ext/jsonpath/flatten.hpp create mode 100644 include/jsoncons_ext/jsonpath/json_location.hpp create mode 100644 include/jsoncons_ext/jsonpath/json_query.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_error.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_expression.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_selector.hpp create mode 100644 include/jsoncons_ext/jsonpointer/jsonpointer.hpp create mode 100644 include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp create mode 100644 include/jsoncons_ext/jsonschema/format_validator.hpp create mode 100644 include/jsoncons_ext/jsonschema/json_validator.hpp create mode 100644 include/jsoncons_ext/jsonschema/jsonschema.hpp create mode 100644 include/jsoncons_ext/jsonschema/jsonschema_error.hpp create mode 100644 include/jsoncons_ext/jsonschema/jsonschema_version.hpp create mode 100644 include/jsoncons_ext/jsonschema/keyword_validator.hpp create mode 100644 include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp create mode 100644 include/jsoncons_ext/jsonschema/schema_draft7.hpp create mode 100644 include/jsoncons_ext/jsonschema/schema_location.hpp create mode 100644 include/jsoncons_ext/jsonschema/schema_version.hpp create mode 100644 include/jsoncons_ext/jsonschema/subschema.hpp create mode 100644 include/jsoncons_ext/mergepatch/mergepatch.hpp create mode 100644 include/jsoncons_ext/msgpack/decode_msgpack.hpp create mode 100644 include/jsoncons_ext/msgpack/encode_msgpack.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_cursor.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_cursor2.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_encoder.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_error.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_options.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_parser.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_reader.hpp create mode 100644 include/jsoncons_ext/msgpack/msgpack_type.hpp create mode 100644 include/jsoncons_ext/ubjson/decode_ubjson.hpp create mode 100644 include/jsoncons_ext/ubjson/encode_ubjson.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_cursor.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_encoder.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_error.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_options.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_parser.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_reader.hpp create mode 100644 include/jsoncons_ext/ubjson/ubjson_type.hpp create mode 100644 src/opcodes.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30d7024 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +examples/example_output.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..612d056 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,203 @@ +# from https://github.com/csound/plugins : common CMake operations +cmake_minimum_required(VERSION 2.8.12) +project(Csound-plugins) + +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + set(CMAKE_COMPILER_IS_CLANG 1) +endif() + +# C++11 needed +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +set(APIVERSION "6.0") + +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + +include(TestBigEndian) +include(CheckFunctionExists) +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + +### COMPILER OPTIMIZATION FLAGS +option(USE_COMPILER_OPTIMIZATIONS "Use the default Csound compiler optimization flags" ON) +if(USE_COMPILER_OPTIMIZATIONS) + include(${CMAKE_SOURCE_DIR}/cmake/CompilerOptimizations.cmake) +endif() + +if(APPLE) + set(OSX_VERSION " ") +endif() + +## USER OPTIONS ## +# Optional targets, they should all default to ON (check_deps will disable them if not possible to build) +option(USE_DOUBLE "Set to use double-precision floating point for audio samples." ON) +option(USE_LRINT "Use lrint/lrintf for converting floating point values to integers." ON) +option(BUILD_RELEASE "Build for release" ON) +option(USE_GIT_COMMIT "Show the git commit in version information" ON) + +# in Release configuration, set NDEBUG +if(${CMAKE_BUILD_TYPE} MATCHES "Release") +message("-----> Release mode") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG") +elseif(${CMAKE_BUILD_TYPE} MATCHES "Debug") +message("-----> Debug mode") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBETA") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBETA") +endif() + +# set -Werror if in Debug configuration +if(NOT MSVC AND NOT WASM) + set(CMAKE_CXX_FLAGS_RELEASE "-O3 ") + set(CMAKE_C_FLAGS_RELEASE "-O3 ") + if(${CMAKE_BUILD_TYPE} MATCHES "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-missing-field-initializers") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-missing-field-initializers") + endif() +endif() + + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(LINUX YES) +else() + set(LINUX NO) +endif() + +set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +check_c_compiler_flag(-fvisibility=hidden HAS_VISIBILITY_HIDDEN) +check_cxx_compiler_flag(-fvisibility=hidden HAS_CXX_VISIBILITY_HIDDEN) +if (HAS_VISIBILITY_HIDDEN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") +endif() +if (HAS_CXX_VISIBILITY_HIDDEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") +endif() + +check_c_compiler_flag(-std=gnu99 HAS_GNU99) +if (HAS_GNU99) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +endif() +if (HAS_CXX_VISIBILITY_HIDDEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") +endif() + +find_package(Csound) + +option(USE_LIB64 "Set to on to set installation directory for libraries to lib64" OFF) +if(USE_LIB64) + set(LIBRARY_INSTALL_DIR "lib64") + add_definitions("-DLIB64") +else() + set(LIBRARY_INSTALL_DIR "lib") +endif() +message(STATUS "LIBRARY INSTALL DIR: ${LIBRARY_INSTALL_DIR}") + +if(USE_DOUBLE) + message(STATUS "Building with 64-bit floats") + set(PLUGIN_INSTALL_DIR "${LIBRARY_INSTALL_DIR}/csound/plugins64-${APIVERSION}") + if(APPLE) + set(PLUGIN_INSTALL_DIR "${CS_FRAMEWORK_DEST}/${CSOUNDLIB}.framework/Versions/${APIVERSION}/Resources/Opcodes64") + endif() +else() + message(STATUS "Building with 32-bit floats") + set(PLUGIN_INSTALL_DIR "${LIBRARY_INSTALL_DIR}/csound/plugins-${APIVERSION}") + if(APPLE) + set(PLUGIN_INSTALL_DIR "${CS_FRAMEWORK_DEST}/${CSOUNDLIB}.framework/Versions/${APIVERSION}/Resources/Opcodes") + endif() +endif() + + +# Checks if dependencies for an enabled target are fulfilled. +# If FAIL_MISSING is true and the dependencies are not fulfilled, +# it will abort the cmake run. +# If FAIL_MISSING is false, it will set the option to OFF. +# If the target is not enabled, it will do nothing. +# example: check_deps(BUILD_NEW_PARSER FLEX_EXECUTABLE BISON_EXECUTABLE) +function(check_deps option) + if(${option}) + set(i 1) + while( ${i} LESS ${ARGC} ) + set(dep ${ARGV${i}}) + if(NOT ${dep}) + if(FAIL_MISSING) + message(FATAL_ERROR + "${option} is enabled, but ${dep}=\"${${dep}}\"") + else() + message(STATUS "${dep}=\"${${dep}}\", so disabling ${option}") + set(${option} OFF PARENT_SCOPE) + # Set it in the local scope too + set(${option} OFF) + endif() + endif() + math(EXPR i "${i}+1") + endwhile() + endif() + if(${option}) + message(STATUS "${option} is enabled.") + else() + message(STATUS "${option} is disabled.") + endif() +endfunction(check_deps) + +# Utility function to make plugins. All plugin targets should use this as it +# sets up output directory set in top-level CmakeLists.txt +# and adds the appropriate install target +# +# libname - name of library to produce +# srcs - list of src files (must be quoted if a list) +# extralibs (OPTIONAL) - extra libraries to link the plugin to +# +# NB - this was moved here as it needs some VARS defined above +# for setting up the framework +function(make_plugin libname srcs) + if(APPLE) + add_library(${libname} SHARED ${srcs}) + else() + add_library(${libname} MODULE ${srcs}) + endif() + + set(i 2) + while( ${i} LESS ${ARGC} ) + target_link_libraries(${libname} ${ARGV${i}}) + math(EXPR i "${i}+1") + endwhile() + + set_target_properties(${libname} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + install(TARGETS ${libname} + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}" ) +endfunction(make_plugin) + +# Linux does not have a separate libintl, it is part of libc +set(LIBINTL_AVAIL (LIBINTL_LIBRARY OR LINUX)) + +if(LINUX) + message(STATUS "Building on Linux.") + add_definitions(-DLINUX -DPIPES -D_GNU_SOURCE -DHAVE_SOCKETS) + list(APPEND libcsound_LIBS ${MATH_LIBRARY} dl) + +endif() + +if(APPLE AND NOT IOS) + message(STATUS "Building on OSX") + add_definitions(-DMACOSX -DPIPES -DNO_FLTK_THREADS -DHAVE_SOCKETS) + find_library(ACCELERATE_LIBRARY Accelerate) + find_path(VECLIB_PATH "Accelerate/Accelerate.h") + include_directories(${VECLIB_PATH}) + list(APPEND libcsound_LIBS ${MATH_LIBRARY} dl ${ACCELERATE_LIBRARY}) +endif() + +if(WIN32) + add_definitions(-DWIN32) +endif() + +include(Plugin.cmake) diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..6649a0e --- /dev/null +++ b/LICENCE @@ -0,0 +1,513 @@ +/* + * csound-json : JSON parsing and manipulation for Csound + * Copyright (C) 2022 Richard Knight + * Licensed under GPL 2.1 + * The included jsoncons code is licensed under the Boost Software License + * with full license details in the relevant include/jsoncons + * and include/jsoncons_ext subdirectories. + * Examples are released under Unlicense, as specified within the CSD files + * (http://unlicense.org/) + */ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. +^L + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Plugin.cmake b/Plugin.cmake new file mode 100644 index 0000000..8dc15e2 --- /dev/null +++ b/Plugin.cmake @@ -0,0 +1,10 @@ +set(PLUGIN_NAME csjson) + +# Dependencies + # None + +# Source files +set(CPPFILES src/opcodes.cpp) +set(INCLUDES ${CSOUND_INCLUDE_DIRS} "include") +make_plugin(${PLUGIN_NAME} "${CPPFILES}") +target_include_directories(${PLUGIN_NAME} PRIVATE ${INCLUDES}) diff --git a/README.md b/README.md new file mode 100644 index 0000000..a58e93f --- /dev/null +++ b/README.md @@ -0,0 +1,381 @@ + + +# csound-json : JSON parsing and manipulation for Csound + +## Overview +csound-json provides over fifty permutations of opcodes for the parsing and manipulation of JSON. Data can be deserialised from a string or file, and similarly serialised and output to string or file. +Simple key/index access is available, as are more powerful query expressions using JSONPath and JSON Pointer. Arrays can be easily translated to native Csound arrays and reinserted to JSON structures. Mostly all operations are performed on object handles, which are Csound references to JSON objects stored internally. All opcodes are available at init time, and those feasible for k-rate performance are additionally provided. + +## Requirements +* Cmake >= 2.8.12 +* Csound with development headers >= 6.14.0 +* Compiler with C++11 support + * If using G++, >= 4.8.5 is needed due to std::regex implementation + + + +## Installation +Create a build directory at the top of the source tree, execute *cmake ..*, *make* and optionally *make install* as root. If the latter is not used/possible then the resulting library can be used with the *--opcode-lib* flag in Csound. +eg: + + mkdir build && cd build + cmake .. + make && sudo make install + +Cmake should find Csound and any other required libraries using the modules in the cmake/Modules directory and installation should be as simple as above. + +## Examples +Some examples are provided in the examples directory. + + +## Opcode reference + +### jsonloads +Parse a JSON string and load to an object handle for use in other opcodes. + + iJson jsonloads Sjson +* **iJson** loaded JSON object handle +* **Sjson** string to parse + + +### jsonload +Parse JSON from a file and load to an object handle for use in other opcodes. + + iJson jsonload Sfile +* **iJson** loaded JSON object handle +* **Sfile** file path containing JSON data + + +### jsondumps +Output a JSON object handle as a string. + + Soutput jsondumps iJson [, ipretty=1] +* **Soutput** the serialised object contents +* **iJson** JSON object handle to evaluate +* **ipretty** 1=pretty print with formatting and indenting, 0=raw + + +### jsondumpsk +Output a JSON object handle as a string at k-rate + + Soutput jsondumpsk iJson [, ipretty=1] +* **Soutput** the serialised object contents, at k-rate +* **iJson** JSON object handle to evaluate +* **ipretty** 1=pretty print with formatting and indenting, 0=raw + + +### jsondump +Output a JSON object handle to a text file. + + jsondump iJson, Sfile [, ipretty=1] +* **iJson** JSON object handle to evaluate +* **Sfile** file path to write serialised object contents to +* **ipretty** 1=pretty print with formatting and indenting, 0=raw + +### jsoninit +Initialise an empty JSON object (equivalent to `iJson jsonloads "{}"`). + + iJson jsoninit +* **iJson** new empty object + + +### jsonmerge +Shallow merge two JSON object handles, from *iJsonSource* into *iJsonTarget*. If *iupdate* = 1, then any existing keys will be altered, otherwise existing keys will not be merged. + + jsonmerge iJsonTarget, iJsonSource [, iupdate=0] +* **iJsonTarget** JSON object handle to be merged into. +* **iJsonSource** JSON object handle to be merged from. +* **iupdate** 1=update and replace any keys that exist in *iJsonTarget*; 0=skip merging existing keys. + + +### jsoninsert +Insert a JSON object handle to another JSON object handle with a specified key. An array of JSON object handles can be provided as *iJsonInsert[]*, which are then inserted as their relevant types under the key *Skey*. + + jsoninsert iJson, Skey, iJsonInsert + jsoninsert iJson, Skey, iJsonInsert[] +* **iJson** JSON object handle to insert to +* **Skey** key name under which the objects will be added +* **iJsonInsert** single JSON object handle +* **iJsonInsert[]** array of JSON object handles + + +### jsoninsertval +Insert a single value or a set of values to a JSON object handle. +When *Skeys[]* and (*Svalues[]* or *ivalues[]*) are both provided, the array lengths must match and are inserted as a set of individual key/value pairs. +When *Skey* and (*Svalues[]* or *ivalues[]*) are provided, the values are inserted into *Skey* as an array. + + jsoninsertval iJson, Skey, Svalue + jsoninsertval iJson, Skey, ivalue + jsoninsertval iJson, Skey, ivalues[] + jsoninsertval iJson, Skey, Svalues[] + jsoninsertval iJson, Skeys[], ivalues[] + jsoninsertval iJson, Skeys[], Svalues[] +* **iJson** JSON object handle to insert to +* **Skey** key name under which the value(s) will be added +* **Svalue** string value to be added +* **ivalue** numeric value to be added +* **Svalues[]** array of string values to be added +* **ivalues[]** array of numeric values to be added +* **Skeys[]** array of keys to be used for key/value operation + + +### jsoninsertvalk +Insert a single value or a set of values to a JSON object handle, at k-rate +When *Skeys[]* and (*Svalues[]* or *kvalues[]*) are both provided, the array lengths must match and are inserted as a set of individual key/value pairs. +When *Skey* and (*Svalues[]* or *kvalues[]*) are provided, the values are inserted into *Skey* as an array. + + jsoninsertvalk iJson, Skey, Svalue + jsoninsertvalk iJson, Skey, kvalue + jsoninsertvalk iJson, Skey, kvalues[] + jsoninsertvalk iJson, Skey, Svalues[] + jsoninsertvalk iJson, Skeys[], kvalues[] + jsoninsertvalk iJson, Skeys[], Svalues[] +* **iJson** JSON object handle to insert to +* **Skey** key name under which the value(s) will be added +* **Svalue** string value to be added +* **kvalue** numeric value to be added +* **Svalues[]** array of string values to be added +* **kvalues[]** array of numeric values to be added +* **Skeys[]** array of keys to be used for key/value operation + + +### jsontype +Get the type of a JSON object handle. + + itype jsontype iJson + Stype jsontype iJson +* **itype** numeric type: + * -1 unknown + * 0 null + * 1 string + * 2 number + * 3 boolean + * 4 array + * 5 object +* **Stype** string type, as listed above +* **iJson** JSON object handle to evaluate + + +### jsonkeys +Get the keys of a JSON object handle, if available (ie, if the type is *object*). + + Skeys[] jsonkeys iJson +* **Skeys[]** the keys +* **iJson** JSON object handle to evaluate + + +### jsonkeysk +Get the keys of a JSON object handle at k-rate, if available (ie, if the type is *object*). + + Skeys[] jsonkeysk iJson +* **Skeys[]** the keys, at k-rate +* **iJson** JSON object handle to evaluate + + +### jsonsize +Get the size of a JSON object handle (ie, the array size or number of object keys). + + isize jsonsize iJson +* **isize** number of elements +* ** iJson** JSON object handle to evaluate + + +### jsonsizek +Get the size of a JSON object handle at k-rate (ie, the array size or number of object keys). + + ksize jsonsizek iJson +* **ksize** number of elements +* ** iJson** JSON object handle to evaluate + + +### jsonget +Get a JSON object handle of the object contained in the specified key or index. + + iJsonOutput jsonget iJson, Skey + iJsonOutput jsonget iJson, index +* **iJsonOutput** the JSON object handle as contained in *Skey* or *index* +* **iJson** JSON object handle to evaluate +* **Skey** key for accessing an object +* **index** index for accessing an array + + +### jsonpath +Perform a JSONPath query and obtain the resulting JSON object handle. + + iJsonOutput jsonpath iJson, Spath +* **iJsonOutput** JSON object handle specified by *Spath* +* **iJson** JSON object handle to evaluate +* **Spath** JSONPath expression + + +### jsonpathrplval +Replace a value in a location specified by the JSONPath expression *Spath* + + jsonpathrplval iJson, Spath, ivalue + jsonpathrplval iJson, Spath, Svalue +* **iJson** JSON object handle to evaluate +* **Spath** JSONPath expression +* **ivalue** numeric value to replace target with +* **Svalue** string value to replace target with + + +### jsonpathrplvalk +Replace a value in a location specified by the JSONPath expression *Spath* at k-rate. + + jsonpathrplvalk iJson, Spath, kvalue + jsonpathrplvalk iJson, Spath, Svalue +* **iJson** JSON object handle to evaluate +* **Spath** JSONPath expression +* **kvalue** numeric value to replace target with +* **Svalue** string value to replace target with + + +### jsonpathrpl +Replace a JSON object specified by the JSONPath expression *Spath* + + jsonpathrpl iJson, Spath, iJsonNew +* **iJson** JSON object handle to evaluate +* **Spath** JSONPath expression +* **iJsonNew** JSON object handle to replace target with + + +### jsonptr +Perform a JSON Pointer query and obtain the resulting JSON object handle. + + iJsonOutput jsonptr iJson, Spointer +* **iJsonOutput** JSON object handle specified by *Spointer* +* **iJson** JSON object handle to evaluate +* **Spointer** JSON Pointer expression + + +### jsonptrhas +Check if a JSON Pointer query results in a valid existing object. + + iexists jsonptrhas iJson, Spointer +* **iexists** 1 if existing, 0 if not +* **iJson** JSON object handle to evaluate +* **Spointer** JSON Pointer expression + + +### jsonptrhask +Check if a JSON Pointer query results in a valid existing object, at k-rate + + kexists jsonptrhask iJson, Spointer +* **kexists** 1 if existing, 0 if not +* **iJson** JSON object handle to evaluate +* **Spointer** JSON Pointer expression + + +### jsonptraddval +Add a value at a location specified by the JSON Pointer expression *Spointer*. + + jsonptraddval iJson, Spointer, ivalue + jsonptraddval iJson, Spointer, Svalue +* **iJson** JSON object handle to add value to +* **Spointer** JSON Pointer expression +* **ivalue** numeric value to add +* **Svalue** string value to add + + +### jsonptraddvalk +Add a value at a location specified by the JSON Pointer expression *Spointer*. + + jsonptraddvalk iJson, Spointer, kvalue + jsonptraddvalk iJson, Spointer, Svalue +* **iJson** JSON object handle to add value to +* **Spointer** JSON Pointer expression +* **kvalue** numeric value to add +* **Svalue** string value to add + +### jsonptradd +Add a JSON object handle to a location specified by the JSON Pointer expression *Spointer*. + + jsonptradd iJson, Spointer, iJsonNew + +* **iJson** JSON object handle to add to +* **Spointer** JSON pointer expression +* **iJsonNew** JSON object handle to add to *iJson* + + +### jsonptrrm +Remove an object specified by the JSON Pointer expression *Spointer*. + + jsonptrrm iJson, Spointer +* **iJson** JSON object handle to remove from +* **Spointer** JSON pointer expression + + +### jsonptrrmk +Remove an object specified by the JSON Pointer expression *Spointer*, at k-rate. + + jsonptrrmk iJson, Spointer +* **iJson** JSON object handle to remove from +* **Spointer** JSON pointer expression, at k-rate + + +### jsonptrrplval +Replace a value specified by the JSON Pointer expression *Spointer*. + + jsonptrrplval iJson, Spointer, ivalue + jsonptrrplval iJson, Spointer, Svalue +* **iJson** JSON object handle to replace in +* **Spointer** JSON Pointer expression +* **ivalue** numeric value to set +* **Svalue** string value to set + + +### jsonptrrplvalk +Replace a value specified by the JSON Pointer expression *Spointer*, at k-rate. + + jsonptrrplvalk iJson, Spointer, kvalue + jsonptrrplvalk iJson, Spointer, Svalue +* **iJson** JSON object handle to replace in +* **Spointer** JSON Pointer expression +* **kvalue** numeric value to set +* **Svalue** string value to set + + +### jsonptrrpl +Replace an object specified by the JSON Pointer expression *Spointer*. + + jsonptrrpl iJson, Spointer, iJsonNew +* **iJson** JSON object handle to replace in +* **Spointer** JSON Pointer expression +* **iJsonNew** JSON object handle to set + + +### jsonarrval +Get an array of values from a JSON object handle. + + ivalues[] jsonarrval iJson + Svalues[] jsonarrval iJson +* **ivalues[]** returned numeric values +* **Svalues[]** returned string values +* **iJson** JSON object handle to evaluate + + +### jsonarrvalk +Get an array of values from a JSON object handle, at k-rate. + + kvalues[] jsonarrvalk iJson + Svalues[] jsonarrvalk iJson +* **kvalues[]** returned numeric values +* **Svalues[]** returned string values +* **iJson** JSON object handle to evaluate + + +### jsonarr +Get an array of JSON object handles from a JSON object handle. + + iJsonObjects[] jsonarr iJson +* **iJsonObjects[]** array of JSON object handles +* **iJson** JSON object handle to evaluate + + + +## Links +* JSON Pointer is standardised in a [RFC specification](https://www.rfc-editor.org/rfc/rfc6901). +* JSONPath is not standardised but has an original [specification](https://goessner.net/articles/JsonPath/) and an [IETF standardisation](https://datatracker.ietf.org/wg/jsonpath/about/) working group in progress as of writing. +* JSONPath and JSON Pointer online evaluators may be helpful for authoring queries, [here is one such tool](https://www.jsonquerytool.com/). +* csound-json is powered by [jsoncons](https://github.com/danielaparker/jsoncons). + diff --git a/cmake/CompilerOptimizations.cmake b/cmake/CompilerOptimizations.cmake new file mode 100644 index 0000000..543daf8 --- /dev/null +++ b/cmake/CompilerOptimizations.cmake @@ -0,0 +1,53 @@ + +check_c_compiler_flag(-ftree-vectorize HAS_TREE_VECTORIZE) +check_cxx_compiler_flag(-ftree-vectorize HAS_CXX_TREE_VECTORIZE) +if (HAS_TREE_VECTORISE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftree-vectorize") +endif() +if (HAS_CXX_TREE_VECTORISE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftree-vectorize") +endif() + + +check_c_compiler_flag(-ffast-math HAS_FAST_MATH) +check_cxx_compiler_flag(-ffast-math HAS_CXX_FAST_MATH) +if (HAS_FAST_MATH AND NOT MINGW) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math") +endif() +if (HAS_CXX_FAST_MATH AND NOT MINGW) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") +endif() + + + +if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +check_c_compiler_flag(-mfpmath=sse HAS_FPMATH_SSE) +check_cxx_compiler_flag(-mfpmath=sse HAS_CXX_FPMATH_SSE) + if (HAS_FPMATH_SSE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse") +endif() +if (HAS_CXX_FPMATH_SSE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse") +endif() + +endif() + + +check_c_compiler_flag(-msse2 HAS_SSE2) +check_cxx_compiler_flag(-msse2 HAS_CXX_SSE2) + if (HAS_SSE2 AND NOT IOS AND NOT WASM) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") +endif() +if (HAS_CXX_SSE2 AND NOT IOS AND NOT WASM) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2") +endif() + + +check_c_compiler_flag(-fomit-frame-pointer HAS_OMIT_FRAME_POINTER) +check_cxx_compiler_flag(-fomit-frame-pointer HAS_CXX_OMIT_FRAME_POINTER) +if (HAS_OMIT_FRAME_POINTER) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") +endif() +if (HAS_CXX_OMIT_FRAME_POINTER) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer") +endif() diff --git a/cmake/Modules/FindCsound.cmake b/cmake/Modules/FindCsound.cmake new file mode 100644 index 0000000..24c90d9 --- /dev/null +++ b/cmake/Modules/FindCsound.cmake @@ -0,0 +1,62 @@ +# Try to find the Csound library. +# Once done this will define: +# CSOUND_FOUND - System has the Csound library +# CSOUND_INCLUDE_DIRS - The Csound include directories. +# CSOUND_LIBRARIES - The libraries needed to use the Csound library. + +# RKnight 2021-07-21 : quick copy paste hack to deal with 32 bit if not using double + +if(USE_DOUBLE) + # 64 bit + if(APPLE) + find_path(CSOUND_INCLUDE_DIR csound.h HINTS /Library/Frameworks/CsoundLib64.framework/Headers + "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework/Headers") + elseif(WIN32) + find_path(CSOUND_INCLUDE_DIR csound.h PATH_SUFFIXES csound + HINTS "c:\\Program Files\\Csound6_x64\\include") + else() + find_path(CSOUND_INCLUDE_DIR csound.h PATH_SUFFIXES csound) + endif() + + if(APPLE) + find_library(CSOUND_LIBRARY NAMES CsoundLib64 HINTS /Library/Frameworks/CsoundLib64.framework/ + "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework") + elseif(WIN32) + find_library(CSOUND_LIBRARY NAMES csound64 HINTS "c:\\Program Files\\Csound6_x64\\lib") + else() + find_library(CSOUND_LIBRARY NAMES csound64 csound) + endif() + +else() + # 32 bit + if(APPLE) + find_path(CSOUND_INCLUDE_DIR csound.h HINTS /Library/Frameworks/CsoundLib.framework/Headers + "$ENV{HOME}/Library/Frameworks/CsoundLib.framework/Headers") + elseif(WIN32) + find_path(CSOUND_INCLUDE_DIR csound.h PATH_SUFFIXES csound + HINTS "c:\\Program Files (x86)\\Csound6\\include") + else() + find_path(CSOUND_INCLUDE_DIR csound.h PATH_SUFFIXES csound) + endif() + + if(APPLE) + find_library(CSOUND_LIBRARY NAMES CsoundLib HINTS /Library/Frameworks/CsoundLib.framework/ + "$ENV{HOME}/Library/Frameworks/CsoundLib.framework") + elseif(WIN32) + find_library(CSOUND_LIBRARY NAMES csound HINTS "c:\\Program Files (x86)\\Csound6\\lib") + else() + find_library(CSOUND_LIBRARY NAMES csound csound) + endif() + +endif() + + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set CSOUND_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(CSOUND + CSOUND_LIBRARY CSOUND_INCLUDE_DIR) +mark_as_advanced(CSOUND_INCLUDE_DIR CSOUND_LIBRARY) + +set(CSOUND_INCLUDE_DIRS ${CSOUND_INCLUDE_DIR}) +set(CSOUND_LIBRARIES ${CSOUND_LIBRARY} ) diff --git a/examples/example1.csd b/examples/example1.csd new file mode 100644 index 0000000..5ba2436 --- /dev/null +++ b/examples/example1.csd @@ -0,0 +1,133 @@ +/* + csound-json example 1 + + load data, examine and modify, print and output to file + +*/ + + + Released into the public domain under the Unlicense license + http://unlicense.org/ + + +-odac +-d + + +sr = 44100 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + + +; base JSON string +gSjson = {{ + { + "instruments": { + "oscil1": [ + [0, 2, 440], + [2, 4, 880], + [4, 2, 220] + ], + "oscil2": [ + [6, 2, 880, "word"], + [8, 2, 220] + ] + }, + "details": { + "createdby": "Richard Knight", + "createdon": "2022-09-02" + } + } +}} + +instr boot + + ; load JSON data and print the type detected + iJson jsonloads gSjson + prints sprintf("1. Loaded; type = %s\n", jsontype:S(iJson)) + + ; query using JSONPath, print type found and dump string + iqueried jsonpath iJson, "$.instruments['oscil2'][0][:]" + prints sprintf("2. Path query 1; type = %s\n", jsontype:S(iqueried)) + prints sprintf("3. Dumped path query 1:\n%s\n\n", jsondumps(iqueried)) + + ; output JSONPath query result to numeric array (strings result in 0) + prints "4. Path query 1 to numeric array:\n" + idata[] jsonarrval iqueried + printarray idata + + ; output JSONPath query result to string array + prints "\n5. Path query 1 to string array:\n" + Sarray[] jsonarrval iqueried + index = 0 + while (index < lenarray(Sarray)) do + prints sprintf("%s ", Sarray[index]) + index += 1 + od + prints "\n\n" + + ; query with JSON Pointer, print type and dump string + iqueried jsonptr iJson, "/instruments/oscil1" + prints sprintf("6. Pointer query 1; type = %s\n", jsontype:S(iqueried)) + prints sprintf("7. output:\n%s\n\n", jsondumps(iqueried)) + + ; query with JSON Pointer, print type (should be numeric), convert and print + iqueried jsonptr iJson, "/instruments/oscil1/0/2" + prints sprintf("8. Pointer query 2; type = %s\n", jsontype:S(iqueried)) + inum = strtod(jsondumps(iqueried)) + prints sprintf("9. output as string, converted to numeric in csound: %d\n\n", inum) + + ; query with JSON Pointer to get instruments object + iinstruments1 jsonptr iJson, "/instruments" + + ; load supplementary data from file and extract instruments object with JSON Pointer query + iJson2 jsonload "supplement.json" + prints sprintf("10. File loaded; type = %s\n", jsontype:S(iJson2)) + iinstruments2 jsonptr iJson2, "/instruments" + + + ; merge the two instruments objects + jsonmerge iinstruments1, iinstruments2, 1 + prints sprintf("11. Merged instruments; dumped data:\n%s\n\n", jsondumps(iinstruments1)) + + ; insert the merged object back to the "instruments" key + jsoninsert iJson, "instruments", iinstruments1 + prints sprintf("12. Inserted instruments back to original; dumped data:\n%s\n\n", jsondumps(iJson)) + + ; create a new empty JSON object and insert some values with keys + iJson jsoninit + jsoninsertval iJson, "hello", "world" + jsoninsertval iJson, "numeric", 3 + + ; create JSON object from string and insert with key + jsoninsert iJson, "array", jsonloads("[1,2,3]") + + ; insert array values + iarraydata[] fillarray 3, 2, 1, 0 + jsoninsertval iJson, "arraycs", iarraydata + + ; insert string array values + Sarraydata[] fillarray "hello", "world", "again" + jsoninsertval iJson, "stringarraycs", Sarraydata + + iJsonarray[] fillarray jsonloads("{\"a\": \"b\"}"), jsonloads("{\"c\": 321, \"e\": 123}") + jsoninsert iJson, "subobject", iJsonarray + + ; use JSON Pointer to replace a value + jsonptrrplval iJson, "/hello", "goodbye" + + ; use JSONPath to replace value + jsonpathrplval iJson, "$.subobject[0]['a']", "this is a" + + prints sprintf("13. Inserted to new; dumped data:\n%s\n\n", jsondumps(iJson)) + + jsondump iJson, "example_output.json" + prints "14. Wrote to output file\n" +endin + + + +i"boot" 0 1 + + diff --git a/examples/example2.csd b/examples/example2.csd new file mode 100644 index 0000000..fd03ec3 --- /dev/null +++ b/examples/example2.csd @@ -0,0 +1,120 @@ +/* + csound-json example 2 + + merge objects, play score based on contents + +*/ + + + Released into the public domain under the Unlicense license + http://unlicense.org/ + + +-d +-m0 + + + +sr = 44100 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + + +; base JSON string +gSjson = {{ + { + "instruments": { + "oscil1": [ + [0, 2, 440], + [2, 4, 880], + [4, 2, 220] + ], + "oscil2": [ + [6, 2, 330], + [8, 2, 660] + ] + }, + "details": { + "createdby": "Richard Knight", + "createdon": "2022-09-02" + } + } +}} + + +instr boot + + ; load the string and the supplementary file + iJbase jsonloads gSjson + iJsupplement jsonload "supplement.json" + + ; get the instruments objects and merge them + iJinstrs1 jsongetval iJbase, "instruments" + iJinstrs2 jsongetval iJsupplement, "instruments" + jsonmerge iJinstrs1, iJinstrs2, 1 + + ; get keys of the resulting instruments + Skeys[] jsonkeys iJinstrs1 + + ; loop through the keys + indexi = 0 + while (indexi < lenarray(Skeys)) do + Sinstrument = Skeys[indexi] + + ; get the relevant instrument object + iJscore jsongetval iJinstrs1, Sinstrument + + ; score items are retrieved as handles to JSON arrays, in a Csound array + iJscorelines[] jsonarr iJscore + + ; loop through each of the JSON arrays + indexs = 0 + while (indexs < lenarray(iJscorelines)) do + + ; get the JSON array values and format/call the scoreline accordingly + isc[] jsonarrval iJscorelines[indexs] + Scoreline = sprintf("i\"%s\" %d %d %d\n", Sinstrument, isc[0], isc[1], isc[2]) + prints Scoreline + scoreline_i Scoreline + indexs += 1 + od + indexi += 1 + od +endin + + +/* + The sound producting instruments to be called +*/ +instr oscil1 + ifreq = p4 + kamp linseg 1, p3*0.7, 1, p3*0.3, 0 + aout oscil kamp, ifreq, 1 + outs aout, aout +endin + +instr oscil2 + ifreq = p4 + kamp linseg 1, p3*0.7, 1, p3*0.3, 0 + aout oscil kamp, ifreq, 2 + outs aout, aout +endin + +instr oscil3 + ifreq = p4 + kamp linseg 1, p3*0.7, 1, p3*0.3, 0 + aout oscil kamp, ifreq, 3 + outs aout, aout +endin + + + + +f1 0 16384 10 1 ; Sine +f2 0 16384 10 1 0.5 0.3 0.25 0.2 0.167 0.14 0.125 .111 ; Sawtooth +f3 0 16384 10 1 0 0.3 0 0.2 0 0.14 0 .111 ; Square +f0 15 +i"boot" 0 1 + + \ No newline at end of file diff --git a/examples/example3.csd b/examples/example3.csd new file mode 100644 index 0000000..7d02ea4 --- /dev/null +++ b/examples/example3.csd @@ -0,0 +1,54 @@ +/* + csound-json example 3 + + add values to an object at k-rate and print them after + +*/ + + + Released into the public domain under the Unlicense license + http://unlicense.org/ + + +-d +-m0 + + +sr = 44100 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + + +instr write_values + + ; create empty object + iJson jsoninit + + ; insert some values to the object four times a second + kmetro metro 4 + kindex init 1 + if (kmetro == 1) then + jsoninsertvalk iJson, sprintfk("key%03d", kindex), kindex*random:k(1, 10) + kindex += 1 + endif + + ; print what has been inserted at the end + schedule "print_values", p3, 1, iJson +endin + + +instr print_values + iJson = p4 + + ; dump JSON and print + prints sprintf("%s\n\n", jsondumps(iJson)) +endin + + + + +f0 11 +i"write_values" 0 10 + + \ No newline at end of file diff --git a/examples/example4.csd b/examples/example4.csd new file mode 100644 index 0000000..b6bc78c --- /dev/null +++ b/examples/example4.csd @@ -0,0 +1,57 @@ +/* + csound-json example 4 + + fill JSON object with various data at init time + +*/ + + + Released into the public domain under the Unlicense license + http://unlicense.org/ + + +-d +-m0 + + +sr = 44100 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + + +instr boot + + ; empty object + iJson jsoninit + + ; add key/value combination as "description" object + Skeys[] fillarray "colour", "taste", "smell" + Svalues[] fillarray "blue", "sweet", "vile" + iJsonSub1 jsoninit + jsoninsertval iJsonSub1, Skeys, Svalues + jsoninsert iJson, "description", iJsonSub1 + + ; add key/value combination and additional value to iJsonSub2 + Skeys[] fillarray "height", "width" + ivalues[] fillarray 35.4, 6.41 + iJsonSub2 jsoninit + jsoninsertval iJsonSub2, Skeys, ivalues + jsoninsertval iJsonSub2, "depth", 16.439 + + ; add iJsonSub2 to array along with new specified string objects + iJsonObjects[] fillarray iJsonSub2, jsonloads("{\"not\": \"much\"}"), jsonloads("[1,2,3]") + + ; add all of the above iJsonObjects back into the main object under "measurements" key + jsoninsert iJson, "measurements", iJsonObjects + + ; show the result + prints sprintf("%s\n\n", jsondumps(iJson)) +endin + + + + +i"boot" 0 1 + + \ No newline at end of file diff --git a/examples/example5.csd b/examples/example5.csd new file mode 100644 index 0000000..d30629b --- /dev/null +++ b/examples/example5.csd @@ -0,0 +1,112 @@ +/* + csound-json example 5 + + fill JSON object with various data at init time + +*/ + + + Released into the public domain under the Unlicense license + http://unlicense.org/ + + +-d +-m0 + + +sr = 44100 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + + +/* + Create a random alphabetic string +*/ +opcode randstring, S, 0 + Soutput = sprintf("%c%c%c%c%c%c", \ + random(97, 122), random(97, 122),\ + random(97, 122), random(97, 122),\ + random(97, 122), random(97, 122)\ + ) + xout Soutput +endop + + +instr boot + iJson1 jsoninit + iJson2 jsoninit + schedule "run1", 0, 5, iJson1 + schedule "run2", 5, 5, iJson2 + schedule "process", 10, 1, iJson1, iJson2 + turnoff +endin + + +instr run1 + iJson = p4 + kdata[] init 4 + kindex init 1 + kmetro metro 5 + + ; add random numeric values with incremental key names + if (kmetro == 1) then + Skey = sprintfk("sound%03d", kindex) + kdata[0] = random:k(0, 1) + kdata[1] = random:k(0, 1) + kdata[2] = random:k(0, 1) + kdata[3] = random:k(0, 1) + jsoninsertvalk iJson, Skey, kdata + kindex += 1 + endif +endin + + +instr run2 + iJson = p4 + Skeys[] init 3 + Svalues[] init 3 + + ; add random key/value strings + index = 0 + while (index < 10) do + Skeys[0] randstring + Skeys[1] randstring + Skeys[2] randstring + Svalues[0] randstring + Svalues[1] randstring + Svalues[2] randstring + jsoninsertval iJson, Skeys, Svalues + index += 1 + od +endin + + +instr process + + ; populated objects + iJsons[] fillarray p4, p5 + + ; load file + iJson jsonload "supplement.json" + + ; insert as array of objects under "added" key + jsoninsert iJson, "added", iJsons + + ; add f-table sine wave points + ipoints[] tab2array 1 + jsoninsertval iJson, "sine", ipoints + + ; show and write output + prints sprintf("%s\n\n", jsondumps(iJson)) + jsondump iJson, "example_output.json" +endin + + + + + +f1 0 64 10 1 ; sine +i"boot" 0 15 + + \ No newline at end of file diff --git a/examples/supplement.json b/examples/supplement.json new file mode 100644 index 0000000..5e20466 --- /dev/null +++ b/examples/supplement.json @@ -0,0 +1,10 @@ +{ + "instruments": { + "oscil3": [ + [10, 1, 123], + [11, 1, 456], + [12, 1, 789], + [13, 1, 432] + ] + } +} \ No newline at end of file diff --git a/include/handling.h b/include/handling.h new file mode 100644 index 0000000..c5e0691 --- /dev/null +++ b/include/handling.h @@ -0,0 +1,53 @@ +/* + handling.h + Copyright (C) 2021 Richard Knight + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ +#include + +const char* handleIdentifier = "::%s%d"; + +/* + * Obtain global object of typename from global variables by handle + */ +template +T* getHandle(csnd::Csound* csound, MYFLT handle, const char* name) { + char buffer[32]; + snprintf(buffer, 32, handleIdentifier, name, (int)handle); + return (T*) csound->query_global_variable(buffer); +} + + +/* + * Create global object of typename in global variables, returning handle + */ +template +MYFLT createHandle(csnd::Csound* csound, T** data, const char* name) { + char buffer[32]; + int handle = 0; + snprintf(buffer, 32, handleIdentifier, name, handle); + while ((*data = (T*) csound->query_global_variable(buffer)) != NULL) { + snprintf(buffer, 32, handleIdentifier, name, ++handle); + } + csound->create_global_variable(buffer, sizeof(T)); + *data = (T*) csound->query_global_variable(buffer); + + return FL(handle); +} + + diff --git a/include/jsoncons/LICENSE b/include/jsoncons/LICENSE new file mode 100644 index 0000000..ecf46ab --- /dev/null +++ b/include/jsoncons/LICENSE @@ -0,0 +1,28 @@ +// Copyright Daniel Parker 2013 - 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/include/jsoncons/allocator_holder.hpp b/include/jsoncons/allocator_holder.hpp new file mode 100644 index 0000000..b69fcfe --- /dev/null +++ b/include/jsoncons/allocator_holder.hpp @@ -0,0 +1,38 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_ALLOCATOR_HOLDER_HPP +#define JSONCONS_ALLOCATOR_HOLDER_HPP + +namespace jsoncons { + +template +class allocator_holder +{ +public: + using allocator_type = Allocator; +private: + allocator_type alloc_; +public: + allocator_holder() = default; + allocator_holder(const allocator_holder&) = default; + allocator_holder(allocator_holder&&) = default; + allocator_holder& operator=(const allocator_holder&) = default; + allocator_holder& operator=(allocator_holder&&) = default; + allocator_holder(const allocator_type& alloc) + : alloc_(alloc) + {} + ~allocator_holder() = default; + + allocator_type get_allocator() const + { + return alloc_; + } +}; + +} + +#endif diff --git a/include/jsoncons/basic_json.hpp b/include/jsoncons/basic_json.hpp new file mode 100644 index 0000000..31de47b --- /dev/null +++ b/include/jsoncons/basic_json.hpp @@ -0,0 +1,5839 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BASIC_JSON_HPP +#define JSONCONS_BASIC_JSON_HPP + +#include // std::numeric_limits +#include +#include +#include +#include +#include +#include // std::allocator +#include +#include // std::memcpy +#include // std::swap +#include // std::initializer_list +#include // std::move +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + namespace type_traits { + + namespace detail { + + template + using + basic_json_t = basic_json; + + } // namespace detail + + template + struct is_basic_json : std::false_type {}; + + template + struct is_basic_json::type>::value>::type + > : std::true_type {}; + + } // namespace type_traits + + namespace detail { + + template + class random_access_iterator_wrapper + { + }; + + template + class random_access_iterator_wrapper::iterator_category, + std::random_access_iterator_tag>::value>::type> + { + Iterator it_; + bool has_value_; + + template + friend class random_access_iterator_wrapper; + public: + using iterator_category = std::random_access_iterator_tag; + + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + + random_access_iterator_wrapper() : it_(), has_value_(false) + { + } + + explicit random_access_iterator_wrapper(Iterator ptr) : it_(ptr), has_value_(true) + { + } + + random_access_iterator_wrapper(const random_access_iterator_wrapper&) = default; + random_access_iterator_wrapper(random_access_iterator_wrapper&&) = default; + random_access_iterator_wrapper& operator=(const random_access_iterator_wrapper&) = default; + random_access_iterator_wrapper& operator=(random_access_iterator_wrapper&&) = default; + + template ::value && std::is_convertible::value>::type> + random_access_iterator_wrapper(const random_access_iterator_wrapper& other) + : it_(other.it_), has_value_(other.has_value_) + { + } + + operator Iterator() const + { + return it_; + } + + reference operator*() const + { + return *it_; + } + + pointer operator->() const + { + return &(*it_); + } + + random_access_iterator_wrapper& operator++() + { + ++it_; + return *this; + } + + random_access_iterator_wrapper operator++(int) + { + random_access_iterator_wrapper temp = *this; + ++*this; + return temp; + } + + random_access_iterator_wrapper& operator--() + { + --it_; + return *this; + } + + random_access_iterator_wrapper operator--(int) + { + random_access_iterator_wrapper temp = *this; + --*this; + return temp; + } + + random_access_iterator_wrapper& operator+=(const difference_type offset) + { + it_ += offset; + return *this; + } + + random_access_iterator_wrapper operator+(const difference_type offset) const + { + random_access_iterator_wrapper temp = *this; + return temp += offset; + } + + random_access_iterator_wrapper& operator-=(const difference_type offset) + { + return *this += -offset; + } + + random_access_iterator_wrapper operator-(const difference_type offset) const + { + random_access_iterator_wrapper temp = *this; + return temp -= offset; + } + + difference_type operator-(const random_access_iterator_wrapper& rhs) const noexcept + { + return it_ - rhs.it_; + } + + reference operator[](const difference_type offset) const noexcept + { + return *(*this + offset); + } + + bool operator==(const random_access_iterator_wrapper& rhs) const noexcept + { + if (!has_value_ || !rhs.has_value_) + { + return has_value_ == rhs.has_value_ ? true : false; + } + else + { + return it_ == rhs.it_; + } + } + + bool operator!=(const random_access_iterator_wrapper& rhs) const noexcept + { + return !(*this == rhs); + } + + bool operator<(const random_access_iterator_wrapper& rhs) const noexcept + { + if (!has_value_ || !rhs.has_value_) + { + return has_value_ == rhs.has_value_ ? false :(has_value_ ? false : true); + } + else + { + return it_ < rhs.it_; + } + } + + bool operator>(const random_access_iterator_wrapper& rhs) const noexcept + { + return rhs < *this; + } + + bool operator<=(const random_access_iterator_wrapper& rhs) const noexcept + { + return !(rhs < *this); + } + + bool operator>=(const random_access_iterator_wrapper& rhs) const noexcept + { + return !(*this < rhs); + } + + inline + friend random_access_iterator_wrapper operator+( + difference_type offset, random_access_iterator_wrapper next) + { + return next += offset; + } + + bool has_value() const + { + return has_value_; + } + }; + } // namespace detail + + struct sorted_policy + { + template + using object = sorted_json_object; + + template + using array = json_array; + + using parse_error_handler_type = default_json_parsing; + }; + + struct order_preserving_policy + { + template + using object = order_preserving_json_object; + + template + using array = json_array; + + using parse_error_handler_type = default_json_parsing; + }; + + #if !defined(JSONCONS_NO_DEPRECATED) + using preserve_order_policy = order_preserving_policy; + #endif + + template + class range + { + public: + using iterator = IteratorT; + using const_iterator = ConstIteratorT; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + private: + iterator first_; + iterator last_; + public: + range(const IteratorT& first, const IteratorT& last) + : first_(first), last_(last) + { + } + + iterator begin() + { + return first_; + } + iterator end() + { + return last_; + } + const_iterator cbegin() + { + return first_; + } + const_iterator cend() + { + return last_; + } + reverse_iterator rbegin() + { + return reverse_iterator(last_); + } + reverse_iterator rend() + { + return reverse_iterator(first_); + } + const_reverse_iterator crbegin() + { + return reverse_iterator(last_); + } + const_reverse_iterator crend() + { + return reverse_iterator(first_); + } + }; + + // is_proxy_of + + template + struct is_proxy_of : std::false_type {}; + + template + struct is_proxy_of::value>::type + > : std::true_type {}; + + + // is_proxy + + template + struct is_proxy : std::false_type {}; + + template + struct is_proxy::value>::type + > : std::true_type {}; + + template + class basic_json + { + public: + + using allocator_type = Allocator; + + using implementation_policy = ImplementationPolicy; + + using parse_error_handler_type = typename ImplementationPolicy::parse_error_handler_type; + + using char_type = CharT; + using char_traits_type = std::char_traits; + using string_view_type = jsoncons::basic_string_view; + + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + using key_type = std::basic_string; + + + using reference = basic_json&; + using const_reference = const basic_json&; + using pointer = basic_json*; + using const_pointer = const basic_json*; + + using key_value_type = key_value; + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("no replacement") typedef basic_json value_type; + JSONCONS_DEPRECATED_MSG("no replacement") typedef std::basic_string string_type; + JSONCONS_DEPRECATED_MSG("Instead, use key_value_type") typedef key_value_type kvp_type; + JSONCONS_DEPRECATED_MSG("Instead, use key_value_type") typedef key_value_type member_type; + #endif + + using array = typename ImplementationPolicy::template array; + + using key_value_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + using object = typename ImplementationPolicy::template object; + + using object_iterator = jsoncons::detail::random_access_iterator_wrapper; + using const_object_iterator = jsoncons::detail::random_access_iterator_wrapper; + using array_iterator = typename array::iterator; + using const_array_iterator = typename array::const_iterator; + + private: + + static constexpr uint8_t major_type_shift = 0x04; + static constexpr uint8_t additional_information_mask = (1U << 4) - 1; + + class common_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + }; + + class null_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + + null_storage(semantic_tag tag = semantic_tag::none) + : storage_kind_(static_cast(json_storage_kind::null_value)), length_(0), tag_(tag) + { + } + }; + + class empty_object_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + + empty_object_storage(semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::empty_object_value)), length_(0), tag_(tag) + { + } + }; + + class bool_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + bool val_; + public: + bool_storage(bool val, semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::bool_value)), length_(0), tag_(tag), + val_(val) + { + } + + bool value() const + { + return val_; + } + + }; + + class int64_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + int64_t val_; + public: + int64_storage(int64_t val, + semantic_tag tag = semantic_tag::none) + : storage_kind_(static_cast(json_storage_kind::int64_value)), length_(0), tag_(tag), + val_(val) + { + } + + int64_t value() const + { + return val_; + } + }; + + class uint64_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + uint64_t val_; + public: + uint64_storage(uint64_t val, + semantic_tag tag = semantic_tag::none) + : storage_kind_(static_cast(json_storage_kind::uint64_value)), length_(0), tag_(tag), + val_(val) + { + } + + uint64_t value() const + { + return val_; + } + }; + + class half_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + uint16_t val_; + public: + half_storage(uint16_t val, semantic_tag tag = semantic_tag::none) + : storage_kind_(static_cast(json_storage_kind::half_value)), length_(0), tag_(tag), + val_(val) + { + } + + uint16_t value() const + { + return val_; + } + }; + + class double_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + double val_; + public: + double_storage(double val, + semantic_tag tag = semantic_tag::none) + : storage_kind_(static_cast(json_storage_kind::double_value)), length_(0), tag_(tag), + val_(val) + { + } + + double value() const + { + return val_; + } + }; + + class short_string_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + static constexpr size_t capacity = (2*sizeof(uint64_t) - 2*sizeof(uint8_t))/sizeof(char_type); + char_type data_[capacity]; + public: + static constexpr size_t max_length = capacity - 1; + + short_string_storage(semantic_tag tag, const char_type* p, uint8_t length) + : storage_kind_(static_cast(json_storage_kind::short_string_value)), length_(length), tag_(tag) + { + JSONCONS_ASSERT(length <= max_length); + std::memcpy(data_,p,length*sizeof(char_type)); + data_[length] = 0; + } + + short_string_storage(const short_string_storage& val) + : storage_kind_(val.storage_kind_), length_(val.length_), tag_(val.tag_) + { + std::memcpy(data_,val.data_,val.length_*sizeof(char_type)); + data_[length_] = 0; + } + + short_string_storage& operator=(const short_string_storage& val) = delete; + + uint8_t length() const + { + return length_; + } + + const char_type* data() const + { + return data_; + } + + const char_type* c_str() const + { + return data_; + } + }; + + // long_string_storage + class long_string_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + jsoncons::detail::string_wrapper s_; + public: + + long_string_storage(semantic_tag tag, const char_type* data, std::size_t length, const Allocator& a) + : storage_kind_(static_cast(json_storage_kind::long_string_value)), length_(0), tag_(tag), + s_(data, length, a) + { + } + + long_string_storage(const long_string_storage& val) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(val.s_) + { + } + + long_string_storage(long_string_storage&& val) noexcept + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(nullptr) + { + swap(val); + } + + long_string_storage(const long_string_storage& val, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(val.s_, a) + { + } + + ~long_string_storage() noexcept + { + } + + long_string_storage& operator=(const long_string_storage& val) = delete; + + long_string_storage& operator=(long_string_storage&& val) noexcept = delete; + + void swap(long_string_storage& val) noexcept + { + s_.swap(val.s_); + } + + const char_type* data() const + { + return s_.data(); + } + + const char_type* c_str() const + { + return s_.c_str(); + } + + std::size_t length() const + { + return s_.length(); + } + + allocator_type get_allocator() const + { + return s_.get_allocator(); + } + }; + + // byte_string_storage + class byte_string_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + jsoncons::detail::tagged_string_wrapper s_; + public: + + byte_string_storage(semantic_tag tag, const uint8_t* data, std::size_t length, uint64_t ext_tag, const Allocator& alloc) + : storage_kind_(static_cast(json_storage_kind::byte_string_value)), length_(0), tag_(tag), + s_(data, length, ext_tag, alloc) + { + } + + byte_string_storage(const byte_string_storage& val) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(val.s_) + { + } + + byte_string_storage(byte_string_storage&& val) noexcept + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(nullptr) + { + swap(val); + } + + byte_string_storage(const byte_string_storage& val, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + s_(val.s_, a) + { + } + + ~byte_string_storage() noexcept + { + } + + void swap(byte_string_storage& val) noexcept + { + s_.swap(val.s_); + } + + const uint8_t* data() const + { + return s_.data(); + } + + std::size_t length() const + { + return s_.length(); + } + + uint64_t ext_tag() const + { + return s_.tag(); + } + + allocator_type get_allocator() const + { + return s_.get_allocator(); + } + }; + + // array_storage + class array_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + using array_allocator = typename std::allocator_traits:: template rebind_alloc; + using pointer = typename std::allocator_traits::pointer; + + pointer ptr_; + + template + void create(array_allocator alloc, Args&& ... args) + { + ptr_ = std::allocator_traits::allocate(alloc, 1); + JSONCONS_TRY + { + std::allocator_traits::construct(alloc, type_traits::to_plain_pointer(ptr_), std::forward(args)...); + } + JSONCONS_CATCH(...) + { + std::allocator_traits::deallocate(alloc, ptr_,1); + JSONCONS_RETHROW; + } + } + + void destroy() noexcept + { + array_allocator alloc(ptr_->get_allocator()); + std::allocator_traits::destroy(alloc, type_traits::to_plain_pointer(ptr_)); + std::allocator_traits::deallocate(alloc, ptr_,1); + } + public: + array_storage(const array& val, semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::array_value)), length_(0), tag_(tag) + { + create(val.get_allocator(), val); + } + + array_storage(array&& val, semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::array_value)), length_(0), tag_(tag) + { + create(val.get_allocator(), std::forward(val)); + } + + array_storage(const array& val, semantic_tag tag, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(tag) + { + create(array_allocator(a), val, a); + } + + array_storage(const array_storage& val) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_) + { + create(val.ptr_->get_allocator(), *(val.ptr_)); + } + + array_storage(array_storage&& val) noexcept + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + ptr_(nullptr) + { + std::swap(val.ptr_, ptr_); + } + + array_storage(const array_storage& val, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_) + { + create(array_allocator(a), *(val.ptr_), a); + } + ~array_storage() noexcept + { + if (ptr_ != nullptr) + { + destroy(); + } + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + + void swap(array_storage& val) noexcept + { + std::swap(val.ptr_,ptr_); + } + + array& value() + { + return *ptr_; + } + + const array& value() const + { + return *ptr_; + } + }; + + // object_storage + class object_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + using object_allocator = typename std::allocator_traits:: template rebind_alloc; + using pointer = typename std::allocator_traits::pointer; + + pointer ptr_; + + template + void create(object_allocator alloc, Args&& ... args) + { + ptr_ = std::allocator_traits::allocate(alloc, 1); + JSONCONS_TRY + { + std::allocator_traits::construct(alloc, type_traits::to_plain_pointer(ptr_), std::forward(args)...); + } + JSONCONS_CATCH(...) + { + std::allocator_traits::deallocate(alloc, ptr_,1); + JSONCONS_RETHROW; + } + } + public: + explicit object_storage(const object& val, semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::object_value)), length_(0), tag_(tag) + { + create(val.get_allocator(), val); + } + + explicit object_storage(object&& val, semantic_tag tag) + : storage_kind_(static_cast(json_storage_kind::object_value)), length_(0), tag_(tag) + { + create(val.get_allocator(), std::forward(val)); + } + + explicit object_storage(const object& val, semantic_tag tag, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(tag) + { + create(object_allocator(a), val, a); + } + + explicit object_storage(const object_storage& val) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_) + { + create(val.ptr_->get_allocator(), *(val.ptr_)); + } + + explicit object_storage(object_storage&& val) noexcept + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_), + ptr_(nullptr) + { + std::swap(val.ptr_,ptr_); + } + + explicit object_storage(const object_storage& val, const Allocator& a) + : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_) + { + create(object_allocator(a), *(val.ptr_), a); + } + + ~object_storage() noexcept + { + if (ptr_ != nullptr) + { + destroy(); + } + } + + void swap(object_storage& val) noexcept + { + std::swap(val.ptr_,ptr_); + } + + object& value() + { + return *ptr_; + } + + const object& value() const + { + return *ptr_; + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + private: + + void destroy() noexcept + { + object_allocator alloc(ptr_->get_allocator()); + std::allocator_traits::destroy(alloc, type_traits::to_plain_pointer(ptr_)); + std::allocator_traits::deallocate(alloc, ptr_,1); + } + }; + + class json_const_pointer_storage final + { + public: + uint8_t storage_kind_:4; + uint8_t length_:4; + semantic_tag tag_; + private: + const basic_json* p_; + public: + json_const_pointer_storage(const basic_json* p) + : storage_kind_(static_cast(json_storage_kind::json_const_pointer)), length_(0), tag_(p->tag()), + p_(p) + { + } + + const basic_json* value() const + { + return p_; + } + }; + + template + class proxy + { + friend class basic_json; + + ParentType& parent_; + string_view_type key_; + + proxy() = delete; + + proxy(const proxy& other) = default; + proxy(proxy&& other) = default; + proxy& operator = (const proxy& other) = delete; + proxy& operator = (proxy&& other) = delete; + + proxy(ParentType& parent, const string_view_type& key) + : parent_(parent), key_(key) + { + } + + basic_json& evaluate_with_default() + { + basic_json& val = parent_.evaluate_with_default(); + auto it = val.find(key_); + if (it == val.object_range().end()) + { + auto r = val.try_emplace(key_, json_object_arg, semantic_tag::none, val.object_value().get_allocator()); + return r.first->value(); + } + else + { + return it->value(); + } + } + + basic_json& evaluate(std::size_t index) + { + return evaluate().at(index); + } + + const basic_json& evaluate(std::size_t index) const + { + return evaluate().at(index); + } + + basic_json& evaluate(const string_view_type& index) + { + return evaluate().at(index); + } + + const basic_json& evaluate(const string_view_type& index) const + { + return evaluate().at(index); + } + public: + using proxied_type = basic_json; + using proxy_type = proxy; + + basic_json& evaluate() + { + return parent_.evaluate(key_); + } + + const basic_json& evaluate() const + { + return parent_.evaluate(key_); + } + + operator basic_json&() + { + return evaluate(); + } + + operator const basic_json&() const + { + return evaluate(); + } + + range object_range() + { + return evaluate().object_range(); + } + + range object_range() const + { + return evaluate().object_range(); + } + + range array_range() + { + return evaluate().array_range(); + } + + range array_range() const + { + return evaluate().array_range(); + } + + std::size_t size() const noexcept + { + if (!parent_.contains(key_)) + { + return 0; + } + return evaluate().size(); + } + + json_storage_kind storage_kind() const + { + return evaluate().storage_kind(); + } + + semantic_tag tag() const + { + return evaluate().tag(); + } + + json_type type() const + { + return evaluate().type(); + } + + std::size_t count(const string_view_type& name) const + { + return evaluate().count(name); + } + + allocator_type get_allocator() const + { + return evaluate().get_allocator(); + } + + uint64_t ext_tag() const + { + return evaluate().ext_tag(); + } + + bool contains(const string_view_type& key) const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + + return evaluate().contains(key); + } + + bool is_null() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_null(); + } + + bool empty() const noexcept + { + if (!parent_.contains(key_)) + { + return true; + } + return evaluate().empty(); + } + + std::size_t capacity() const + { + return evaluate().capacity(); + } + + void reserve(std::size_t n) + { + evaluate().reserve(n); + } + + void resize(std::size_t n) + { + evaluate().resize(n); + } + + template + void resize(std::size_t n, T val) + { + evaluate().resize(n,val); + } + + template + bool is(Args&&... args) const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().template is(std::forward(args)...); + } + + bool is_string() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_string(); + } + + bool is_string_view() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_string_view(); + } + + bool is_byte_string() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_byte_string(); + } + + bool is_byte_string_view() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_byte_string_view(); + } + + bool is_bignum() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_bignum(); + } + + bool is_number() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_number(); + } + bool is_bool() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_bool(); + } + + bool is_object() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_object(); + } + + bool is_array() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_array(); + } + + bool is_int64() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_int64(); + } + + bool is_uint64() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_uint64(); + } + + bool is_half() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_half(); + } + + bool is_double() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().is_double(); + } + + string_view_type as_string_view() const + { + return evaluate().as_string_view(); + } + + byte_string_view as_byte_string_view() const + { + return evaluate().as_byte_string_view(); + } + + template > + std::basic_string as_string() const + { + return evaluate().as_string(); + } + + template > + std::basic_string as_string(const SAllocator& alloc) const + { + return evaluate().as_string(alloc); + } + + template > + basic_byte_string as_byte_string() const + { + return evaluate().template as_byte_string(); + } + + template + typename std::enable_if::value,T>::type + as() const + { + return evaluate().template as(); + } + + template + typename std::enable_if::value,T>::type + as(byte_string_arg_t, semantic_tag hint) const + { + return evaluate().template as(byte_string_arg, hint); + } + + bool as_bool() const + { + return evaluate().as_bool(); + } + + double as_double() const + { + return evaluate().as_double(); + } + + template + T as_integer() const + { + return evaluate().template as_integer(); + } + + template + proxy& operator=(T&& val) + { + parent_.evaluate_with_default().insert_or_assign(key_, std::forward(val)); + return *this; + } + + basic_json& operator[](std::size_t i) + { + return evaluate_with_default().at(i); + } + + const basic_json& operator[](std::size_t i) const + { + return evaluate().at(i); + } + + proxy_type operator[](const string_view_type& key) + { + return proxy_type(*this,key); + } + + const basic_json& operator[](const string_view_type& name) const + { + return at(name); + } + + basic_json& at(const string_view_type& name) + { + return evaluate().at(name); + } + + const basic_json& at(const string_view_type& name) const + { + return evaluate().at(name); + } + + const basic_json& at_or_null(const string_view_type& name) const + { + return evaluate().at_or_null(name); + } + + const basic_json& at(std::size_t index) + { + return evaluate().at(index); + } + + const basic_json& at(std::size_t index) const + { + return evaluate().at(index); + } + + object_iterator find(const string_view_type& name) + { + return evaluate().find(name); + } + + const_object_iterator find(const string_view_type& name) const + { + return evaluate().find(name); + } + + template + T get_value_or(const string_view_type& name, U&& default_value) const + { + static_assert(std::is_copy_constructible::value, + "get_value_or: T must be copy constructible"); + static_assert(std::is_convertible::value, + "get_value_or: U must be convertible to T"); + return evaluate().template get_value_or(name,std::forward(default_value)); + } + + void shrink_to_fit() + { + evaluate_with_default().shrink_to_fit(); + } + + void clear() + { + evaluate().clear(); + } + // Remove all elements from an array or object + + object_iterator erase(const_object_iterator pos) + { + return evaluate().erase(pos); + } + // Remove a range of elements from an object + + object_iterator erase(const_object_iterator first, const_object_iterator last) + { + return evaluate().erase(first, last); + } + // Remove a range of elements from an object + + void erase(const string_view_type& name) + { + evaluate().erase(name); + } + + array_iterator erase(const_array_iterator pos) + { + return evaluate().erase(pos); + } + // Removes the element at pos + + array_iterator erase(const_array_iterator first, const_array_iterator last) + { + return evaluate().erase(first, last); + } + // Remove a range of elements from an array + + // merge + + void merge(const basic_json& source) + { + return evaluate().merge(source); + } + + void merge(basic_json&& source) + { + return evaluate().merge(std::forward(source)); + } + + void merge(object_iterator hint, const basic_json& source) + { + return evaluate().merge(hint, source); + } + + void merge(object_iterator hint, basic_json&& source) + { + return evaluate().merge(hint, std::forward(source)); + } + + // merge_or_update + + void merge_or_update(const basic_json& source) + { + return evaluate().merge_or_update(source); + } + + void merge_or_update(basic_json&& source) + { + return evaluate().merge_or_update(std::forward(source)); + } + + void merge_or_update(object_iterator hint, const basic_json& source) + { + return evaluate().merge_or_update(hint, source); + } + + void merge_or_update(object_iterator hint, basic_json&& source) + { + return evaluate().merge_or_update(hint, std::forward(source)); + } + + template + std::pair insert_or_assign(const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(name,std::forward(val)); + } + + // emplace + + template + std::pair try_emplace(const string_view_type& name, Args&&... args) + { + return evaluate().try_emplace(name,std::forward(args)...); + } + + template + object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(hint, name, std::forward(val)); + } + + template + object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args) + { + return evaluate().try_emplace(hint, name, std::forward(args)...); + } + + template + array_iterator emplace(const_array_iterator pos, Args&&... args) + { + evaluate_with_default().emplace(pos, std::forward(args)...); + } + + template + basic_json& emplace_back(Args&&... args) + { + return evaluate_with_default().emplace_back(std::forward(args)...); + } + + template + void push_back(T&& val) + { + evaluate_with_default().push_back(std::forward(val)); + } + + template + array_iterator insert(const_array_iterator pos, T&& val) + { + return evaluate_with_default().insert(pos, std::forward(val)); + } + + template + array_iterator insert(const_array_iterator pos, InputIt first, InputIt last) + { + return evaluate_with_default().insert(pos, first, last); + } + + template + void insert(InputIt first, InputIt last) + { + evaluate_with_default().insert(first, last); + } + + template + void insert(sorted_unique_range_tag tag, InputIt first, InputIt last) + { + evaluate_with_default().insert(tag, first, last); + } + + template + void dump(Args&& ... args) const + { + evaluate().dump(std::forward(args)...); + } + + template + void dump_pretty(Args&& ... args) const + { + evaluate().dump_pretty(std::forward(args)...); + } + + void swap(basic_json& val) + { + evaluate_with_default().swap(val); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const proxy& o) + { + o.dump(os); + return os; + } + + template + T get_with_default(const string_view_type& name, const T& default_value) const + { + return evaluate().template get_with_default(name,default_value); + } + + template > + T get_with_default(const string_view_type& name, const char_type* default_value) const + { + return evaluate().template get_with_default(name,default_value); + } + + std::basic_string to_string() const + { + return evaluate().to_string(); + } + + template + bool is_integer() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().template is_integer(); + } + + #if !defined(JSONCONS_NO_DEPRECATED) + + const basic_json& get_with_default(const string_view_type& name) const + { + return evaluate().at_or_null(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use tag()") + semantic_tag get_semantic_tag() const + { + return evaluate().tag(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::datetime") + bool is_datetime() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().tag() == semantic_tag::datetime; + } + + JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::epoch_second") + bool is_epoch_time() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().tag() == semantic_tag::epoch_second; + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use push_back(T&&)") + void add(T&& val) + { + evaluate_with_default().push_back(std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)") + array_iterator add(const_array_iterator pos, T&& val) + { + return evaluate_with_default().insert(pos, std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)") + std::pair set(const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(name,std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(object_iterator, const string_view_type&, T&&)") + object_iterator set(object_iterator hint, const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(hint, name, std::forward(val)); + } + + JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)") + bool has_key(const string_view_type& name) const noexcept + { + return contains(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use is()") + bool is_ulonglong() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().template is(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use is()") + bool is_longlong() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return evaluate().template is(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + int as_int() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned int as_uint() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + long as_long() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned long as_ulong() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + long long as_longlong() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned long long as_ulonglong() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + uint64_t as_uinteger() const + { + return evaluate().template as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void write(basic_json_visitor& visitor) const + { + evaluate().dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&)") + void write(std::basic_ostream& os) const + { + evaluate().dump(os); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&)") + void write(std::basic_ostream& os, const basic_json_encode_options& options) const + { + evaluate().dump(os, options); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump_pretty(std::basic_ostream&, const basic_json_encode_options&)") + void write(std::basic_ostream& os, const basic_json_encode_options& options, bool pprint) const + { + if (pprint) + { + evaluate().dump_pretty(os, options); + } + else + { + evaluate().dump(os, options); + } + } + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void to_stream(basic_json_visitor& visitor) const + { + evaluate().dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&)") + void to_stream(std::basic_ostream& os) const + { + evaluate().dump(os); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&)") + void to_stream(std::basic_ostream& os, const basic_json_encode_options& options) const + { + evaluate().dump(os,options); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump_pretty(std::basic_ostream&, const basic_json_encode_options&)") + void to_stream(std::basic_ostream& os, const basic_json_encode_options& options, bool pprint) const + { + if (pprint) + { + evaluate().dump_pretty(os,options); + } + else + { + evaluate().dump(os,options); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t)") + void resize_array(std::size_t n) + { + evaluate().resize(n); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t, T)") + void resize_array(std::size_t n, T val) + { + evaluate().resize(n,val); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range()") + range members() + { + return evaluate().object_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range()") + range members() const + { + return evaluate().object_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range()") + range elements() + { + return evaluate().array_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range()") + range elements() const + { + return evaluate().array_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()") + object_iterator begin_members() + { + return evaluate().object_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()") + const_object_iterator begin_members() const + { + return evaluate().object_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()") + object_iterator end_members() + { + return evaluate().object_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()") + const_object_iterator end_members() const + { + return evaluate().object_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()") + array_iterator begin_elements() + { + return evaluate().array_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()") + const_array_iterator begin_elements() const + { + return evaluate().array_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()") + array_iterator end_elements() + { + return evaluate().array_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()") + const_array_iterator end_elements() const + { + return evaluate().array_range().end(); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use get_with_default(const string_view_type&, T&&)") + basic_json get(const string_view_type& name, T&& default_value) const + { + return evaluate().get_with_default(name,std::forward(default_value)); + } + + JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)") + const basic_json& get(const string_view_type& name) const + { + return evaluate().at_or_null(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)") + bool has_member(const string_view_type& name) const noexcept + { + return contains(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use erase(const_object_iterator, const_object_iterator)") + void remove_range(std::size_t from_index, std::size_t to_index) + { + evaluate().remove_range(from_index, to_index); + } + JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)") + void remove(const string_view_type& name) + { + evaluate().remove(name); + } + JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)") + void remove_member(const string_view_type& name) + { + evaluate().remove(name); + } + JSONCONS_DEPRECATED_MSG("Instead, use empty()") + bool is_empty() const noexcept + { + return empty(); + } + JSONCONS_DEPRECATED_MSG("Instead, use is_number()") + bool is_numeric() const noexcept + { + if (!parent_.contains(key_)) + { + return false; + } + return is_number(); + } + #endif + }; + + using proxy_type = proxy; + + union + { + common_storage common_stor_; + null_storage null_stor_; + bool_storage bool_stor_; + int64_storage int64_stor_; + uint64_storage uint64_stor_; + half_storage half_stor_; + double_storage double_stor_; + short_string_storage short_string_stor_; + long_string_storage long_string_stor_; + byte_string_storage byte_string_stor_; + array_storage array_stor_; + object_storage object_stor_; + empty_object_storage empty_object_stor_; + json_const_pointer_storage json_const_pointer_stor_; + }; + + void Destroy_() + { + switch (storage_kind()) + { + case json_storage_kind::long_string_value: + destroy_var(); + break; + case json_storage_kind::byte_string_value: + destroy_var(); + break; + case json_storage_kind::array_value: + destroy_var(); + break; + case json_storage_kind::object_value: + destroy_var(); + break; + default: + break; + } + } + + template + void construct(Args&&... args) + { + ::new (&cast()) VariantType(std::forward(args)...); + } + + template + void destroy_var() + { + cast().~T(); + } + + template + struct identity { using type = T*; }; + + template + T& cast() + { + return cast(identity()); + } + + template + const T& cast() const + { + return cast(identity()); + } + + null_storage& cast(identity) + { + return null_stor_; + } + + const null_storage& cast(identity) const + { + return null_stor_; + } + + empty_object_storage& cast(identity) + { + return empty_object_stor_; + } + + const empty_object_storage& cast(identity) const + { + return empty_object_stor_; + } + + bool_storage& cast(identity) + { + return bool_stor_; + } + + const bool_storage& cast(identity) const + { + return bool_stor_; + } + + int64_storage& cast(identity) + { + return int64_stor_; + } + + const int64_storage& cast(identity) const + { + return int64_stor_; + } + + uint64_storage& cast(identity) + { + return uint64_stor_; + } + + const uint64_storage& cast(identity) const + { + return uint64_stor_; + } + + half_storage& cast(identity) + { + return half_stor_; + } + + const half_storage& cast(identity) const + { + return half_stor_; + } + + double_storage& cast(identity) + { + return double_stor_; + } + + const double_storage& cast(identity) const + { + return double_stor_; + } + + short_string_storage& cast(identity) + { + return short_string_stor_; + } + + const short_string_storage& cast(identity) const + { + return short_string_stor_; + } + + long_string_storage& cast(identity) + { + return long_string_stor_; + } + + const long_string_storage& cast(identity) const + { + return long_string_stor_; + } + + byte_string_storage& cast(identity) + { + return byte_string_stor_; + } + + const byte_string_storage& cast(identity) const + { + return byte_string_stor_; + } + + object_storage& cast(identity) + { + return object_stor_; + } + + const object_storage& cast(identity) const + { + return object_stor_; + } + + array_storage& cast(identity) + { + return array_stor_; + } + + const array_storage& cast(identity) const + { + return array_stor_; + } + + json_const_pointer_storage& cast(identity) + { + return json_const_pointer_stor_; + } + + const json_const_pointer_storage& cast(identity) const + { + return json_const_pointer_stor_; + } + + template + void swap_a_b(basic_json& other) + { + TypeA& curA = cast(); + TypeB& curB = other.cast(); + TypeB tmpB(std::move(curB)); + other.construct(std::move(curA)); + construct(std::move(tmpB)); + } + + template + void swap_a(basic_json& other) + { + switch (other.storage_kind()) + { + case json_storage_kind::null_value : swap_a_b(other); break; + case json_storage_kind::empty_object_value : swap_a_b(other); break; + case json_storage_kind::bool_value : swap_a_b(other); break; + case json_storage_kind::int64_value : swap_a_b(other); break; + case json_storage_kind::uint64_value : swap_a_b(other); break; + case json_storage_kind::half_value : swap_a_b(other); break; + case json_storage_kind::double_value : swap_a_b(other); break; + case json_storage_kind::short_string_value : swap_a_b(other); break; + case json_storage_kind::long_string_value : swap_a_b(other); break; + case json_storage_kind::byte_string_value : swap_a_b(other); break; + case json_storage_kind::array_value : swap_a_b(other); break; + case json_storage_kind::object_value : swap_a_b(other); break; + case json_storage_kind::json_const_pointer : swap_a_b(other); break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + void Init_(const basic_json& val) + { + switch (val.storage_kind()) + { + case json_storage_kind::null_value: + construct(val.cast()); + break; + case json_storage_kind::empty_object_value: + construct(val.cast()); + break; + case json_storage_kind::bool_value: + construct(val.cast()); + break; + case json_storage_kind::int64_value: + construct(val.cast()); + break; + case json_storage_kind::uint64_value: + construct(val.cast()); + break; + case json_storage_kind::half_value: + construct(val.cast()); + break; + case json_storage_kind::double_value: + construct(val.cast()); + break; + case json_storage_kind::short_string_value: + construct(val.cast()); + break; + case json_storage_kind::long_string_value: + construct(val.cast()); + break; + case json_storage_kind::byte_string_value: + construct(val.cast()); + break; + case json_storage_kind::object_value: + construct(val.cast()); + break; + case json_storage_kind::array_value: + construct(val.cast()); + break; + case json_storage_kind::json_const_pointer: + construct(val.cast()); + break; + default: + break; + } + } + + void Init_(const basic_json& val, const Allocator& a) + { + switch (val.storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + case json_storage_kind::bool_value: + case json_storage_kind::int64_value: + case json_storage_kind::uint64_value: + case json_storage_kind::half_value: + case json_storage_kind::double_value: + case json_storage_kind::short_string_value: + case json_storage_kind::json_const_pointer: + Init_(val); + break; + case json_storage_kind::long_string_value: + construct(val.cast(),a); + break; + case json_storage_kind::byte_string_value: + construct(val.cast(),a); + break; + case json_storage_kind::array_value: + construct(val.cast(),a); + break; + case json_storage_kind::object_value: + construct(val.cast(),a); + break; + default: + break; + } + } + + void Init_rv_(basic_json&& val) noexcept + { + switch (val.storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + case json_storage_kind::half_value: + case json_storage_kind::double_value: + case json_storage_kind::int64_value: + case json_storage_kind::uint64_value: + case json_storage_kind::bool_value: + case json_storage_kind::short_string_value: + case json_storage_kind::json_const_pointer: + Init_(val); + break; + case json_storage_kind::long_string_value: + case json_storage_kind::byte_string_value: + case json_storage_kind::array_value: + case json_storage_kind::object_value: + { + construct(); + swap(val); + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + void Init_rv_(basic_json&& val, const Allocator&, std::true_type) noexcept + { + Init_rv_(std::forward(val)); + } + + void Init_rv_(basic_json&& val, const Allocator& a, std::false_type) noexcept + { + switch (val.storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + case json_storage_kind::half_value: + case json_storage_kind::double_value: + case json_storage_kind::int64_value: + case json_storage_kind::uint64_value: + case json_storage_kind::bool_value: + case json_storage_kind::short_string_value: + case json_storage_kind::json_const_pointer: + Init_(std::forward(val)); + break; + case json_storage_kind::long_string_value: + { + if (a == val.cast().get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + break; + } + case json_storage_kind::byte_string_value: + { + if (a == val.cast().get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + break; + } + case json_storage_kind::object_value: + { + if (a == val.cast().get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + break; + } + case json_storage_kind::array_value: + { + if (a == val.cast().get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + break; + } + default: + break; + } + } + + basic_json& evaluate_with_default() + { + return *this; + } + + basic_json& evaluate(const string_view_type& name) + { + return at(name); + } + + const basic_json& evaluate(const string_view_type& name) const + { + return at(name); + } + + public: + + basic_json& evaluate() + { + return *this; + } + + const basic_json& evaluate() const + { + return *this; + } + + basic_json& operator=(const basic_json& val) + { + if (this != &val) + { + Destroy_(); + Init_(val); + } + return *this; + } + + basic_json& operator=(basic_json&& val) noexcept + { + if (this !=&val) + { + swap(val); + } + return *this; + } + + json_storage_kind storage_kind() const + { + // It is legal to access 'common_stor_.storage_kind_' even though + // common_stor_ is not the active member of the union because 'storage_kind_' + // is a part of the common initial sequence of all union members + // as defined in 11.4-25 of the Standard. + return static_cast(common_stor_.storage_kind_); + } + + json_type type() const + { + switch(storage_kind()) + { + case json_storage_kind::null_value: + return json_type::null_value; + case json_storage_kind::bool_value: + return json_type::bool_value; + case json_storage_kind::int64_value: + return json_type::int64_value; + case json_storage_kind::uint64_value: + return json_type::uint64_value; + case json_storage_kind::half_value: + return json_type::half_value; + case json_storage_kind::double_value: + return json_type::double_value; + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + return json_type::string_value; + case json_storage_kind::byte_string_value: + return json_type::byte_string_value; + case json_storage_kind::array_value: + return json_type::array_value; + case json_storage_kind::empty_object_value: + case json_storage_kind::object_value: + return json_type::object_value; + case json_storage_kind::json_const_pointer: + return cast().value()->type(); + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + semantic_tag tag() const + { + // It is legal to access 'common_stor_.tag_' even though + // common_stor_ is not the active member of the union because 'tag_' + // is a part of the common initial sequence of all union members + // as defined in 11.4-25 of the Standard. + switch(storage_kind()) + { + case json_storage_kind::json_const_pointer: + return cast().value()->tag(); + default: + return common_stor_.tag_; + } + } + + std::size_t size() const + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return cast().value().size(); + case json_storage_kind::empty_object_value: + return 0; + case json_storage_kind::object_value: + return cast().value().size(); + case json_storage_kind::json_const_pointer: + return cast().value()->size(); + default: + return 0; + } + } + + string_view_type as_string_view() const + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + return string_view_type(cast().data(),cast().length()); + case json_storage_kind::long_string_value: + return string_view_type(cast().data(),cast().length()); + case json_storage_kind::json_const_pointer: + return cast().value()->as_string_view(); + default: + JSONCONS_THROW(json_runtime_error("Not a string")); + } + } + + template > + basic_byte_string as_byte_string() const + { + using byte_string_type = basic_byte_string; + converter convert; + std::error_code ec; + + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + byte_string_type v = convert.from(as_string_view(),tag(),ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return v; + } + case json_storage_kind::byte_string_value: + return basic_byte_string(cast().data(),cast().length()); + case json_storage_kind::json_const_pointer: + return cast().value()->as_byte_string(); + default: + JSONCONS_THROW(json_runtime_error("Not a byte string")); + } + } + + byte_string_view as_byte_string_view() const + { + switch (storage_kind()) + { + case json_storage_kind::byte_string_value: + return byte_string_view(cast().data(),cast().length()); + case json_storage_kind::json_const_pointer: + return cast().value()->as_byte_string_view(); + default: + JSONCONS_THROW(json_runtime_error("Not a byte string")); + } + } + + int compare(const basic_json& rhs) const noexcept + { + if (this == &rhs) + { + return 0; + } + switch (storage_kind()) + { + case json_storage_kind::json_const_pointer: + switch (rhs.storage_kind()) + { + case json_storage_kind::json_const_pointer: + return (cast().value())->compare(*(rhs.cast().value())); + default: + return (cast().value())->compare(rhs); + } + break; + case json_storage_kind::null_value: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + case json_storage_kind::empty_object_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::empty_object_value: + return 0; + case json_storage_kind::object_value: + return rhs.empty() ? 0 : -1; + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::bool_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::bool_value: + return static_cast(cast().value()) - static_cast(rhs.cast().value()); + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::int64_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::int64_value: + { + if (cast().value() == rhs.cast().value()) + return 0; + return cast().value() < rhs.cast().value() ? -1 : 1; + } + case json_storage_kind::uint64_value: + if (cast().value() < 0) + return -1; + else if (static_cast(cast().value()) == rhs.cast().value()) + return 0; + else + return static_cast(cast().value()) < rhs.cast().value() ? -1 : 1; + case json_storage_kind::double_value: + { + double r = static_cast(cast().value()) - rhs.cast().value(); + return r == 0.0 ? 0 : (r < 0.0 ? -1 : 1); + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::uint64_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::int64_value: + if (rhs.cast().value() < 0) + return 1; + else if (cast().value() == static_cast(rhs.cast().value())) + return 0; + else + return cast().value() < static_cast(rhs.cast().value()) ? -1 : 1; + case json_storage_kind::uint64_value: + if (cast().value() == static_cast(rhs.cast().value())) + return 0; + else + return cast().value() < static_cast(rhs.cast().value()) ? -1 : 1; + case json_storage_kind::double_value: + { + auto r = static_cast(cast().value()) - rhs.cast().value(); + return r == 0 ? 0 : (r < 0.0 ? -1 : 1); + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::double_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::int64_value: + { + auto r = cast().value() - static_cast(rhs.cast().value()); + return r == 0 ? 0 : (r < 0.0 ? -1 : 1); + } + case json_storage_kind::uint64_value: + { + auto r = cast().value() - static_cast(rhs.cast().value()); + return r == 0 ? 0 : (r < 0.0 ? -1 : 1); + } + case json_storage_kind::double_value: + { + auto r = cast().value() - rhs.cast().value(); + return r == 0 ? 0 : (r < 0.0 ? -1 : 1); + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::short_string_value: + return as_string_view().compare(rhs.as_string_view()); + case json_storage_kind::long_string_value: + return as_string_view().compare(rhs.as_string_view()); + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::byte_string_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::byte_string_value: + { + return as_byte_string_view().compare(rhs.as_byte_string_view()); + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::array_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::array_value: + { + if (cast().value() == rhs.cast().value()) + return 0; + else + return cast().value() < rhs.cast().value() ? -1 : 1; + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + case json_storage_kind::object_value: + switch (rhs.storage_kind()) + { + case json_storage_kind::empty_object_value: + return empty() ? 0 : 1; + case json_storage_kind::object_value: + { + if (cast().value() == rhs.cast().value()) + return 0; + else + return cast().value() < rhs.cast().value() ? -1 : 1; + } + case json_storage_kind::json_const_pointer: + return compare(*(rhs.cast().value())); + default: + return static_cast(storage_kind()) - static_cast((int)rhs.storage_kind()); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + void swap(basic_json& other) noexcept + { + if (this == &other) + { + return; + } + + switch (storage_kind()) + { + case json_storage_kind::null_value: swap_a(other); break; + case json_storage_kind::empty_object_value : swap_a(other); break; + case json_storage_kind::bool_value: swap_a(other); break; + case json_storage_kind::int64_value: swap_a(other); break; + case json_storage_kind::uint64_value: swap_a(other); break; + case json_storage_kind::half_value: swap_a(other); break; + case json_storage_kind::double_value: swap_a(other); break; + case json_storage_kind::short_string_value: swap_a(other); break; + case json_storage_kind::long_string_value: swap_a(other); break; + case json_storage_kind::byte_string_value: swap_a(other); break; + case json_storage_kind::array_value: swap_a(other); break; + case json_storage_kind::object_value: swap_a(other); break; + case json_storage_kind::json_const_pointer: swap_a(other); break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + // from string + + template + static + typename std::enable_if::value,basic_json>::type + parse(const Source& s, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing()) + { + json_decoder decoder; + basic_json_parser parser(options,err_handler); + + auto r = unicode_traits::detect_encoding_from_bom(s.data(), s.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser.line(),parser.column())); + } + std::size_t offset = (r.ptr - s.data()); + parser.update(s.data()+offset,s.size()-offset); + parser.parse_some(decoder); + parser.finish_parse(decoder); + parser.check_done(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json string")); + } + return decoder.get_result(); + } + + template + static + typename std::enable_if::value,basic_json>::type + parse(const Source& s, + std::function err_handler) + { + return parse(s, basic_json_decode_options(), err_handler); + } + + static basic_json parse(const char_type* s, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing()) + { + return parse(jsoncons::basic_string_view(s), options, err_handler); + } + + static basic_json parse(const char_type* s, + std::function err_handler) + { + return parse(jsoncons::basic_string_view(s), basic_json_decode_options(), err_handler); + } + + // from stream + + static basic_json parse(std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing()) + { + json_decoder visitor; + basic_json_reader> reader(is, visitor, options, err_handler); + reader.read_next(); + reader.check_done(); + if (!visitor.is_valid()) + { + JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json stream")); + } + return visitor.get_result(); + } + + static basic_json parse(std::basic_istream& is, std::function err_handler) + { + return parse(is, basic_json_decode_options(), err_handler); + } + + // from iterator + + template + static basic_json parse(InputIt first, InputIt last, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing()) + { + json_decoder visitor; + basic_json_reader> reader(iterator_source(std::forward(first),std::forward(last)), visitor, options, err_handler); + reader.read_next(); + reader.check_done(); + if (!visitor.is_valid()) + { + JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json from iterator pair")); + } + return visitor.get_result(); + } + + template + static basic_json parse(InputIt first, InputIt last, + std::function err_handler) + { + return parse(first, last, basic_json_decode_options(), err_handler); + } + + static basic_json make_array() + { + return basic_json(array()); + } + + static basic_json make_array(const array& a) + { + return basic_json(a); + } + + static basic_json make_array(const array& a, allocator_type alloc) + { + return basic_json(a, semantic_tag::none, alloc); + } + + static basic_json make_array(std::initializer_list init, const Allocator& alloc = Allocator()) + { + return array(std::move(init),alloc); + } + + static basic_json make_array(std::size_t n, const Allocator& alloc = Allocator()) + { + return array(n,alloc); + } + + template + static basic_json make_array(std::size_t n, const T& val, const Allocator& alloc = Allocator()) + { + return basic_json::array(n, val,alloc); + } + + template + static typename std::enable_if::type make_array(std::size_t n) + { + return array(n); + } + + template + static typename std::enable_if::type make_array(std::size_t n, const T& val, const Allocator& alloc = Allocator()) + { + return array(n,val,alloc); + } + + template + static typename std::enable_if<(dim>1),basic_json>::type make_array(std::size_t n, Args... args) + { + const size_t dim1 = dim - 1; + + basic_json val = make_array(std::forward(args)...); + val.resize(n); + for (std::size_t i = 0; i < n; ++i) + { + val[i] = make_array(std::forward(args)...); + } + return val; + } + + static const basic_json& null() + { + static const basic_json a_null = basic_json(null_type(), semantic_tag::none); + return a_null; + } + + basic_json() + { + construct(semantic_tag::none); + } + + basic_json(semantic_tag tag) + { + construct(tag); + } + + #if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use basic_json(json_object_t,semantic_tag,const Allocator&)") + explicit basic_json(const Allocator& alloc, semantic_tag tag = semantic_tag::none) + { + construct(object(alloc), tag); + } + + #endif + + basic_json(const basic_json& other) + { + Init_(other); + } + + basic_json(const basic_json& other, const Allocator& alloc) + { + Init_(other,alloc); + } + + basic_json(basic_json&& other) noexcept + { + Init_rv_(std::forward(other)); + } + + basic_json(basic_json&& other, const Allocator&) noexcept + { + Init_rv_(std::forward(other)); + } + + explicit basic_json(json_object_arg_t, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(object(alloc), tag); + } + + template + basic_json(json_object_arg_t, + InputIt first, InputIt last, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(object(first,last,alloc), tag); + } + + basic_json(json_object_arg_t, + std::initializer_list,basic_json>> init, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(object(init,alloc), tag); + } + + explicit basic_json(json_array_arg_t, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(array(alloc), tag); + } + + template + basic_json(json_array_arg_t, + InputIt first, InputIt last, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(array(first,last,alloc), tag); + } + + basic_json(json_array_arg_t, + std::initializer_list init, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator()) + { + construct(array(init,alloc), tag); + } + + basic_json(json_const_pointer_arg_t, const basic_json* p) noexcept + { + if (p == nullptr) + { + construct(semantic_tag::none); + } + else + { + construct(p); + } + } + + basic_json(const array& val, semantic_tag tag = semantic_tag::none) + { + construct(val, tag); + } + + basic_json(array&& val, semantic_tag tag = semantic_tag::none) + { + construct(std::forward(val), tag); + } + + basic_json(const object& val, semantic_tag tag = semantic_tag::none) + { + construct(val, tag); + } + + basic_json(object&& val, semantic_tag tag = semantic_tag::none) + { + construct(std::forward(val), tag); + } + + template ::value && !type_traits::is_basic_json::value>::type> + basic_json(const T& val) + : basic_json(json_type_traits::to_json(val)) + { + } + + template ::value && !type_traits::is_basic_json::value>::type> + basic_json(const T& val, const Allocator& alloc) + : basic_json(json_type_traits::to_json(val,alloc)) + { + } + + basic_json(const char_type* s, semantic_tag tag = semantic_tag::none) + : basic_json(s, char_traits_type::length(s), tag) + { + } + + basic_json(const char_type* s, const Allocator& alloc) + : basic_json(s, char_traits_type::length(s), semantic_tag::none, alloc) + { + } + + basic_json(const char_type* s, std::size_t length, semantic_tag tag = semantic_tag::none) + { + if (length <= short_string_storage::max_length) + { + construct(tag, s, static_cast(length)); + } + else + { + construct(tag, s, length, char_allocator_type()); + } + } + + basic_json(const char_type* s, std::size_t length, semantic_tag tag, const Allocator& alloc) + { + if (length <= short_string_storage::max_length) + { + construct(tag, s, static_cast(length)); + } + else + { + construct(tag, s, length, alloc); + } + } + + basic_json(half_arg_t, uint16_t val, semantic_tag tag = semantic_tag::none) + { + construct(val, tag); + } + + basic_json(double val, semantic_tag tag) + { + construct(val, tag); + } + + template + basic_json(IntegerType val, semantic_tag tag, + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(uint64_t), int>::type = 0) + { + construct(val, tag); + } + + template + basic_json(IntegerType val, semantic_tag tag, Allocator, + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(uint64_t), int>::type = 0) + { + construct(val, tag); + } + + template + basic_json(IntegerType val, semantic_tag, Allocator alloc = Allocator(), + typename std::enable_if::value && sizeof(uint64_t) < sizeof(IntegerType), int>::type = 0) + { + std::basic_string s; + jsoncons::detail::from_integer(val, s); + if (s.length() <= short_string_storage::max_length) + { + construct(semantic_tag::bigint, s.data(), static_cast(s.length())); + } + else + { + construct(semantic_tag::bigint, s.data(), s.length(), alloc); + } + } + + template + basic_json(IntegerType val, semantic_tag tag, + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(int64_t),int>::type = 0) + { + construct(val, tag); + } + + template + basic_json(IntegerType val, semantic_tag tag, Allocator, + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(int64_t),int>::type = 0) + { + construct(val, tag); + } + + template + basic_json(IntegerType val, semantic_tag, Allocator alloc = Allocator(), + typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),int>::type = 0) + { + std::basic_string s; + jsoncons::detail::from_integer(val, s); + if (s.length() <= short_string_storage::max_length) + { + construct(semantic_tag::bigint, s.data(), static_cast(s.length())); + } + else + { + construct(semantic_tag::bigint, s.data(), s.length(), alloc); + } + } + + basic_json(const string_view_type& sv, semantic_tag tag) + : basic_json(sv.data(), sv.length(), tag) + { + } + + basic_json(null_type, semantic_tag tag) + { + construct(tag); + } + + basic_json(bool val, semantic_tag tag) + { + construct(val,tag); + } + + basic_json(const string_view_type& sv, semantic_tag tag, const Allocator& alloc) + : basic_json(sv.data(), sv.length(), tag, alloc) + { + } + + template + basic_json(byte_string_arg_t, const Source& source, + semantic_tag tag = semantic_tag::none, + const Allocator& alloc = Allocator(), + typename std::enable_if::value,int>::type = 0) + { + auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); + construct(tag, bytes.data(), bytes.size(), 0, alloc); + } + + template + basic_json(byte_string_arg_t, const Source& source, + uint64_t ext_tag, + const Allocator& alloc = Allocator(), + typename std::enable_if::value,int>::type = 0) + { + auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); + construct(semantic_tag::ext, bytes.data(), bytes.size(), ext_tag, alloc); + } + + ~basic_json() noexcept + { + Destroy_(); + } + + template + basic_json& operator=(const T& val) + { + *this = json_type_traits::to_json(val); + return *this; + } + + basic_json& operator=(const char_type* s) + { + *this = basic_json(s, char_traits_type::length(s), semantic_tag::none); + return *this; + } + + basic_json& operator[](std::size_t i) + { + return at(i); + } + + const basic_json& operator[](std::size_t i) const + { + return at(i); + } + + proxy_type operator[](const string_view_type& name) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + return proxy_type(*this, name); + break; + default: + JSONCONS_THROW(not_an_object(name.data(),name.length())); + break; + } + } + + const basic_json& operator[](const string_view_type& name) const + { + return at(name); + } + + + template + typename std::enable_if::value>::type + dump(Container& s, + const basic_json_encode_options& options = basic_json_encode_options()) const + { + std::error_code ec; + dump(s, options, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value>::type + dump_pretty(Container& s, + const basic_json_encode_options& options = basic_json_encode_options()) const + { + std::error_code ec; + dump_pretty(s, options, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + void dump(std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) const + { + std::error_code ec; + dump(os, options, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + void dump_pretty(std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) const + { + std::error_code ec; + dump_pretty(os, options, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // Legacy + + template + typename std::enable_if::value>::type + dump(Container& s, + indenting line_indent) const + { + std::error_code ec; + + dump(s, line_indent, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value>::type + dump(Container& s, + const basic_json_encode_options& options, + indenting line_indent) const + { + std::error_code ec; + + dump(s, options, line_indent, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + void dump(std::basic_ostream& os, + indenting line_indent) const + { + std::error_code ec; + + dump(os, line_indent, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + void dump(std::basic_ostream& os, + const basic_json_encode_options& options, + indenting line_indent) const + { + std::error_code ec; + + dump(os, options, line_indent, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // end legacy + + void dump(basic_json_visitor& visitor) const + { + std::error_code ec; + dump(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // dump + template + typename std::enable_if::value>::type + dump(Container& s, + const basic_json_encode_options& options, + std::error_code& ec) const + { + basic_compact_json_encoder> encoder(s, options); + dump(encoder, ec); + } + + template + typename std::enable_if::value>::type + dump(Container& s, + std::error_code& ec) const + { + basic_compact_json_encoder> encoder(s); + dump(encoder, ec); + } + + void dump(std::basic_ostream& os, + const basic_json_encode_options& options, + std::error_code& ec) const + { + basic_compact_json_encoder encoder(os, options); + dump(encoder, ec); + } + + void dump(std::basic_ostream& os, + std::error_code& ec) const + { + basic_compact_json_encoder encoder(os); + dump(encoder, ec); + } + + // dump_pretty + + template + typename std::enable_if::value>::type + dump_pretty(Container& s, + const basic_json_encode_options& options, + std::error_code& ec) const + { + basic_json_encoder> encoder(s, options); + dump(encoder, ec); + } + + template + typename std::enable_if::value>::type + dump_pretty(Container& s, + std::error_code& ec) const + { + dump_pretty(s, basic_json_encode_options(), ec); + } + + void dump_pretty(std::basic_ostream& os, + const basic_json_encode_options& options, + std::error_code& ec) const + { + basic_json_encoder encoder(os, options); + dump(encoder, ec); + } + + void dump_pretty(std::basic_ostream& os, + std::error_code& ec) const + { + dump_pretty(os, basic_json_encode_options(), ec); + } + + // legacy + template + typename std::enable_if::value>::type + dump(Container& s, + const basic_json_encode_options& options, + indenting line_indent, + std::error_code& ec) const + { + if (line_indent == indenting::indent) + { + dump_pretty(s, options, ec); + } + else + { + dump(s, options, ec); + } + } + + template + typename std::enable_if::value>::type + dump(Container& s, + indenting line_indent, + std::error_code& ec) const + { + if (line_indent == indenting::indent) + { + dump_pretty(s, ec); + } + else + { + dump(s, ec); + } + } + + void dump(std::basic_ostream& os, + const basic_json_encode_options& options, + indenting line_indent, + std::error_code& ec) const + { + if (line_indent == indenting::indent) + { + dump_pretty(os, options, ec); + } + else + { + dump(os, options, ec); + } + } + + void dump(std::basic_ostream& os, + indenting line_indent, + std::error_code& ec) const + { + if (line_indent == indenting::indent) + { + dump_pretty(os, ec); + } + else + { + dump(os, ec); + } + } + // end legacy + + void dump(basic_json_visitor& visitor, + std::error_code& ec) const + { + dump_noflush(visitor, ec); + if (ec) + { + return; + } + visitor.flush(); + } + + bool is_null() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::null_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_null(); + default: + return false; + } + } + + allocator_type get_allocator() const + { + switch (storage_kind()) + { + case json_storage_kind::long_string_value: + { + return cast().get_allocator(); + } + case json_storage_kind::byte_string_value: + { + return cast().get_allocator(); + } + case json_storage_kind::array_value: + { + return cast().get_allocator(); + } + case json_storage_kind::object_value: + { + return cast().get_allocator(); + } + default: + return allocator_type(); + } + } + + uint64_t ext_tag() const + { + switch (storage_kind()) + { + case json_storage_kind::byte_string_value: + { + return cast().ext_tag(); + } + case json_storage_kind::json_const_pointer: + return cast().value()->ext_tag(); + default: + return 0; + } + } + + bool contains(const string_view_type& key) const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + return it != object_value().end(); + } + case json_storage_kind::json_const_pointer: + return cast().value()->contains(key); + default: + return false; + } + } + + std::size_t count(const string_view_type& key) const + { + switch (storage_kind()) + { + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it == object_value().end()) + { + return 0; + } + std::size_t count = 0; + while (it != object_value().end()&& it->key() == key) + { + ++count; + ++it; + } + return count; + } + case json_storage_kind::json_const_pointer: + return cast().value()->count(key); + default: + return 0; + } + } + + template + bool is(Args&&... args) const noexcept + { + return json_type_traits::is(*this,std::forward(args)...); + } + + bool is_string() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_string(); + default: + return false; + } + } + + bool is_string_view() const noexcept + { + return is_string(); + } + + bool is_byte_string() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::byte_string_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_byte_string(); + default: + return false; + } + } + + bool is_byte_string_view() const noexcept + { + return is_byte_string(); + } + + bool is_bignum() const + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + return jsoncons::detail::is_base10(as_string_view().data(), as_string_view().length()); + case json_storage_kind::int64_value: + case json_storage_kind::uint64_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_bignum(); + default: + return false; + } + } + + bool is_bool() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::bool_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_bool(); + default: + return false; + } + } + + bool is_object() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + case json_storage_kind::object_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_object(); + default: + return false; + } + } + + bool is_array() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_array(); + default: + return false; + } + } + + bool is_int64() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::int64_value: + return true; + case json_storage_kind::uint64_value: + return as_integer() <= static_cast((std::numeric_limits::max)()); + case json_storage_kind::json_const_pointer: + return cast().value()->is_int64(); + default: + return false; + } + } + + bool is_uint64() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::uint64_value: + return true; + case json_storage_kind::int64_value: + return as_integer() >= 0; + case json_storage_kind::json_const_pointer: + return cast().value()->is_uint64(); + default: + return false; + } + } + + bool is_half() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::half_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_half(); + default: + return false; + } + } + + bool is_double() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::double_value: + return true; + case json_storage_kind::json_const_pointer: + return cast().value()->is_double(); + default: + return false; + } + } + + bool is_number() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::int64_value: + case json_storage_kind::uint64_value: + case json_storage_kind::half_value: + case json_storage_kind::double_value: + return true; + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + return tag() == semantic_tag::bigint || + tag() == semantic_tag::bigdec || + tag() == semantic_tag::bigfloat; + case json_storage_kind::json_const_pointer: + return cast().value()->is_number(); + default: + return false; + } + } + + bool empty() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::byte_string_value: + return cast().length() == 0; + break; + case json_storage_kind::short_string_value: + return cast().length() == 0; + case json_storage_kind::long_string_value: + return cast().length() == 0; + case json_storage_kind::array_value: + return array_value().empty(); + case json_storage_kind::empty_object_value: + return true; + case json_storage_kind::object_value: + return object_value().empty(); + case json_storage_kind::json_const_pointer: + return cast().value()->empty(); + default: + return false; + } + } + + std::size_t capacity() const + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().capacity(); + case json_storage_kind::object_value: + return object_value().capacity(); + case json_storage_kind::json_const_pointer: + return cast().value()->capacity(); + default: + return 0; + } + } + + template + void create_object_implicitly() + { + create_object_implicitly(type_traits::is_stateless()); + } + + void create_object_implicitly(std::false_type) + { + static_assert(std::true_type::value, "Cannot create object implicitly - alloc is stateful."); + } + + void create_object_implicitly(std::true_type) + { + *this = basic_json(json_object_arg, tag()); + } + + void reserve(std::size_t n) + { + if (n > 0) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().reserve(n); + break; + case json_storage_kind::empty_object_value: + { + create_object_implicitly(); + object_value().reserve(n); + } + break; + case json_storage_kind::object_value: + { + object_value().reserve(n); + } + break; + default: + break; + } + } + } + + void resize(std::size_t n) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().resize(n); + break; + default: + break; + } + } + + template + void resize(std::size_t n, T val) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().resize(n, val); + break; + default: + break; + } + } + + template + typename std::enable_if::value,T>::type + as() const + { + T val = json_type_traits::as(*this); + return val; + } + + template + typename std::enable_if<(!type_traits::is_basic_string::value && + type_traits::is_back_insertable_byte_container::value) || + type_traits::is_basic_byte_string::value,T>::type + as(byte_string_arg_t, semantic_tag hint) const + { + converter convert; + std::error_code ec; + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + switch (tag()) + { + case semantic_tag::base16: + case semantic_tag::base64: + case semantic_tag::base64url: + { + T v = convert.from(as_string_view(),tag(),ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return v; + } + default: + { + T v = convert.from(as_string_view(), hint, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return T(v.begin(),v.end()); + } + break; + } + break; + } + case json_storage_kind::byte_string_value: + return T(as_byte_string_view().begin(), as_byte_string_view().end()); + case json_storage_kind::json_const_pointer: + return cast().value()->template as(byte_string_arg, hint); + default: + JSONCONS_THROW(json_runtime_error("Not a byte string")); + } + } + + bool as_bool() const + { + switch (storage_kind()) + { + case json_storage_kind::bool_value: + return cast().value(); + case json_storage_kind::int64_value: + return cast().value() != 0; + case json_storage_kind::uint64_value: + return cast().value() != 0; + case json_storage_kind::json_const_pointer: + return cast().value()->as_bool(); + default: + JSONCONS_THROW(json_runtime_error("Not a bool")); + } + } + + template + IntegerType as_integer() const + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + IntegerType val; + auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + if (!result) + { + JSONCONS_THROW(json_runtime_error(result.error_code().message())); + } + return val; + } + case json_storage_kind::half_value: + return static_cast(cast().value()); + case json_storage_kind::double_value: + return static_cast(cast().value()); + case json_storage_kind::int64_value: + return static_cast(cast().value()); + case json_storage_kind::uint64_value: + return static_cast(cast().value()); + case json_storage_kind::bool_value: + return static_cast(cast().value() ? 1 : 0); + case json_storage_kind::json_const_pointer: + return cast().value()->template as_integer(); + default: + JSONCONS_THROW(json_runtime_error("Not an integer")); + } + } + + template + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(int64_t),bool>::type + is_integer() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::int64_value: + return (as_integer() >= (type_traits::integer_limits::lowest)()) && (as_integer() <= (type_traits::integer_limits::max)()); + case json_storage_kind::uint64_value: + return as_integer() <= static_cast((type_traits::integer_limits::max)()); + case json_storage_kind::json_const_pointer: + return cast().value()->template is_integer(); + default: + return false; + } + } + + template + typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),bool>::type + is_integer() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + IntegerType val; + auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + return result ? true : false; + } + case json_storage_kind::int64_value: + return (as_integer() >= (type_traits::integer_limits::lowest)()) && (as_integer() <= (type_traits::integer_limits::max)()); + case json_storage_kind::uint64_value: + return as_integer() <= static_cast((type_traits::integer_limits::max)()); + case json_storage_kind::json_const_pointer: + return cast().value()->template is_integer(); + default: + return false; + } + } + + template + typename std::enable_if::value && sizeof(IntegerType) <= sizeof(int64_t),bool>::type + is_integer() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::int64_value: + return as_integer() >= 0 && static_cast(as_integer()) <= (type_traits::integer_limits::max)(); + case json_storage_kind::uint64_value: + return as_integer() <= (type_traits::integer_limits::max)(); + case json_storage_kind::json_const_pointer: + return cast().value()->template is_integer(); + default: + return false; + } + } + + template + typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),bool>::type + is_integer() const noexcept + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + IntegerType val; + auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + return result ? true : false; + } + case json_storage_kind::int64_value: + return as_integer() >= 0 && static_cast(as_integer()) <= (type_traits::integer_limits::max)(); + case json_storage_kind::uint64_value: + return as_integer() <= (type_traits::integer_limits::max)(); + case json_storage_kind::json_const_pointer: + return cast().value()->template is_integer(); + default: + return false; + } + } + + double as_double() const + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + jsoncons::detail::chars_to to_double; + // to_double() throws std::invalid_argument if conversion fails + return to_double(as_cstring(), as_string_view().length()); + } + case json_storage_kind::half_value: + return binary::decode_half(cast().value()); + case json_storage_kind::double_value: + return cast().value(); + case json_storage_kind::int64_value: + return static_cast(cast().value()); + case json_storage_kind::uint64_value: + return static_cast(cast().value()); + case json_storage_kind::json_const_pointer: + return cast().value()->as_double(); + default: + JSONCONS_THROW(json_runtime_error("Not a double")); + } + } + + template > + std::basic_string as_string() const + { + return as_string(SAllocator()); + } + + template > + std::basic_string as_string(const SAllocator& alloc) const + { + using string_type = std::basic_string; + + converter convert; + std::error_code ec; + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + { + return string_type(as_string_view().data(),as_string_view().length(),alloc); + } + case json_storage_kind::byte_string_value: + { + auto s = convert.from(as_byte_string_view(), tag(), alloc, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return s; + } + case json_storage_kind::array_value: + { + string_type s(alloc); + { + basic_compact_json_encoder> encoder(s); + dump(encoder); + } + return s; + } + case json_storage_kind::json_const_pointer: + return cast().value()->as_string(alloc); + default: + { + string_type s(alloc); + basic_compact_json_encoder> encoder(s); + dump(encoder); + return s; + } + } + } + + const char_type* as_cstring() const + { + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + return cast().c_str(); + case json_storage_kind::long_string_value: + return cast().c_str(); + case json_storage_kind::json_const_pointer: + return cast().value()->as_cstring(); + default: + JSONCONS_THROW(json_runtime_error("Not a cstring")); + } + } + + basic_json& at(const string_view_type& name) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + JSONCONS_THROW(key_not_found(name.data(),name.length())); + case json_storage_kind::object_value: + { + auto it = object_value().find(name); + if (it == object_value().end()) + { + JSONCONS_THROW(key_not_found(name.data(),name.length())); + } + return it->value(); + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + const basic_json& at(const string_view_type& key) const + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + JSONCONS_THROW(key_not_found(key.data(),key.length())); + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it == object_value().end()) + { + JSONCONS_THROW(key_not_found(key.data(),key.length())); + } + return it->value(); + } + case json_storage_kind::json_const_pointer: + return cast().value()->at(key); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + basic_json& at(std::size_t i) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + if (i >= array_value().size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return array_value().operator[](i); + case json_storage_kind::object_value: + return object_value().at(i); + default: + JSONCONS_THROW(json_runtime_error("Index on non-array value not supported")); + } + } + + const basic_json& at(std::size_t i) const + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + if (i >= array_value().size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return array_value().operator[](i); + case json_storage_kind::object_value: + return object_value().at(i); + case json_storage_kind::json_const_pointer: + return cast().value()->at(i); + default: + JSONCONS_THROW(json_runtime_error("Index on non-array value not supported")); + } + } + + object_iterator find(const string_view_type& name) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return object_range().end(); + case json_storage_kind::object_value: + return object_iterator(object_value().find(name)); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + const_object_iterator find(const string_view_type& key) const + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return object_range().end(); + case json_storage_kind::object_value: + return const_object_iterator(object_value().find(key)); + case json_storage_kind::json_const_pointer: + return cast().value()->find(key); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + const basic_json& at_or_null(const string_view_type& key) const + { + switch (storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + { + return null(); + } + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it != object_value().end()) + { + return it->value(); + } + else + { + return null(); + } + } + case json_storage_kind::json_const_pointer: + return cast().value()->at_or_null(key); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + template + T get_value_or(const string_view_type& key, U&& default_value) const + { + static_assert(std::is_copy_constructible::value, + "get_value_or: T must be copy constructible"); + static_assert(std::is_convertible::value, + "get_value_or: U must be convertible to T"); + switch (storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + { + return static_cast(std::forward(default_value)); + } + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it != object_value().end()) + { + return it->value().template as(); + } + else + { + return static_cast(std::forward(default_value)); + } + } + case json_storage_kind::json_const_pointer: + return cast().value()->template get_value_or(key,std::forward(default_value)); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + // Modifiers + + void shrink_to_fit() + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().shrink_to_fit(); + break; + case json_storage_kind::object_value: + object_value().shrink_to_fit(); + break; + default: + break; + } + } + + void clear() + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().clear(); + break; + case json_storage_kind::object_value: + object_value().clear(); + break; + default: + break; + } + } + + object_iterator erase(const_object_iterator pos) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return object_range().end(); + case json_storage_kind::object_value: + return object_iterator(object_value().erase(pos)); + default: + JSONCONS_THROW(json_runtime_error("Not an object")); + break; + } + } + + object_iterator erase(const_object_iterator first, const_object_iterator last) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return object_range().end(); + case json_storage_kind::object_value: + return object_iterator(object_value().erase(first, last)); + default: + JSONCONS_THROW(json_runtime_error("Not an object")); + break; + } + } + + array_iterator erase(const_array_iterator pos) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().erase(pos); + default: + JSONCONS_THROW(json_runtime_error("Not an array")); + } + } + + array_iterator erase(const_array_iterator first, const_array_iterator last) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().erase(first, last); + default: + JSONCONS_THROW(json_runtime_error("Not an array")); + break; + } + } + + // Removes all elements from an array value whose index is between from_index, inclusive, and to_index, exclusive. + + void erase(const string_view_type& name) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + break; + case json_storage_kind::object_value: + object_value().erase(name); + break; + default: + JSONCONS_THROW(not_an_object(name.data(),name.length())); + break; + } + } + + template + std::pair insert_or_assign(const string_view_type& name, T&& val) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + { + auto result = object_value().insert_or_assign(name, std::forward(val)); + return std::make_pair(object_iterator(result.first), result.second); + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + std::pair try_emplace(const string_view_type& name, Args&&... args) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + { + auto result = object_value().try_emplace(name, std::forward(args)...); + return std::make_pair(object_iterator(result.first),result.second); + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + // merge + + void merge(const basic_json& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge(source.object_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); + } + } + } + + void merge(basic_json&& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge(std::move(source.object_value())); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); + } + } + } + + void merge(object_iterator hint, const basic_json& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge(hint, source.object_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); + } + } + } + + void merge(object_iterator hint, basic_json&& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge(hint, std::move(source.object_value())); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); + } + } + } + + // merge_or_update + + void merge_or_update(const basic_json& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge_or_update(source.object_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(basic_json&& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge_or_update(std::move(source.object_value())); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(object_iterator hint, const basic_json& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge_or_update(hint, source.object_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(object_iterator hint, basic_json&& source) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + object_value().merge_or_update(hint, std::move(source.object_value())); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); + } + } + } + + template + object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + return object_iterator(object_value().insert_or_assign(hint, name, std::forward(val))); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + return object_iterator(object_value().try_emplace(hint, name, std::forward(args)...)); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + array_iterator insert(const_array_iterator pos, T&& val) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().insert(pos, std::forward(val)); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); + } + } + } + + template + array_iterator insert(const_array_iterator pos, InputIt first, InputIt last) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().insert(pos, first, last); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); + } + } + } + + template + void insert(InputIt first, InputIt last) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + case json_storage_kind::object_value: + object_value().insert(first, last, get_key_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an object")); + } + } + } + + template + void insert(sorted_unique_range_tag tag, InputIt first, InputIt last) + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + case json_storage_kind::object_value: + object_value().insert(tag, first, last, get_key_value()); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an object")); + } + } + } + + template + array_iterator emplace(const_array_iterator pos, Args&&... args) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().emplace(pos, std::forward(args)...); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); + } + } + } + + template + basic_json& emplace_back(Args&&... args) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return array_value().emplace_back(std::forward(args)...); + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); + } + } + } + + friend void swap(basic_json& a, basic_json& b) noexcept + { + a.swap(b); + } + + template + void push_back(T&& val) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().push_back(std::forward(val)); + break; + default: + { + JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); + } + } + } + + template + T get_with_default(const string_view_type& key, const T& default_value) const + { + switch (storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + { + return default_value; + } + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it != object_value().end()) + { + return it->value().template as(); + } + else + { + return default_value; + } + } + case json_storage_kind::json_const_pointer: + return cast().value()->get_with_default(key, default_value); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + template> + T get_with_default(const string_view_type& key, const char_type* default_value) const + { + switch (storage_kind()) + { + case json_storage_kind::null_value: + case json_storage_kind::empty_object_value: + { + return T(default_value); + } + case json_storage_kind::object_value: + { + auto it = object_value().find(key); + if (it != object_value().end()) + { + return it->value().template as(); + } + else + { + return T(default_value); + } + } + case json_storage_kind::json_const_pointer: + return cast().value()->get_with_default(key, default_value); + default: + { + JSONCONS_THROW(not_an_object(key.data(),key.length())); + } + } + } + + std::basic_string to_string() const noexcept + { + using string_type = std::basic_string; + string_type s; + basic_compact_json_encoder> encoder(s); + dump(encoder); + return s; + } + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use basic_json(byte_string_arg_t, const Source&, semantic_tag=semantic_tag::none,const Allocator& = Allocator())") + basic_json(const byte_string_view& bytes, + semantic_tag tag, + const Allocator& alloc = Allocator()) + : basic_json(byte_string_arg, bytes, tag, alloc) + { + } + + JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)") + const basic_json& get_with_default(const string_view_type& name) const + { + return at_or_null(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&)") + static basic_json parse(const char_type* s, std::size_t length) + { + parse_error_handler_type err_handler; + return parse(s,length,err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&, parse_error_handler)") + static basic_json parse(const char_type* s, std::size_t length, std::function err_handler) + { + return parse(string_view_type(s,length),err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream&)") + static basic_json parse_file(const std::basic_string& filename) + { + parse_error_handler_type err_handler; + return parse_file(filename,err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream&, std::function)") + static basic_json parse_file(const std::basic_string& filename, + std::function err_handler) + { + std::basic_ifstream is(filename); + return parse(is,err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream&)") + static basic_json parse_stream(std::basic_istream& is) + { + return parse(is); + } + JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream&, std::function)") + static basic_json parse_stream(std::basic_istream& is, std::function err_handler) + { + return parse(is,err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&)") + static basic_json parse_string(const string_view_type& s) + { + return parse(s); + } + + JSONCONS_DEPRECATED_MSG("Instead, use parse(parse(const string_view_type&, std::function)") + static const basic_json parse_string(const string_view_type& s, std::function err_handler) + { + return parse(s,err_handler); + } + + JSONCONS_DEPRECATED_MSG("Instead, use basic_json(double)") + basic_json(double val, uint8_t) + : basic_json(val, semantic_tag::none) + { + } + + JSONCONS_DEPRECATED_MSG("Instead, use basic_json(const byte_string_view& ,semantic_tag)") + basic_json(const byte_string_view& bytes, + byte_string_chars_format encoding_hint, + semantic_tag tag = semantic_tag::none) + : basic_json(bytes, tag) + { + switch (encoding_hint) + { + case byte_string_chars_format::base16: + *this = basic_json(bytes, semantic_tag::base16); + break; + case byte_string_chars_format::base64: + *this = basic_json(bytes, semantic_tag::base64); + break; + case byte_string_chars_format::base64url: + *this = basic_json(bytes, semantic_tag::base64url); + break; + default: + break; + } + } + + template + basic_json(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) + : basic_json(json_array_arg,first,last,alloc) + { + } + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void dump_fragment(basic_json_visitor& visitor) const + { + dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void dump_body(basic_json_visitor& visitor) const + { + dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, indenting)") + void dump(std::basic_ostream& os, bool pprint) const + { + if (pprint) + { + basic_json_encoder encoder(os); + dump(encoder); + } + else + { + basic_compact_json_encoder encoder(os); + dump(encoder); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&, indenting)") + void dump(std::basic_ostream& os, const basic_json_encode_options& options, bool pprint) const + { + if (pprint) + { + basic_json_encoder encoder(os, options); + dump(encoder); + } + else + { + basic_compact_json_encoder encoder(os, options); + dump(encoder); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void write_body(basic_json_visitor& visitor) const + { + dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void write(basic_json_visitor& visitor) const + { + dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&)") + void write(std::basic_ostream& os) const + { + dump(os); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&)") + void write(std::basic_ostream& os, const basic_json_encode_options& options) const + { + dump(os,options); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&, indenting)") + void write(std::basic_ostream& os, const basic_json_encode_options& options, bool pprint) const + { + dump(os,options,pprint); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor&)") + void to_stream(basic_json_visitor& visitor) const + { + dump(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&)") + void to_stream(std::basic_ostream& os) const + { + dump(os); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&)") + void to_stream(std::basic_ostream& os, const basic_json_encode_options& options) const + { + dump(os,options); + } + + JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream&, const basic_json_encode_options&, indenting)") + void to_stream(std::basic_ostream& os, const basic_json_encode_options& options, bool pprint) const + { + dump(os,options,pprint ? indenting::indent : indenting::no_indent); + } + + JSONCONS_DEPRECATED_MSG("No replacement") + std::size_t precision() const + { + switch (storage_kind()) + { + case json_storage_kind::double_value: + return 0; + default: + JSONCONS_THROW(json_runtime_error("Not a double")); + } + } + + JSONCONS_DEPRECATED_MSG("No replacement") + std::size_t decimal_places() const + { + switch (storage_kind()) + { + case json_storage_kind::double_value: + return 0; + default: + JSONCONS_THROW(json_runtime_error("Not a double")); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::datetime") + bool is_datetime() const noexcept + { + return tag() == semantic_tag::datetime; + } + + JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::epoch_second") + bool is_epoch_time() const noexcept + { + return tag() == semantic_tag::epoch_second; + } + + JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)") + bool has_key(const string_view_type& name) const noexcept + { + return contains(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + uint64_t as_uinteger() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("No replacement") + std::size_t double_precision() const + { + switch (storage_kind()) + { + case json_storage_kind::double_value: + return 0; + default: + JSONCONS_THROW(json_runtime_error("Not a double")); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)") + void add(std::size_t index, const basic_json& value) + { + evaluate_with_default().add(index, value); + } + + JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)") + void add(std::size_t index, basic_json&& value) + { + evaluate_with_default().add(index, std::forward(value)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use push_back(T&&)") + void add(T&& val) + { + push_back(std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)") + array_iterator add(const_array_iterator pos, T&& val) + { + return insert(pos, std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)") + std::pair set(const string_view_type& name, T&& val) + { + return insert_or_assign(name, std::forward(val)); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)") + object_iterator set(object_iterator hint, const string_view_type& name, T&& val) + { + return insert_or_assign(hint, name, std::forward(val)); + } + + JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t)") + void resize_array(std::size_t n) + { + resize(n); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t, T)") + void resize_array(std::size_t n, T val) + { + resize(n,val); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()") + object_iterator begin_members() + { + return object_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()") + const_object_iterator begin_members() const + { + return object_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()") + object_iterator end_members() + { + return object_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()") + const_object_iterator end_members() const + { + return object_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()") + array_iterator begin_elements() + { + return array_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()") + const_array_iterator begin_elements() const + { + return array_range().begin(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()") + array_iterator end_elements() + { + return array_range().end(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()") + const_array_iterator end_elements() const + { + return array_range().end(); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use get_with_default(const string_view_type&, T&&)") + basic_json get(const string_view_type& name, T&& default_value) const + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + { + return basic_json(std::forward(default_value)); + } + case json_storage_kind::object_value: + { + auto it = object_value().find(name); + if (it != object_value().end()) + { + return it->value(); + } + else + { + return basic_json(std::forward(default_value)); + } + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)") + const basic_json& get(const string_view_type& name) const + { + static const basic_json a_null = null_type(); + + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return a_null; + case json_storage_kind::object_value: + { + auto it = object_value().find(name); + return it != object_value().end() ? it->value() : a_null; + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use is()") + bool is_longlong() const noexcept + { + return is(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use is()") + bool is_ulonglong() const noexcept + { + return is(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + long long as_longlong() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned long long as_ulonglong() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + int as_int() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned int as_uint() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + long as_long() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use as()") + unsigned long as_ulong() const + { + return as(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)") + bool has_member(const string_view_type& key) const noexcept + { + return contains(key); + } + + JSONCONS_DEPRECATED_MSG("Instead, use erase(const_object_iterator, const_object_iterator)") + void remove_range(std::size_t from_index, std::size_t to_index) + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + array_value().remove_range(from_index, to_index); + break; + default: + break; + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)") + void remove(const string_view_type& name) + { + erase(name); + } + + JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)") + void remove_member(const string_view_type& name) + { + erase(name); + } + // Removes a member from an object value + + JSONCONS_DEPRECATED_MSG("Instead, use empty()") + bool is_empty() const noexcept + { + return empty(); + } + JSONCONS_DEPRECATED_MSG("Instead, use is_number()") + bool is_numeric() const noexcept + { + return is_number(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range()") + range members() + { + return object_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_range()") + range members() const + { + return object_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range()") + range elements() + { + return array_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use array_range()") + range elements() const + { + return array_range(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use storage_kind()") + json_storage_kind get_stor_type() const + { + return storage_kind(); + } + #endif + + range object_range() + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return range(object_iterator(), object_iterator()); + case json_storage_kind::object_value: + return range(object_iterator(object_value().begin()), + object_iterator(object_value().end())); + default: + JSONCONS_THROW(json_runtime_error("Not an object")); + } + } + + range object_range() const + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + return range(const_object_iterator(), const_object_iterator()); + case json_storage_kind::object_value: + return range(const_object_iterator(object_value().begin()), + const_object_iterator(object_value().end())); + case json_storage_kind::json_const_pointer: + return cast().value()->object_range(); + default: + JSONCONS_THROW(json_runtime_error("Not an object")); + } + } + + range array_range() + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return range(array_value().begin(),array_value().end()); + default: + JSONCONS_THROW(json_runtime_error("Not an array")); + } + } + + range array_range() const + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return range(array_value().begin(),array_value().end()); + case json_storage_kind::json_const_pointer: + return cast().value()->array_range(); + default: + JSONCONS_THROW(json_runtime_error("Not an array")); + } + } + + array& array_value() + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return cast().value(); + default: + JSONCONS_THROW(json_runtime_error("Bad array cast")); + break; + } + } + + const array& array_value() const + { + switch (storage_kind()) + { + case json_storage_kind::array_value: + return cast().value(); + case json_storage_kind::json_const_pointer: + return cast().value()->array_value(); + default: + JSONCONS_THROW(json_runtime_error("Bad array cast")); + break; + } + } + + object& object_value() + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + create_object_implicitly(); + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + return cast().value(); + default: + JSONCONS_THROW(json_runtime_error("Bad object cast")); + break; + } + } + + const object& object_value() const + { + switch (storage_kind()) + { + case json_storage_kind::empty_object_value: + const_cast(this)->create_object_implicitly(); // HERE + JSONCONS_FALLTHROUGH; + case json_storage_kind::object_value: + return cast().value(); + case json_storage_kind::json_const_pointer: + return cast().value()->object_value(); + default: + JSONCONS_THROW(json_runtime_error("Bad object cast")); + break; + } + } + + private: + + void dump_noflush(basic_json_visitor& visitor, std::error_code& ec) const + { + const ser_context context{}; + switch (storage_kind()) + { + case json_storage_kind::short_string_value: + case json_storage_kind::long_string_value: + visitor.string_value(as_string_view(), tag(), context, ec); + break; + case json_storage_kind::byte_string_value: + if (tag() == semantic_tag::ext) + { + visitor.byte_string_value(as_byte_string_view(), ext_tag(), context, ec); + } + else + { + visitor.byte_string_value(as_byte_string_view(), tag(), context, ec); + } + break; + case json_storage_kind::half_value: + visitor.half_value(cast().value(), tag(), context, ec); + break; + case json_storage_kind::double_value: + visitor.double_value(cast().value(), + tag(), context, ec); + break; + case json_storage_kind::int64_value: + visitor.int64_value(cast().value(), tag(), context, ec); + break; + case json_storage_kind::uint64_value: + visitor.uint64_value(cast().value(), tag(), context, ec); + break; + case json_storage_kind::bool_value: + visitor.bool_value(cast().value(), tag(), context, ec); + break; + case json_storage_kind::null_value: + visitor.null_value(tag(), context, ec); + break; + case json_storage_kind::empty_object_value: + visitor.begin_object(0, tag(), context, ec); + visitor.end_object(context, ec); + break; + case json_storage_kind::object_value: + { + bool more = visitor.begin_object(size(), tag(), context, ec); + const object& o = object_value(); + for (auto it = o.begin(); more && it != o.end(); ++it) + { + visitor.key(string_view_type((it->key()).data(),it->key().length()), context, ec); + it->value().dump_noflush(visitor, ec); + } + if (more) + { + visitor.end_object(context, ec); + } + break; + } + case json_storage_kind::array_value: + { + bool more = visitor.begin_array(size(), tag(), context, ec); + const array& o = array_value(); + for (const_array_iterator it = o.begin(); more && it != o.end(); ++it) + { + it->dump_noflush(visitor, ec); + } + if (more) + { + visitor.end_array(context, ec); + } + break; + } + case json_storage_kind::json_const_pointer: + return cast().value()->dump_noflush(visitor, ec); + default: + break; + } + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_json& o) + { + o.dump(os); + return os; + } + + friend std::basic_istream& operator>>(std::basic_istream& is, basic_json& o) + { + json_decoder visitor; + basic_json_reader> reader(is, visitor); + reader.read_next(); + reader.check_done(); + if (!visitor.is_valid()) + { + JSONCONS_THROW(json_runtime_error("Failed to parse json stream")); + } + o = visitor.get_result(); + return is; + } + + friend basic_json deep_copy(const basic_json& other) + { + switch (other.storage_kind()) + { + case json_storage_kind::array_value: + { + basic_json j(json_array_arg, other.tag(), other.get_allocator()); + j.reserve(other.size()); + + for (const auto& item : other.array_range()) + { + j.push_back(deep_copy(item)); + } + return j; + } + case json_storage_kind::object_value: + { + basic_json j(json_object_arg, other.tag(), other.get_allocator()); + j.reserve(other.size()); + + for (const auto& item : other.object_range()) + { + j.try_emplace(item.key(), deep_copy(item.value())); + } + return j; + } + case json_storage_kind::json_const_pointer: + return deep_copy(*(other.cast().value())); + default: + return other; + } + } + }; + + // operator== + + template + typename std::enable_if::value,bool>::type + operator==(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator==(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) == 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator==(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) == 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator==(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) == 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator==(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) == 0; + } + + // operator!= + + template + typename std::enable_if::value,bool>::type + operator!=(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator!=(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) != 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator!=(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) != 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator!=(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) != 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator!=(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) != 0; + } + + // operator< + + template + typename std::enable_if::value,bool>::type + operator<(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator<(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) < 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator<(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) < 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator<(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) > 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator<(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) > 0; + } + + // operator<= + + template + typename std::enable_if::value,bool>::type + operator<=(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator<=(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) <= 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator<=(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) <= 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator<=(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) >= 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator<=(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) >= 0; + } + + // operator> + + template + typename std::enable_if::value,bool>::type + operator>(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator>(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) > 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator>(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) > 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator>(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) < 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator>(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) < 0; + } + + // operator>= + + template + typename std::enable_if::value,bool>::type + operator>=(const Json& lhs, const Json& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator>=(const Json& lhs, const T& rhs) + { + return lhs.compare(rhs) >= 0; + } + + template + typename std::enable_if::value && std::is_convertible::value,bool>::type + operator>=(const Json& lhs, const T& rhs) + { + return lhs.evaluate().compare(rhs) >= 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator>=(const T& lhs, const Json& rhs) + { + return rhs.compare(lhs) <= 0; + } + + template + typename std::enable_if::value && !is_proxy::value && !type_traits::is_basic_json::value && std::is_convertible::value,bool>::type + operator>=(const T& lhs, const Json& rhs) + { + return rhs.evaluate().compare(lhs) <= 0; + } + + // swap + + template + void swap(typename Json::key_value_type& a, typename Json::key_value_type& b) noexcept + { + a.swap(b); + } + + using json = basic_json>; + using wjson = basic_json>; + using ojson = basic_json>; + using wojson = basic_json>; + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use wojson") typedef basic_json> owjson; + JSONCONS_DEPRECATED_MSG("Instead, use json_decoder") typedef json_decoder json_deserializer; + JSONCONS_DEPRECATED_MSG("Instead, use json_decoder") typedef json_decoder wjson_deserializer; + JSONCONS_DEPRECATED_MSG("Instead, use json_decoder") typedef json_decoder ojson_deserializer; + JSONCONS_DEPRECATED_MSG("Instead, use json_decoder") typedef json_decoder wojson_deserializer; + #endif + + inline namespace literals { + + inline + jsoncons::json operator "" _json(const char* s, std::size_t n) + { + return jsoncons::json::parse(jsoncons::json::string_view_type(s, n)); + } + + inline + jsoncons::wjson operator "" _json(const wchar_t* s, std::size_t n) + { + return jsoncons::wjson::parse(jsoncons::wjson::string_view_type(s, n)); + } + + inline + jsoncons::ojson operator "" _ojson(const char* s, std::size_t n) + { + return jsoncons::ojson::parse(jsoncons::ojson::string_view_type(s, n)); + } + + inline + jsoncons::wojson operator "" _ojson(const wchar_t* s, std::size_t n) + { + return jsoncons::wojson::parse(jsoncons::wojson::string_view_type(s, n)); + } + + } // inline namespace literals + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/bigint.hpp b/include/jsoncons/bigint.hpp new file mode 100644 index 0000000..223b697 --- /dev/null +++ b/include/jsoncons/bigint.hpp @@ -0,0 +1,1611 @@ +// Copyright 2018 vDaniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BIGINT_HPP +#define JSONCONS_BIGINT_HPP + +#include +#include // std::vector +#include +#include +#include // assert +#include // std::numeric_limits +#include // std::max, std::min, std::reverse +#include // std::string +#include // std::memcpy +#include // std::fmod +#include // std::allocator +#include // std::initializer_list +#include // std::enable_if +#include + +namespace jsoncons { + +/* +This implementation is based on Chapter 2 and Appendix A of +Ammeraal, L. (1996) Algorithms and Data Structures in C++, +Chichester: John Wiley. + +*/ + + +namespace detail { + + template + class basic_bigint_base + { + public: + using allocator_type = Allocator; + using basic_type_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + private: + basic_type_allocator_type alloc_; + public: + using allocator_traits_type = std::allocator_traits; + using stored_allocator_type = allocator_type; + using pointer = typename allocator_traits_type::pointer; + using value_type = typename allocator_traits_type::value_type; + using size_type = typename allocator_traits_type::size_type; + using pointer_traits = std::pointer_traits; + + basic_bigint_base() + : alloc_() + { + } + explicit basic_bigint_base(const allocator_type& alloc) + : alloc_(basic_type_allocator_type(alloc)) + { + } + + basic_type_allocator_type get_allocator() const + { + return alloc_; + } + }; + +} // namespace detail + +template > +class basic_bigint : protected detail::basic_bigint_base +{ + using base_t = detail::basic_bigint_base; + +public: + + using size_type = typename base_t::size_type; + using value_type = typename base_t::value_type; + using base_t::get_allocator; + using bigint_type = basic_bigint; + + static constexpr uint64_t max_basic_type = (std::numeric_limits::max)(); + static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8; // Number of bits + static constexpr uint64_t basic_type_halfBits = basic_type_bits/2; + + static constexpr uint16_t word_length = 4; // Use multiples of word_length words + static constexpr uint64_t r_mask = (uint64_t(1) << basic_type_halfBits) - 1; + static constexpr uint64_t l_mask = max_basic_type - r_mask; + static constexpr uint64_t l_bit = max_basic_type - (max_basic_type >> 1); + +private: + + struct common_storage + { + uint8_t is_dynamic_:1; + uint8_t is_negative_:1; + size_type length_; + }; + + struct short_storage + { + uint8_t is_dynamic_:1; + uint8_t is_negative_:1; + size_type length_; + uint64_t values_[2]; + + short_storage() + : is_dynamic_(false), + is_negative_(false), + length_(0), + values_{0,0} + { + } + + template + short_storage(T n, + typename std::enable_if::value && + sizeof(T) <= sizeof(int64_t) && + std::is_signed::value>::type* = 0) + : is_dynamic_(false), + is_negative_(n < 0), + length_(n == 0 ? 0 : 1) + { + values_[0] = n < 0 ? (uint64_t(0)-static_cast(n)) : static_cast(n); + values_[1] = 0; + } + + template + short_storage(T n, + typename std::enable_if::value && + sizeof(T) <= sizeof(int64_t) && + !std::is_signed::value>::type* = 0) + : is_dynamic_(false), + is_negative_(false), + length_(n == 0 ? 0 : 1) + { + values_[0] = n; + values_[1] = 0; + } + + template + short_storage(T n, + typename std::enable_if::value && + sizeof(int64_t) < sizeof(T) && + std::is_signed::value>::type* = 0) + : is_dynamic_(false), + is_negative_(n < 0), + length_(n == 0 ? 0 : 2) + { + using unsigned_type = typename std::make_unsigned::type; + + auto u = n < 0 ? (unsigned_type(0)-static_cast(n)) : static_cast(n); + values_[0] = uint64_t(u & max_basic_type);; + u >>= basic_type_bits; + values_[1] = uint64_t(u & max_basic_type);; + } + + template + short_storage(T n, + typename std::enable_if::value && + sizeof(int64_t) < sizeof(T) && + !std::is_signed::value>::type* = 0) + : is_dynamic_(false), + is_negative_(false), + length_(n == 0 ? 0 : 2) + { + values_[0] = uint64_t(n & max_basic_type);; + n >>= basic_type_bits; + values_[1] = uint64_t(n & max_basic_type);; + } + + short_storage(const short_storage& stor) + : is_dynamic_(false), + is_negative_(stor.is_negative_), + length_(stor.length_) + { + values_[0] = stor.values_[0]; + values_[1] = stor.values_[1]; + } + + short_storage& operator=(const short_storage& stor) = delete; + }; + + struct dynamic_storage + { + using real_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using pointer = typename std::allocator_traits::pointer; + + uint8_t is_dynamic_:1; + uint8_t is_negative_:1; + size_type length_; + size_type capacity_; + pointer data_; + + dynamic_storage() + : is_dynamic_(true), + is_negative_(false), + length_(0), + capacity_(0), + data_(nullptr) + { + } + + dynamic_storage(const dynamic_storage& stor, const real_allocator_type& alloc) + : is_dynamic_(true), + is_negative_(stor.is_negative_), + length_(stor.length_), + capacity_(0), + data_(nullptr) + { + create(stor.length_, alloc); + std::memcpy(data_, stor.data_, stor.length_*sizeof(uint64_t)); + } + + dynamic_storage(dynamic_storage&& stor) noexcept + : is_dynamic_(true), + is_negative_(stor.is_negative_), + length_(stor.length_), + capacity_(stor.capacity_), + data_(stor.data_) + { + stor.length_ = 0; + stor.capacity_ = 0; + stor.data_ = nullptr; + } + + void create(size_type length, real_allocator_type alloc) + { + capacity_ = round_up(length); + data_ = std::allocator_traits::allocate(alloc, capacity_); + JSONCONS_TRY + { + std::allocator_traits::construct(alloc, type_traits::to_plain_pointer(data_)); + } + JSONCONS_CATCH(...) + { + std::allocator_traits::deallocate(alloc, data_, capacity_); + JSONCONS_RETHROW; + } + } + + void destroy(const real_allocator_type& a) noexcept + { + if (data_ != nullptr) + { + real_allocator_type alloc(a); + + std::allocator_traits::destroy(alloc, type_traits::to_plain_pointer(data_)); + std::allocator_traits::deallocate(alloc, data_,capacity_); + } + } + + void reserve(size_type n, const real_allocator_type& a) + { + real_allocator_type alloc(a); + + size_type capacity_new = round_up(n); + uint64_t* data_old = data_; + data_ = std::allocator_traits::allocate(alloc, capacity_new); + if (length_ > 0) + { + std::memcpy( data_, data_old, length_*sizeof(uint64_t)); + } + if (capacity_ > 0 && data_ != nullptr) + { + std::allocator_traits::deallocate(alloc, data_old, capacity_); + } + capacity_ = capacity_new; + } + + // Find suitable new block size + constexpr size_type round_up(size_type i) const + { + return (i/word_length + 1) * word_length; + } + }; + + union + { + common_storage common_stor_; + short_storage short_stor_; + dynamic_storage dynamic_stor_; + }; + +public: + basic_bigint() + { + ::new (&short_stor_) short_storage(); + } + + explicit basic_bigint(const Allocator& alloc) + : base_t(alloc) + { + ::new (&short_stor_) short_storage(); + } + + + basic_bigint(const basic_bigint& n) + : base_t(n.get_allocator()) + { + if (!n.is_dynamic()) + { + ::new (&short_stor_) short_storage(n.short_stor_); + } + else + { + ::new (&dynamic_stor_) dynamic_storage(n.dynamic_stor_, get_allocator()); + } + } + + basic_bigint(basic_bigint&& other) noexcept + : base_t(other.get_allocator()) + { + if (!other.is_dynamic()) + { + ::new (&short_stor_) short_storage(other.short_stor_); + } + else + { + ::new (&dynamic_stor_) dynamic_storage(std::move(other.dynamic_stor_)); + } + } + + template + basic_bigint(Integer n, + typename std::enable_if::value>::type* = 0) + { + ::new (&short_stor_) short_storage(n); + } + + ~basic_bigint() noexcept + { + destroy(); + } + + constexpr bool is_dynamic() const + { + return common_stor_.is_dynamic_; + } + + constexpr size_type length() const + { + return common_stor_.length_; + } + + constexpr size_type capacity() const + { + return is_dynamic() ? dynamic_stor_.capacity_ : 2; + } + + bool is_negative() const + { + return common_stor_.is_negative_; + } + + void is_negative(bool value) + { + common_stor_.is_negative_ = value; + } + + constexpr const uint64_t* data() const + { + return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; + } + + uint64_t* data() + { + return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; + } + + template + static basic_bigint from_string(const std::basic_string& s) + { + return from_string(s.data(), s.length()); + } + + template + static basic_bigint from_string(const CharT* s) + { + return from_string(s, std::char_traits::length(s)); + } + + template + static basic_bigint from_string(const CharT* data, size_type length) + { + bool neg; + if (*data == '-') + { + neg = true; + data++; + --length; + } + else + { + neg = false; + } + + basic_bigint v = 0; + for (size_type i = 0; i < length; i++) + { + CharT c = data[i]; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + v = (v * 10) + (uint64_t)(c - '0'); + break; + default: + JSONCONS_THROW(std::runtime_error(std::string("Invalid digit ") + "\'" + (char)c + "\'")); + } + } + + if (neg) + { + v.common_stor_.is_negative_ = true; + } + + return v; + } + + template + static basic_bigint from_string_radix(const CharT* data, size_type length, uint8_t radix) + { + if (!(radix >= 2 && radix <= 16)) + { + JSONCONS_THROW(std::runtime_error("Unsupported radix")); + } + + bool neg; + if (*data == '-') + { + neg = true; + data++; + --length; + } + else + { + neg = false; + } + + basic_bigint v = 0; + for (size_type i = 0; i < length; i++) + { + CharT c = data[i]; + uint64_t d; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + d = (uint64_t)(c - '0'); + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + d = (uint64_t)(c - ('a' - 10)); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + d = (uint64_t)(c - ('A' - 10)); + break; + default: + JSONCONS_THROW(std::runtime_error(std::string("Invalid digit in radix ") + std::to_string(radix) + ": \'" + (char)c + "\'")); + } + if (d >= radix) + { + JSONCONS_THROW(std::runtime_error(std::string("Invalid digit in radix ") + std::to_string(radix) + ": \'" + (char)c + "\'")); + } + v = (v * radix) + d; + } + + if ( neg ) + { + v.common_stor_.is_negative_ = true; + } + return v; + } + + static basic_bigint from_bytes_be(int signum, const uint8_t* str, std::size_t n) + { + static const double radix_log2 = std::log2(next_power_of_two(256)); + // Estimate how big the result will be, so we can pre-allocate it. + double bits = radix_log2 * n; + double big_digits = std::ceil(bits / 64.0); + //std::cout << "ESTIMATED: " << big_digits << "\n"; + + bigint_type v = 0; + v.reserve(static_cast(big_digits)); + + if (n > 0) + { + for (std::size_t i = 0; i < n; i++) + { + v = (v * 256) + (uint64_t)(str[i]); + } + } + //std::cout << "ACTUAL: " << v.length() << "\n"; + + if (signum < 0) + { + v.common_stor_.is_negative_ = true; + } + + return v; + } + + uint64_t* begin() { return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; } + const uint64_t* begin() const { return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; } + uint64_t* end() { return begin() + length(); } + const uint64_t* end() const { return begin() + length(); } + + void resize(size_type n) + { + size_type len_old = common_stor_.length_; + reserve(n); + common_stor_.length_ = n; + if ( common_stor_.length_ > len_old ) + { + memset( data()+len_old, 0, (common_stor_.length_ - len_old)*sizeof(uint64_t) ); + } + } + + void reserve(size_type n) + { + if (capacity() < n) + { + if (!is_dynamic()) + { + size_type size = short_stor_.length_; + size_type is_neg = short_stor_.is_negative_; + uint64_t values[2] = {short_stor_.values_[0], short_stor_.values_[1]}; + + ::new (&dynamic_stor_) dynamic_storage(); + dynamic_stor_.reserve(n, get_allocator()); + dynamic_stor_.length_ = size; + dynamic_stor_.is_negative_ = is_neg; + dynamic_stor_.data_[0] = values[0]; + dynamic_stor_.data_[1] = values[1]; + } + else + { + dynamic_stor_.reserve(n, get_allocator()); + } + } + } + + // operators + + bool operator!() const + { + return length() == 0 ? true : false; + } + + basic_bigint operator-() const + { + basic_bigint v(*this); + v.common_stor_.is_negative_ = !v.is_negative(); + return v; + } + + basic_bigint& operator=( const basic_bigint& y ) + { + if ( this != &y ) + { + resize( y.length() ); + common_stor_.is_negative_ = y.is_negative(); + if ( y.length() > 0 ) + { + std::memcpy( data(), y.data(), y.length()*sizeof(uint64_t) ); + } + } + return *this; + } + + basic_bigint& operator+=( const basic_bigint& y ) + { + if ( is_negative() != y.is_negative() ) + return *this -= -y; + uint64_t d; + uint64_t carry = 0; + + resize( (std::max)(y.length(), length()) + 1 ); + + for (size_type i = 0; i < length(); i++ ) + { + if ( i >= y.length() && carry == 0 ) + break; + d = data()[i] + carry; + carry = d < carry; + if ( i < y.length() ) + { + data()[i] = d + y.data()[i]; + if ( data()[i] < d ) + carry = 1; + } + else + data()[i] = d; + } + reduce(); + return *this; + } + + basic_bigint& operator-=( const basic_bigint& y ) + { + if ( is_negative() != y.is_negative() ) + return *this += -y; + if ( (!is_negative() && y > *this) || (is_negative() && y < *this) ) + return *this = -(y - *this); + uint64_t borrow = 0; + uint64_t d; + for (size_type i = 0; i < length(); i++ ) + { + if ( i >= y.length() && borrow == 0 ) + break; + d = data()[i] - borrow; + borrow = d > data()[i]; + if ( i < y.length()) + { + data()[i] = d - y.data()[i]; + if ( data()[i] > d ) + borrow = 1; + } + else + data()[i] = d; + } + reduce(); + return *this; + } + + basic_bigint& operator*=( int64_t y ) + { + *this *= uint64_t(y < 0 ? -y : y); + if ( y < 0 ) + common_stor_.is_negative_ = !is_negative(); + return *this; + } + + basic_bigint& operator*=( uint64_t y ) + { + size_type len0 = length(); + uint64_t hi; + uint64_t lo; + uint64_t dig = data()[0]; + uint64_t carry = 0; + + resize( length() + 1 ); + + size_type i = 0; + for (i = 0; i < len0; i++ ) + { + DDproduct( dig, y, hi, lo ); + data()[i] = lo + carry; + dig = data()[i+1]; + carry = hi + (data()[i] < lo); + } + data()[i] = carry; + reduce(); + return *this; + } + + basic_bigint& operator*=( basic_bigint y ) + { + if ( length() == 0 || y.length() == 0 ) + return *this = 0; + bool difSigns = is_negative() != y.is_negative(); + if ( length() + y.length() == 2 ) // length() = y.length() = 1 + { + uint64_t a = data()[0], b = y.data()[0]; + data()[0] = a * b; + if ( data()[0] / a != b ) + { + resize( 2 ); + DDproduct( a, b, data()[1], data()[0] ); + } + common_stor_.is_negative_ = difSigns; + return *this; + } + if ( length() == 1 ) // && y.length() > 1 + { + uint64_t digit = data()[0]; + *this = y; + *this *= digit; + } + else + { + if ( y.length() == 1 ) + *this *= y.data()[0]; + else + { + size_type lenProd = length() + y.length(), jA, jB; + uint64_t sumHi = 0, sumLo, hi, lo, + sumLo_old, sumHi_old, carry=0; + basic_bigint x = *this; + resize( lenProd ); // Give *this length lenProd + + for (size_type i = 0; i < lenProd; i++ ) + { + sumLo = sumHi; + sumHi = carry; + carry = 0; + for ( jA=0; jA < x.length(); jA++ ) + { + jB = i - jA; + if ( jB >= 0 && jB < y.length() ) + { + DDproduct( x.data()[jA], y.data()[jB], hi, lo ); + sumLo_old = sumLo; + sumHi_old = sumHi; + sumLo += lo; + if ( sumLo < sumLo_old ) + sumHi++; + sumHi += hi; + carry += (sumHi < sumHi_old); + } + } + data()[i] = sumLo; + } + } + } + reduce(); + common_stor_.is_negative_ = difSigns; + return *this; + } + + basic_bigint& operator/=( const basic_bigint& divisor ) + { + basic_bigint r; + divide( divisor, *this, r, false ); + return *this; + } + + basic_bigint& operator%=( const basic_bigint& divisor ) + { + basic_bigint q; + divide( divisor, q, *this, true ); + return *this; + } + + basic_bigint& operator<<=( uint64_t k ) + { + size_type q = (size_type)(k / basic_type_bits); + if ( q ) // Increase common_stor_.length_ by q: + { + resize(length() + q); + for (size_type i = length(); i-- > 0; ) + data()[i] = ( i < q ? 0 : data()[i - q]); + k %= basic_type_bits; + } + if ( k ) // 0 < k < basic_type_bits: + { + uint64_t k1 = basic_type_bits - k; + uint64_t mask = (uint64_t(1) << k) - uint64_t(1); + resize( length() + 1 ); + for (size_type i = length(); i-- > 0; ) + { + data()[i] <<= k; + if ( i > 0 ) + data()[i] |= (data()[i-1] >> k1) & mask; + } + } + reduce(); + return *this; + } + + basic_bigint& operator>>=(uint64_t k) + { + size_type q = (size_type)(k / basic_type_bits); + if ( q >= length() ) + { + resize( 0 ); + return *this; + } + if (q > 0) + { + memmove( data(), data()+q, (size_type)((length() - q)*sizeof(uint64_t)) ); + resize( length() - q ); + k %= basic_type_bits; + if ( k == 0 ) + { + reduce(); + return *this; + } + } + + size_type n = (size_type)(length() - 1); + int64_t k1 = basic_type_bits - k; + uint64_t mask = (uint64_t(1) << k) - 1; + for (size_type i = 0; i <= n; i++) + { + data()[i] >>= k; + if ( i < n ) + data()[i] |= ((data()[i+1] & mask) << k1); + } + reduce(); + return *this; + } + + basic_bigint& operator++() + { + *this += 1; + return *this; + } + + basic_bigint operator++(int) + { + basic_bigint old = *this; + ++(*this); + return old; + } + + basic_bigint& operator--() + { + *this -= 1; + return *this; + } + + basic_bigint operator--(int) + { + basic_bigint old = *this; + --(*this); + return old; + } + + basic_bigint& operator|=( const basic_bigint& a ) + { + if ( length() < a.length() ) + { + resize( a.length() ); + } + + const uint64_t* qBegin = a.begin(); + const uint64_t* q = a.end() - 1; + uint64_t* p = begin() + a.length() - 1; + + while ( q >= qBegin ) + { + *p-- |= *q--; + } + + reduce(); + + return *this; + } + + basic_bigint& operator^=( const basic_bigint& a ) + { + if ( length() < a.length() ) + { + resize( a.length() ); + } + + const uint64_t* qBegin = a.begin(); + const uint64_t* q = a.end() - 1; + uint64_t* p = begin() + a.length() - 1; + + while ( q >= qBegin ) + { + *p-- ^= *q--; + } + + reduce(); + + return *this; + } + + basic_bigint& operator&=( const basic_bigint& a ) + { + size_type old_length = length(); + + resize( (std::min)( length(), a.length() ) ); + + const uint64_t* pBegin = begin(); + uint64_t* p = end() - 1; + const uint64_t* q = a.begin() + length() - 1; + + while ( p >= pBegin ) + { + *p-- &= *q--; + } + + if ( old_length > length() ) + { + memset( data() + length(), 0, old_length - length() ); + } + + reduce(); + + return *this; + } + + explicit operator bool() const + { + return length() != 0 ? true : false; + } + + explicit operator int64_t() const + { + int64_t x = 0; + if ( length() > 0 ) + { + x = data() [0]; + } + + return is_negative() ? -x : x; + } + + explicit operator uint64_t() const + { + uint64_t u = 0; + if ( length() > 0 ) + { + u = data() [0]; + } + + return u; + } + + explicit operator double() const + { + double x = 0.0; + double factor = 1.0; + double values = (double)max_basic_type + 1.0; + + const uint64_t* p = begin(); + const uint64_t* pEnd = end(); + while ( p < pEnd ) + { + x += *p*factor; + factor *= values; + ++p; + } + + return is_negative() ? -x : x; + } + + explicit operator long double() const + { + long double x = 0.0; + long double factor = 1.0; + long double values = (long double)max_basic_type + 1.0; + + const uint64_t* p = begin(); + const uint64_t* pEnd = end(); + while ( p < pEnd ) + { + x += *p*factor; + factor *= values; + ++p; + } + + return is_negative() ? -x : x; + } + + template + void write_bytes_be(int& signum, std::vector& data) const + { + basic_bigint n(*this); + signum = (n < 0) ? -1 : (n > 0 ? 1 : 0); + + basic_bigint divisor(256); + + while (n >= 256) + { + basic_bigint q; + basic_bigint r; + n.divide(divisor, q, r, true); + n = q; + data.push_back((uint8_t)(uint64_t)r); + } + if (n >= 0) + { + data.push_back((uint8_t)(uint64_t)n); + } + std::reverse(data.begin(),data.end()); + } + + std::string to_string() const + { + std::string s; + write_string(s); + return s; + } + + template + void write_string(std::basic_string& data) const + { + basic_bigint v(*this); + + size_t len = (v.length() * basic_type_bits / 3) + 2; + data.reserve(len); + + static uint64_t p10 = 1; + static uint64_t ip10 = 0; + + if ( v.length() == 0 ) + { + data.push_back('0'); + } + else + { + uint64_t r; + if ( p10 == 1 ) + { + while ( p10 <= (std::numeric_limits::max)()/10 ) + { + p10 *= 10; + ip10++; + } + } + // p10 is max unsigned power of 10 + basic_bigint R; + basic_bigint LP10 = p10; // LP10 = p10 = ::pow(10, ip10) + + do + { + v.divide( LP10, v, R, true ); + r = (R.length() ? R.data()[0] : 0); + for ( size_type j=0; j < ip10; j++ ) + { + data.push_back(char(r % 10 + '0')); + r /= 10; + if ( r + v.length() == 0 ) + break; + } + } + while ( v.length() ); + if (is_negative()) + { + data.push_back('-'); + } + std::reverse(data.begin(),data.end()); + } + } + + std::string to_string_hex() const + { + std::string s; + write_string_hex(s); + return s; + } + + template + void write_string_hex(std::basic_string& data) const + { + basic_bigint v(*this); + + std::size_t len = (v.length() * basic_bigint::basic_type_bits / 3) + 2; + data.reserve(len); + // 1/3 > ln(2)/ln(10) + static uint64_t p10 = 1; + static uint64_t ip10 = 0; + + if ( v.length() == 0 ) + { + data.push_back('0'); + } + else + { + uint64_t r; + if ( p10 == 1 ) + { + while ( p10 <= (std::numeric_limits::max)()/16 ) + { + p10 *= 16; + ip10++; + } + } // p10 is max unsigned power of 16 + basic_bigint R; + basic_bigint LP10 = p10; // LP10 = p10 = ::pow(16, ip10) + do + { + v.divide( LP10, v, R, true ); + r = (R.length() ? R.data()[0] : 0); + for ( size_type j=0; j < ip10; j++ ) + { + uint8_t c = r % 16; + data.push_back((c < 10) ? ('0' + c) : ('A' - 10 + c)); + r /= 16; + if ( r + v.length() == 0 ) + break; + } + } + while (v.length()); + + if (is_negative()) + { + data.push_back('-'); + } + std::reverse(data.begin(),data.end()); + } + } + +// Global Operators + + friend bool operator==( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) == 0 ? true : false; + } + + friend bool operator==( const basic_bigint& x, int y ) noexcept + { + return x.compare(y) == 0 ? true : false; + } + + friend bool operator!=( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) != 0 ? true : false; + } + + friend bool operator!=( const basic_bigint& x, int y ) noexcept + { + return x.compare(basic_bigint(y)) != 0 ? true : false; + } + + friend bool operator<( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) < 0 ? true : false; + } + + friend bool operator<( const basic_bigint& x, int64_t y ) noexcept + { + return x.compare(y) < 0 ? true : false; + } + + friend bool operator>( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) > 0 ? true : false; + } + + friend bool operator>( const basic_bigint& x, int y ) noexcept + { + return x.compare(basic_bigint(y)) > 0 ? true : false; + } + + friend bool operator<=( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) <= 0 ? true : false; + } + + friend bool operator<=( const basic_bigint& x, int y ) noexcept + { + return x.compare(y) <= 0 ? true : false; + } + + friend bool operator>=( const basic_bigint& x, const basic_bigint& y ) noexcept + { + return x.compare(y) >= 0 ? true : false; + } + + friend bool operator>=( const basic_bigint& x, int y ) noexcept + { + return x.compare(y) >= 0 ? true : false; + } + + friend basic_bigint operator+( basic_bigint x, const basic_bigint& y ) + { + return x += y; + } + + friend basic_bigint operator+( basic_bigint x, int64_t y ) + { + return x += y; + } + + friend basic_bigint operator-( basic_bigint x, const basic_bigint& y ) + { + return x -= y; + } + + friend basic_bigint operator-( basic_bigint x, int64_t y ) + { + return x -= y; + } + + friend basic_bigint operator*( int64_t x, const basic_bigint& y ) + { + return basic_bigint(y) *= x; + } + + friend basic_bigint operator*( basic_bigint x, const basic_bigint& y ) + { + return x *= y; + } + + friend basic_bigint operator*( basic_bigint x, int64_t y ) + { + return x *= y; + } + + friend basic_bigint operator/( basic_bigint x, const basic_bigint& y ) + { + return x /= y; + } + + friend basic_bigint operator/( basic_bigint x, int y ) + { + return x /= y; + } + + friend basic_bigint operator%( basic_bigint x, const basic_bigint& y ) + { + return x %= y; + } + + friend basic_bigint operator<<( basic_bigint u, unsigned k ) + { + return u <<= k; + } + + friend basic_bigint operator<<( basic_bigint u, int k ) + { + return u <<= k; + } + + friend basic_bigint operator>>( basic_bigint u, unsigned k ) + { + return u >>= k; + } + + friend basic_bigint operator>>( basic_bigint u, int k ) + { + return u >>= k; + } + + friend basic_bigint operator|( basic_bigint x, const basic_bigint& y ) + { + return x |= y; + } + + friend basic_bigint operator|( basic_bigint x, int y ) + { + return x |= y; + } + + friend basic_bigint operator|( basic_bigint x, unsigned y ) + { + return x |= y; + } + + friend basic_bigint operator^( basic_bigint x, const basic_bigint& y ) + { + return x ^= y; + } + + friend basic_bigint operator^( basic_bigint x, int y ) + { + return x ^= y; + } + + friend basic_bigint operator^( basic_bigint x, unsigned y ) + { + return x ^= y; + } + + friend basic_bigint operator&( basic_bigint x, const basic_bigint& y ) + { + return x &= y; + } + + friend basic_bigint operator&( basic_bigint x, int y ) + { + return x &= y; + } + + friend basic_bigint operator&( basic_bigint x, unsigned y ) + { + return x &= y; + } + + friend basic_bigint abs( const basic_bigint& a ) + { + if ( a.is_negative() ) + { + return -a; + } + return a; + } + + friend basic_bigint power( basic_bigint x, unsigned n ) + { + basic_bigint y = 1; + + while ( n ) + { + if ( n & 1 ) + { + y *= x; + } + x *= x; + n >>= 1; + } + + return y; + } + + friend basic_bigint sqrt( const basic_bigint& a ) + { + basic_bigint x = a; + basic_bigint b = a; + basic_bigint q; + + b <<= 1; + while ( b >>= 2, b > 0 ) + { + x >>= 1; + } + while ( x > (q = a/x) + 1 || x < q - 1 ) + { + x += q; + x >>= 1; + } + return x < q ? x : q; + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_bigint& v) + { + std::basic_string s; + v.write_string(s); + os << s; + + return os; + } + + int compare( const basic_bigint& y ) const noexcept + { + if ( is_negative() != y.is_negative() ) + return y.is_negative() - is_negative(); + int code = 0; + if ( length() == 0 && y.length() == 0 ) + code = 0; + else if ( length() < y.length() ) + code = -1; + else if ( length() > y.length() ) + code = +1; + else + { + for (size_type i = length(); i-- > 0; ) + { + if (data()[i] > y.data()[i]) + { + code = 1; + break; + } + else if (data()[i] < y.data()[i]) + { + code = -1; + break; + } + } + } + return is_negative() ? -code : code; + } + + void divide( basic_bigint denom, basic_bigint& quot, basic_bigint& rem, bool remDesired ) const + { + if ( denom.length() == 0 ) + { + JSONCONS_THROW(std::runtime_error( "Zero divide." )); + } + bool quot_neg = is_negative() ^ denom.is_negative(); + bool rem_neg = is_negative(); + int x = 0; + basic_bigint num = *this; + num.common_stor_.is_negative_ = denom.common_stor_.is_negative_ = false; + if ( num < denom ) + { + quot = uint64_t(0); + rem = num; + rem.common_stor_.is_negative_ = rem_neg; + return; + } + if ( denom.length() == 1 && num.length() == 1 ) + { + quot = uint64_t( num.data()[0]/denom.data()[0] ); + rem = uint64_t( num.data()[0]%denom.data()[0] ); + quot.common_stor_.is_negative_ = quot_neg; + rem.common_stor_.is_negative_ = rem_neg; + return; + } + else if (denom.length() == 1 && (denom.data()[0] & l_mask) == 0 ) + { + // Denominator fits into a half word + uint64_t divisor = denom.data()[0], dHi = 0, + q1, r, q2, dividend; + quot.resize(length()); + for (size_type i=length(); i-- > 0; ) + { + dividend = (dHi << basic_type_halfBits) | (data()[i] >> basic_type_halfBits); + q1 = dividend/divisor; + r = dividend % divisor; + dividend = (r << basic_type_halfBits) | (data()[i] & r_mask); + q2 = dividend/divisor; + dHi = dividend % divisor; + quot.data()[i] = (q1 << basic_type_halfBits) | q2; + } + quot.reduce(); + rem = dHi; + quot.common_stor_.is_negative_ = quot_neg; + rem.common_stor_.is_negative_ = rem_neg; + return; + } + basic_bigint num0 = num, denom0 = denom; + int second_done = normalize(denom, num, x); + size_type l = denom.length() - 1; + size_type n = num.length() - 1; + quot.resize(n - l); + for (size_type i=quot.length(); i-- > 0; ) + quot.data()[i] = 0; + rem = num; + if ( rem.data()[n] >= denom.data()[l] ) + { + rem.resize(rem.length() + 1); + n++; + quot.resize(quot.length() + 1); + } + uint64_t d = denom.data()[l]; + for ( size_type k = n; k > l; k-- ) + { + uint64_t q = DDquotient(rem.data()[k], rem.data()[k-1], d); + subtractmul( rem.data() + k - l - 1, denom.data(), l + 1, q ); + quot.data()[k - l - 1] = q; + } + quot.reduce(); + quot.common_stor_.is_negative_ = quot_neg; + if ( remDesired ) + { + unnormalize(rem, x, second_done); + rem.common_stor_.is_negative_ = rem_neg; + } + } +private: + void destroy() noexcept + { + if (is_dynamic()) + { + dynamic_stor_.destroy(get_allocator()); + } + } + void DDproduct( uint64_t A, uint64_t B, + uint64_t& hi, uint64_t& lo ) const + // Multiplying two digits: (hi, lo) = A * B + { + uint64_t hiA = A >> basic_type_halfBits, loA = A & r_mask, + hiB = B >> basic_type_halfBits, loB = B & r_mask, + mid1, mid2, old; + + lo = loA * loB; + hi = hiA * hiB; + mid1 = loA * hiB; + mid2 = hiA * loB; + old = lo; + lo += mid1 << basic_type_halfBits; + hi += (lo < old) + (mid1 >> basic_type_halfBits); + old = lo; + lo += mid2 << basic_type_halfBits; + hi += (lo < old) + (mid2 >> basic_type_halfBits); + } + + uint64_t DDquotient( uint64_t A, uint64_t B, uint64_t d ) const + // Divide double word (A, B) by d. Quotient = (qHi, qLo) + { + uint64_t left, middle, right, qHi, qLo, x, dLo1, + dHi = d >> basic_type_halfBits, dLo = d & r_mask; + qHi = A/(dHi + 1); + // This initial guess of qHi may be too small. + middle = qHi * dLo; + left = qHi * dHi; + x = B - (middle << basic_type_halfBits); + A -= (middle >> basic_type_halfBits) + left + (x > B); + B = x; + dLo1 = dLo << basic_type_halfBits; + // Increase qHi if necessary: + while ( A > dHi || (A == dHi && B >= dLo1) ) + { + x = B - dLo1; + A -= dHi + (x > B); + B = x; + qHi++; + } + qLo = ((A << basic_type_halfBits) | (B >> basic_type_halfBits))/(dHi + 1); + // This initial guess of qLo may be too small. + right = qLo * dLo; + middle = qLo * dHi; + x = B - right; + A -= (x > B); + B = x; + x = B - (middle << basic_type_halfBits); + A -= (middle >> basic_type_halfBits) + (x > B); + B = x; + // Increase qLo if necessary: + while ( A || B >= d ) + { + x = B - d; + A -= (x > B); + B = x; + qLo++; + } + return (qHi << basic_type_halfBits) + qLo; + } + + void subtractmul( uint64_t* a, uint64_t* b, size_type n, uint64_t& q ) const + // a -= q * b: b in n positions; correct q if necessary + { + uint64_t hi, lo, d, carry = 0; + size_type i; + for ( i = 0; i < n; i++ ) + { + DDproduct( b[i], q, hi, lo ); + d = a[i]; + a[i] -= lo; + if ( a[i] > d ) + carry++; + d = a[i + 1]; + a[i + 1] -= hi + carry; + carry = a[i + 1] > d; + } + if ( carry ) // q was too large + { + q--; + carry = 0; + for ( i = 0; i < n; i++ ) + { + d = a[i] + carry; + carry = d < carry; + a[i] = d + b[i]; + if ( a[i] < d ) + carry = 1; + } + a[n] = 0; + } + } + + int normalize( basic_bigint& denom, basic_bigint& num, int& x ) const + { + size_type r = denom.length() - 1; + uint64_t y = denom.data()[r]; + + x = 0; + while ( (y & l_bit) == 0 ) + { + y <<= 1; + x++; + } + denom <<= x; + num <<= x; + if ( r > 0 && denom.data()[r] < denom.data()[r-1] ) + { + denom *= max_basic_type; + num *= max_basic_type; + return 1; + } + return 0; + } + + void unnormalize( basic_bigint& rem, int x, int secondDone ) const + { + if ( secondDone ) + { + rem /= max_basic_type; + } + if ( x > 0 ) + { + rem >>= x; + } + else + { + rem.reduce(); + } + } + + size_type round_up(size_type i) const // Find suitable new block size + { + return (i/word_length + 1) * word_length; + } + + void reduce() + { + uint64_t* p = end() - 1; + uint64_t* pBegin = begin(); + while ( p >= pBegin ) + { + if ( *p ) + { + break; + } + --common_stor_.length_; + --p; + } + if ( length() == 0 ) + { + common_stor_.is_negative_ = false; + } + } + + static uint64_t next_power_of_two(uint64_t n) { + n = n - 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + return n + 1; + } +}; + +using bigint = basic_bigint>; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use bigint") typedef bigint bignum; +#endif + +} + +#endif diff --git a/include/jsoncons/byte_string.hpp b/include/jsoncons/byte_string.hpp new file mode 100644 index 0000000..159dff9 --- /dev/null +++ b/include/jsoncons/byte_string.hpp @@ -0,0 +1,820 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BYTE_STRING_HPP +#define JSONCONS_BYTE_STRING_HPP + +#include +#include +#include +#include +#include // std::memcmp +#include // std::allocator +#include +#include +#include // std::setw +#include +#include // std::move +#include +#include +#include +#include + +namespace jsoncons { + + // Algorithms + +namespace detail { + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64_generic(InputIt first, InputIt last, const char alphabet[65], Container& result) + { + std::size_t count = 0; + unsigned char a3[3]; + unsigned char a4[4]; + unsigned char fill = alphabet[64]; + int i = 0; + int j = 0; + + while (first != last) + { + a3[i++] = *first++; + if (i == 3) + { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = a3[2] & 0x3f; + + for (i = 0; i < 4; i++) + { + result.push_back(alphabet[a4[i]]); + ++count; + } + i = 0; + } + } + + if (i > 0) + { + for (j = i; j < 3; ++j) + { + a3[j] = 0; + } + + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + + for (j = 0; j < i + 1; ++j) + { + result.push_back(alphabet[a4[j]]); + ++count; + } + + if (fill != 0) + { + while (i++ < 3) + { + result.push_back(fill); + ++count; + } + } + } + + return count; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base64_generic(InputIt first, InputIt last, + const uint8_t reverse_alphabet[256], + F f, + Container& result) + { + uint8_t a4[4], a3[3]; + uint8_t i = 0; + uint8_t j = 0; + + while (first != last && *first != '=') + { + if (!f(*first)) + { + return decode_result{first, conv_errc::conversion_failed}; + } + + a4[i++] = *first++; + if (i == 4) + { + for (i = 0; i < 4; ++i) + { + a4[i] = reverse_alphabet[a4[i]]; + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; + + for (i = 0; i < 3; i++) + { + result.push_back(a3[i]); + } + i = 0; + } + } + + if (i > 0) + { + for (j = 0; j < i; ++j) + { + a4[j] = reverse_alphabet[a4[j]]; + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + + for (j = 0; j < i - 1; ++j) + { + result.push_back(a3[j]); + } + } + return decode_result{last, conv_errc::success}; + } + +} // namespace detail + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base16(InputIt first, InputIt last, Container& result) + { + static constexpr char characters[] = "0123456789ABCDEF"; + + for (auto it = first; it != last; ++it) + { + uint8_t c = *it; + result.push_back(characters[c >> 4]); + result.push_back(characters[c & 0xf]); + } + return (last-first)*2; + } + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64url(InputIt first, InputIt last, Container& result) + { + static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_" + "\0"; + return detail::encode_base64_generic(first, last, alphabet, result); + } + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64(InputIt first, InputIt last, Container& result) + { + static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/" + "="; + return detail::encode_base64_generic(first, last, alphabet, result); + } + + template + bool is_base64(Char c) + { + return (c >= 0 && c < 128) && (isalnum((int)c) || c == '+' || c == '/'); + } + + template + bool is_base64url(Char c) + { + return (c >= 0 && c < 128) && (isalnum((int)c) || c == '-' || c == '_'); + } + + inline + static bool is_base64url(int c) + { + return isalnum(c) || c == '-' || c == '_'; + } + + // decode + + template + typename std::enable_if::value,decode_result>::type + decode_base64url(InputIt first, InputIt last, Container& result) + { + static constexpr uint8_t reverse_alphabet[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 63, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + is_base64url::value_type>, + result); + return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64url}; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base64(InputIt first, InputIt last, Container& result) + { + static constexpr uint8_t reverse_alphabet[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + is_base64::value_type>, + result); + return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64}; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base16(InputIt first, InputIt last, Container& result) + { + std::size_t len = std::distance(first,last); + if (len & 1) + { + return decode_result{first, conv_errc::not_base16}; + } + + InputIt it = first; + while (it != last) + { + uint8_t val; + auto a = *it++; + if (a >= '0' && a <= '9') + { + val = (a - '0') << 4; + } + else if ((a | 0x20) >= 'a' && (a | 0x20) <= 'f') + { + val = ((a | 0x20) - 'a' + 10) << 4; + } + else + { + return decode_result{first, conv_errc::not_base16}; + } + + auto b = *it++; + if (b >= '0' && b <= '9') + { + val |= (b - '0'); + } + else if ((b | 0x20) >= 'a' && (b | 0x20) <= 'f') + { + val |= ((b | 0x20) - 'a' + 10); + } + else + { + return decode_result{first, conv_errc::not_base16}; + } + + result.push_back(val); + } + return decode_result{last, conv_errc::success}; + } + + struct byte_traits + { + using char_type = uint8_t; + + static constexpr int eof() + { + return std::char_traits::eof(); + } + + static int compare(const char_type* s1, const char_type* s2, std::size_t count) noexcept + { + return std::memcmp(s1,s2,count); + } + }; + + // basic_byte_string + + template + class basic_byte_string; + + // byte_string_view + class byte_string_view + { + const uint8_t* data_; + std::size_t size_; + public: + using traits_type = byte_traits; + + using const_iterator = const uint8_t*; + using iterator = const_iterator; + using size_type = std::size_t; + using value_type = uint8_t; + using reference = uint8_t&; + using const_reference = const uint8_t&; + using difference_type = std::ptrdiff_t; + using pointer = uint8_t*; + using const_pointer = const uint8_t*; + + constexpr byte_string_view() noexcept + : data_(nullptr), size_(0) + { + } + + constexpr byte_string_view(const uint8_t* data, std::size_t length) noexcept + : data_(data), size_(length) + { + } + + template + constexpr explicit byte_string_view(const Container& cont, + typename std::enable_if::value,int>::type = 0) + : data_(reinterpret_cast(cont.data())), size_(cont.size()) + { + } + + template + constexpr byte_string_view(const basic_byte_string& bytes); + + constexpr byte_string_view(const byte_string_view&) noexcept = default; + + JSONCONS_CPP14_CONSTEXPR byte_string_view(byte_string_view&& other) noexcept + : data_(nullptr), size_(0) + { + const_pointer temp_data = data_; + data_ = other.data_; + other.data_ = temp_data; + + size_type temp_size = size_; + size_ = other.size_; + other.size_ = temp_size; + } + + byte_string_view& operator=(const byte_string_view&) = default; + + byte_string_view& operator=(byte_string_view&& other) noexcept + { + std::swap(data_, other.data_); + std::swap(size_, other.size_); + return *this; + } + + constexpr const uint8_t* data() const noexcept + { + return data_; + } + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use size()") + std::size_t length() const + { + return size_; + } + #endif + constexpr size_t size() const noexcept + { + return size_; + } + + // iterator support + constexpr const_iterator begin() const noexcept + { + return data_; + } + constexpr const_iterator end() const noexcept + { + return data_ + size_; + } + constexpr const_iterator cbegin() const noexcept + { + return data_; + } + constexpr const_iterator cend() const noexcept + { + return data_ + size_; + } + + constexpr uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + JSONCONS_CPP14_CONSTEXPR byte_string_view substr(size_type pos) const + { + if (pos > size_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds size")); + } + std::size_t n = size_ - pos; + return byte_string_view(data_ + pos, n); + } + + byte_string_view substr(size_type pos, size_type n) const + { + if (pos > size_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds size")); + } + if (pos + n > size_) + { + n = size_ - pos; + } + return byte_string_view(data_ + pos, n); + } + + int compare(const byte_string_view& s) const noexcept + { + const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size())); + return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1); + } + + template + int compare(const basic_byte_string& s) const noexcept + { + const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size())); + return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const byte_string_view& bstr) + { + std::basic_ostringstream ss; + ss.flags(std::ios::hex); + ss.fill('0'); + + bool first = true; + for (auto b : bstr) + { + if (first) + { + first = false; + } + else + { + ss << ','; + } + ss << std::setw(2) << static_cast(b); + } + os << ss.str(); + return os; + } + }; + + // basic_byte_string + template > + class basic_byte_string + { + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + std::vector data_; + public: + using traits_type = byte_traits; + using allocator_type = byte_allocator_type; + + using value_type = typename std::vector::value_type; + using size_type = typename std::vector::size_type; + using difference_type = typename std::vector::difference_type; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using pointer = typename std::vector::pointer; + using const_pointer = typename std::vector::const_pointer; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + + basic_byte_string() = default; + + explicit basic_byte_string(const Allocator& alloc) + : data_(alloc) + { + } + + basic_byte_string(std::initializer_list init) + : data_(std::move(init)) + { + } + + basic_byte_string(std::initializer_list init, const Allocator& alloc) + : data_(std::move(init), alloc) + { + } + + explicit basic_byte_string(const byte_string_view& v) + : data_(v.begin(),v.end()) + { + } + + basic_byte_string(const basic_byte_string& v) + : data_(v.data_) + { + } + + basic_byte_string(basic_byte_string&& v) noexcept + : data_(std::move(v.data_)) + { + } + + basic_byte_string(const byte_string_view& v, const Allocator& alloc) + : data_(v.begin(),v.end(),alloc) + { + } + + basic_byte_string(const uint8_t* data, std::size_t length, const Allocator& alloc = Allocator()) + : data_(data, data+length,alloc) + { + } + + Allocator get_allocator() const + { + return data_.get_allocator(); + } + + basic_byte_string& operator=(const basic_byte_string& s) = default; + + basic_byte_string& operator=(basic_byte_string&& other) noexcept + { + data_.swap(other.data_); + return *this; + } + + void reserve(std::size_t new_cap) + { + data_.reserve(new_cap); + } + + void push_back(uint8_t b) + { + data_.push_back(b); + } + + void assign(const uint8_t* s, std::size_t count) + { + data_.clear(); + data_.insert(s, s+count); + } + + void append(const uint8_t* s, std::size_t count) + { + data_.insert(s, s+count); + } + + void clear() + { + data_.clear(); + } + + uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + // iterator support + iterator begin() noexcept + { + return data_.begin(); + } + iterator end() noexcept + { + return data_.end(); + } + + const_iterator begin() const noexcept + { + return data_.begin(); + } + const_iterator end() const noexcept + { + return data_.end(); + } + + uint8_t* data() + { + return data_.data(); + } + + const uint8_t* data() const + { + return data_.data(); + } + + std::size_t size() const + { + return data_.size(); + } + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use size()") + std::size_t length() const + { + return data_.size(); + } + #endif + + int compare(const byte_string_view& s) const noexcept + { + const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size())); + return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1); + } + + int compare(const basic_byte_string& s) const noexcept + { + const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size())); + return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_byte_string& o) + { + os << byte_string_view(o); + return os; + } + }; + + template + constexpr byte_string_view::byte_string_view(const basic_byte_string& bytes) + : data_(bytes.data()), size_(bytes.size()) + { + } + + // == + inline + bool operator==(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + template + bool operator==(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + + // != + + inline + bool operator!=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + template + bool operator!=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + + // <= + + inline + bool operator<=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) >= 0; + } + template + bool operator<=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) >= 0; + } + + // < + + inline + bool operator<(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) > 0; + } + template + bool operator<(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) > 0; + } + + // >= + + inline + bool operator>=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) <= 0; + } + template + bool operator>=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) <= 0; + } + + // > + + inline + bool operator>(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) < 0; + } + template + bool operator>(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) < 0; + } + + using byte_string = basic_byte_string>; + + namespace type_traits { + + template + struct is_basic_byte_string + : std::false_type + {}; + + template + struct is_basic_byte_string> + : std::true_type + {}; + + } // namespace type_traits + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/config/binary_config.hpp b/include/jsoncons/config/binary_config.hpp new file mode 100644 index 0000000..51b52a8 --- /dev/null +++ b/include/jsoncons/config/binary_config.hpp @@ -0,0 +1,226 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CONFIG_BINARY_CONFIG_HPP +#define JSONCONS_CONFIG_BINARY_CONFIG_HPP + +#include +#include +#include +#include // std::memcpy +#include +#include // std::enable_if + +// The definitions below follow the definitions in compiler_support_p.h, https://github.com/01org/tinycbor +// MIT license + +#ifdef __F16C__ +# include +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if defined(__GNUC__) +#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) || (__has_builtin(__builtin_bswap64) && __has_builtin(__builtin_bswap32)) +# define JSONCONS_BYTE_SWAP_64 __builtin_bswap64 +# define JSONCONS_BYTE_SWAP_32 __builtin_bswap32 +# ifdef __INTEL_COMPILER +# define JSONCONS_BYTE_SWAP_16 _bswap16 +# elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 608) || __has_builtin(__builtin_bswap16) +# define JSONCONS_BYTE_SWAP_16 __builtin_bswap16 +# endif +#endif +#elif defined(__sun) +# include +#elif defined(_MSC_VER) +// MSVC, which implies sizeof(long) == 4 +# define JSONCONS_BYTE_SWAP_64 _byteswap_uint64 +# define JSONCONS_BYTE_SWAP_32 _byteswap_ulong +# define JSONCONS_BYTE_SWAP_16 _byteswap_ushort +#endif + +namespace jsoncons { +namespace binary { + + struct uint128_holder + { + uint64_t lo; + uint64_t hi; + }; + + static inline bool add_check_overflow(std::size_t v1, std::size_t v2, std::size_t *r) + { + #if ((defined(__GNUC__) && (__GNUC__ >= 5)) && !defined(__INTEL_COMPILER)) || __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(v1, v2, r); + #else + // unsigned additions are well-defined + *r = v1 + v2; + return v1 > v1 + v2; + #endif + } + + #if defined(__apple_build_version__) && ((__clang_major__ < 8) || ((__clang_major__ == 8) && (__clang_minor__ < 1))) + #define APPLE_MISSING_INTRINSICS 1 + #endif + + inline + uint16_t encode_half(double val) + { + #if defined(__F16C__) && !defined(APPLE_MISSING_INTRINSICS) + return _cvtss_sh((float)val, 3); + #else + uint64_t v; + std::memcpy(&v, &val, sizeof(v)); + int64_t sign = static_cast(v >> 63 << 15); + int64_t exp = (v >> 52) & 0x7ff; + int64_t mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */ + exp -= 1023; + if (exp == 1024) { + /* infinity or NaN */ + exp = 16; + mant >>= 1; + } else if (exp >= 16) { + /* overflow, as largest number */ + exp = 15; + mant = 1023; + } else if (exp >= -14) { + /* regular normal */ + } else if (exp >= -24) { + /* subnormal */ + mant |= 1024; + mant >>= -(exp + 14); + exp = -15; + } else { + /* underflow, make zero */ + return 0; + } + + /* safe cast here as bit operations above guarantee not to overflow */ + return static_cast(sign | ((exp + 15) << 10) | mant); + #endif + } + + /* this function was copied & adapted from RFC 7049 Appendix D */ + inline + double decode_half(uint16_t half) + { + #if defined(__F16C__) && !defined(APPLE_MISSING_INTRINSICS) + return _cvtsh_ss(half); + #else + int64_t exp = (half >> 10) & 0x1f; + int64_t mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = ldexp(static_cast(mant), -24); + } + else if (exp != 31) + { + val = ldexp(static_cast(mant) + 1024.0, static_cast(exp - 25)); + } + else + { + val = mant == 0 ? std::numeric_limits::infinity() : std::nan(""); + } + return half & 0x8000 ? -val : val; + #endif + } + + // byte_swap + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint8_t),T>::type + byte_swap(T val) + { + return val; + } + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint16_t),T>::type + byte_swap(T val) + { + #if defined(JSONCONS_BYTE_SWAP_16) + return JSONCONS_BYTE_SWAP_16(val); + #else + return (static_cast(val) >> 8) | (static_cast(val) << 8); + #endif + } + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint32_t),T>::type + byte_swap(T val) + { + #if defined(JSONCONS_BYTE_SWAP_32) + return JSONCONS_BYTE_SWAP_32(val); + #else + uint32_t tmp = ((static_cast(val) << 8) & 0xff00ff00) | ((static_cast(val) >> 8) & 0xff00ff); + return (tmp << 16) | (tmp >> 16); + #endif + } + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint64_t),T>::type + byte_swap(T val) + { + #if defined(JSONCONS_BYTE_SWAP_64) + return JSONCONS_BYTE_SWAP_64(val); + #else + uint64_t tmp = ((static_cast(val) & 0x00000000ffffffffull) << 32) | ((static_cast(val) & 0xffffffff00000000ull) >> 32); + tmp = ((tmp & 0x0000ffff0000ffffull) << 16) | ((tmp & 0xffff0000ffff0000ull) >> 16); + return ((tmp & 0x00ff00ff00ff00ffull) << 8) | ((tmp & 0xff00ff00ff00ff00ull) >> 8); + #endif + } + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint32_t),T>::type + byte_swap(T val) + { + uint32_t x; + std::memcpy(&x,&val,sizeof(uint32_t)); + uint32_t y = byte_swap(x); + T val2; + std::memcpy(&val2,&y,sizeof(uint32_t)); + return val2; + } + + template + typename std::enable_if::value && sizeof(T) == sizeof(uint64_t),T>::type + byte_swap(T val) + { + uint64_t x; + std::memcpy(&x,&val,sizeof(uint64_t)); + uint64_t y = byte_swap(x); + T val2; + std::memcpy(&val2,&y,sizeof(uint64_t)); + return val2; + } + + template + typename std::enable_if::value && sizeof(T) == 2*sizeof(uint64_t),T>::type + byte_swap(T val) + { + uint128_holder x; + uint8_t buf[2*sizeof(uint64_t)]; + std::memcpy(buf,&val,2*sizeof(uint64_t)); + std::memcpy(&x.lo,buf,sizeof(uint64_t)); + std::memcpy(&x.hi,buf+sizeof(uint64_t),sizeof(uint64_t)); + + uint128_holder y; + y.lo = byte_swap(x.hi); + y.hi = byte_swap(x.lo); + + T val2; + std::memcpy(&val2,&y,2*sizeof(uint64_t)); + + return val2; + } + +} // binary +} // jsoncons + +#endif diff --git a/include/jsoncons/config/compiler_support.hpp b/include/jsoncons/config/compiler_support.hpp new file mode 100644 index 0000000..15d30cf --- /dev/null +++ b/include/jsoncons/config/compiler_support.hpp @@ -0,0 +1,389 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_COMPILER_SUPPORT_HPP +#define JSONCONS_COMPILER_SUPPORT_HPP + +#include +#include +#include +#include +#include + +#if defined (__clang__) +#define JSONCONS_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#endif + +// Uncomment the following line to suppress deprecated names (recommended for new code) +//#define JSONCONS_NO_DEPRECATED + +// The definitions below follow the definitions in compiler_support_p.h, https://github.com/01org/tinycbor +// MIT license + +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577 +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9 +#define JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR 1 +#define JSONCONS_NO_MAP_CONS_TAKES_ALLOCATOR 1 +#endif + +#if defined(__clang__) +# define JSONCONS_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__GNUC__) && ((__GNUC__ >= 7)) +# define JSONCONS_FALLTHROUGH __attribute__((fallthrough)) +#elif defined (__GNUC__) +# define JSONCONS_FALLTHROUGH // FALLTHRU +#else +# define JSONCONS_FALLTHROUGH +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define JSONCONS_LIKELY(x) __builtin_expect(!!(x), 1) +#define JSONCONS_UNLIKELY(x) __builtin_expect(!!(x), 0) +#define JSONCONS_UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +#define JSONCONS_LIKELY(x) x +#define JSONCONS_UNLIKELY(x) x +#define JSONCONS_UNREACHABLE() __assume(0) +#else +#define JSONCONS_LIKELY(x) x +#define JSONCONS_UNLIKELY(x) x +#define JSONCONS_UNREACHABLE() do {} while (0) +#endif + +// Deprecated symbols markup +#if (defined(__cplusplus) && __cplusplus >= 201402L) +#define JSONCONS_DEPRECATED_MSG(msg) [[deprecated(msg)]] +#endif + +#if !defined(JSONCONS_DEPRECATED_MSG) && defined(__GNUC__) && defined(__has_extension) +#if __has_extension(attribute_deprecated_with_message) +#define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) +#endif +#endif + +#if !defined(JSONCONS_DEPRECATED_MSG) && defined(_MSC_VER) +#if (_MSC_VER) >= 1920 +#define JSONCONS_DEPRECATED_MSG(msg) [[deprecated(msg)]] +#else +#define JSONCONS_DEPRECATED_MSG(msg) __declspec(deprecated(msg)) +#endif +#endif + +// Following boost/atomic/detail/config.hpp +#if !defined(JSONCONS_DEPRECATED_MSG) && (\ + (defined(__GNUC__) && ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0)) >= 405) ||\ + (defined(__SUNPRO_CC) && (__SUNPRO_CC + 0) >= 0x5130)) + #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) +#endif + +#if !defined(JSONCONS_DEPRECATED_MSG) && defined(__clang__) && defined(__has_extension) + #if __has_extension(attribute_deprecated_with_message) + #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) + #else + #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated)) + #endif +#endif + +#if !defined(JSONCONS_DEPRECATED_MSG) +#define JSONCONS_DEPRECATED_MSG(msg) +#endif + +#if defined(ANDROID) || defined(__ANDROID__) +#if __ANDROID_API__ >= 21 +#define JSONCONS_HAS_STRTOLD_L +#else +#define JSONCONS_NO_LOCALECONV +#endif +#endif + +#if defined(_MSC_VER) +#define JSONCONS_HAS_MSC_STRTOD_L +#define JSONCONS_HAS_FOPEN_S +#endif + +#ifndef JSONCONS_HAS_CP14 + #if defined(_MSVC_LANG) + #if _MSVC_LANG >= 201402L + #define JSONCONS_HAS_CP14 + #endif + #elif __cplusplus >= 201402L + #define JSONCONS_HAS_CP14 + #endif +#endif + +#if !defined(JSONCONS_HAS_STD_FROM_CHARS) +# if defined(__GNUC__) +# if (__GNUC__ >= 11) +# if (__cplusplus >= 201703) +# define JSONCONS_HAS_STD_FROM_CHARS 1 +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 11) +# endif // defined(__GNUC__) +# if defined(_MSC_VER) +# if (_MSC_VER >= 1924 && _MSVC_LANG >= 201703) +# define JSONCONS_HAS_STD_FROM_CHARS 1 +# endif // (_MSC_VER >= 1924 && MSVC_LANG >= 201703) +# endif // defined(_MSC_VER) +#endif +#if defined(JSONCONS_HAS_STD_FROM_CHARS) +#include +#endif + +#if !defined(JSONCONS_HAS_2017) +# if defined(__clang__) +# if (__cplusplus >= 201703) +# define JSONCONS_HAS_2017 1 +# endif // (__cplusplus >= 201703) +# endif // defined(__clang__) +# if defined(__GNUC__) +# if (__GNUC__ >= 7) +# if (__cplusplus >= 201703) +# define JSONCONS_HAS_2017 1 +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 7) +# endif // defined(__GNUC__) +# if defined(_MSC_VER) +# if (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +# define JSONCONS_HAS_2017 1 +# endif // (_MSC_VER >= 1910 && MSVC_LANG >= 201703) +# endif // defined(_MSC_VER) +#endif + +#if defined(JSONCONS_HAS_2017) + #define JSONCONS_NODISCARD [[nodiscard]] + #define JSONCONS_IF_CONSTEXPR if constexpr +#else + #define JSONCONS_NODISCARD + #define JSONCONS_IF_CONSTEXPR if +#endif + +#if !defined(JSONCONS_HAS_STD_STRING_VIEW) +# if (defined JSONCONS_HAS_2017) +# if defined(__clang__) +# if __has_include() +# define JSONCONS_HAS_STD_STRING_VIEW 1 +# endif // __has_include() +# else +# define JSONCONS_HAS_STD_STRING_VIEW 1 +# endif +# endif // defined(JSONCONS_HAS_2017) +#endif // !defined(JSONCONS_HAS_STD_STRING_VIEW) + +#if !defined(JSONCONS_HAS_STD_BYTE) +# if (defined JSONCONS_HAS_2017) +# define JSONCONS_HAS_STD_BYTE 1 +# endif // defined(JSONCONS_HAS_2017) +#endif // !defined(JSONCONS_HAS_STD_BYTE) + +#if !defined(JSONCONS_HAS_STD_OPTIONAL) +# if (defined JSONCONS_HAS_2017) +# if defined(__clang__) +# if __has_include() +# define JSONCONS_HAS_STD_OPTIONAL 1 +# endif // __has_include() +# else +# define JSONCONS_HAS_STD_OPTIONAL 1 +# endif +# endif // defined(JSONCONS_HAS_2017) +#endif // !defined(JSONCONS_HAS_STD_OPTIONAL) + +#if !defined(JSONCONS_HAS_STD_VARIANT) +# if (defined JSONCONS_HAS_2017) +# if defined(__clang__) +# if defined(__APPLE__) +# if JSONCONS_CLANG_VERSION >= 100001 +# define JSONCONS_HAS_STD_VARIANT 1 +# endif +# elif __has_include() +# define JSONCONS_HAS_STD_VARIANT 1 +# endif // __has_include() +# else +# define JSONCONS_HAS_STD_VARIANT 1 +# endif +# endif // defined(JSONCONS_HAS_2017) +#endif // !defined(JSONCONS_HAS_STD_VARIANT) + +#if !defined(JSONCONS_HAS_FILESYSTEM) +# if (defined JSONCONS_HAS_2017) +# if defined(__clang__) +# if __has_include() +# define JSONCONS_HAS_FILESYSTEM 1 +# endif // __has_include() +# else +# define JSONCONS_HAS_FILESYSTEM 1 +# endif +# endif // defined(JSONCONS_HAS_2017) +#endif // !defined(JSONCONS_HAS_FILESYSTEM) + +#if (!defined(JSONCONS_NO_EXCEPTIONS)) +// Check if exceptions are disabled. +# if defined( __cpp_exceptions) && __cpp_exceptions == 0 +# define JSONCONS_NO_EXCEPTIONS 1 +# endif +#endif + +#if !defined(JSONCONS_NO_EXCEPTIONS) + +#if defined(__GNUC__) && !__EXCEPTIONS +# define JSONCONS_NO_EXCEPTIONS 1 +#elif _MSC_VER +#if defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0 +# define JSONCONS_NO_EXCEPTIONS 1 +#elif !defined(_CPPUNWIND) +# define JSONCONS_NO_EXCEPTIONS 1 +#endif +#endif +#endif + +// allow to disable exceptions +#if !defined(JSONCONS_NO_EXCEPTIONS) + #define JSONCONS_THROW(exception) throw exception + #define JSONCONS_RETHROW throw + #define JSONCONS_TRY try + #define JSONCONS_CATCH(exception) catch(exception) +#else + #define JSONCONS_THROW(exception) std::terminate() + #define JSONCONS_RETHROW std::terminate() + #define JSONCONS_TRY if (true) + #define JSONCONS_CATCH(exception) if (false) +#endif + +#if !defined(JSONCONS_HAS_STD_MAKE_UNIQUE) + #if defined(__clang__) && defined(__cplusplus) + #if defined(__APPLE__) + #if __clang_major__ >= 6 && __cplusplus >= 201402L // Xcode 6 + #define JSONCONS_HAS_STD_MAKE_UNIQUE + #endif + #elif ((__clang_major__*100 +__clang_minor__) >= 340) && __cplusplus >= 201402L + #define JSONCONS_HAS_STD_MAKE_UNIQUE + #endif + #elif defined(__GNUC__) + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L + #define JSONCONS_HAS_STD_MAKE_UNIQUE + #endif + #elif defined(_MSC_VER) + #if _MSC_VER >= 1800 + #define JSONCONS_HAS_STD_MAKE_UNIQUE + #endif + #endif +#endif // !defined(JSONCONS_HAS_STD_MAKE_UNIQUE) + +#ifndef JSONCONS_HAS_CP14_CONSTEXPR + #if defined(_MSC_VER) + #if _MSC_VER >= 1910 + #define JSONCONS_HAS_CP14_CONSTEXPR + #endif + #elif defined(__GNUC__) + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 600 && __cplusplus >= 201402L + #define JSONCONS_HAS_CP14_CONSTEXPR + #endif + #endif +#endif + +#if defined(JSONCONS_HAS_CP14_CONSTEXPR) +# define JSONCONS_CPP14_CONSTEXPR constexpr +#else +# define JSONCONS_CPP14_CONSTEXPR +#endif + +// Follows boost + +// gcc and clang +#if (defined(__clang__) || defined(__GNUC__)) && defined(__cplusplus) +#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER) +# define JSONCONS_HAS_INT128 +#endif + +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +#if (__clang_major__ >= 4) && defined(__has_include) +#if __has_include() +# define JSONCONS_HAS_FLOAT128 +#endif +#endif +#endif +#endif + +#if defined(__GNUC__) +#if defined(_GLIBCXX_USE_FLOAT128) +# define JSONCONS_HAS_FLOAT128 +#endif +#endif + +#if defined(__clang__) +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +#if (__clang_major__ >= 4) && defined(__has_include) +#if __has_include() +# define JSONCONS_HAS_FLOAT128 +#endif +#endif +#endif +#endif + +// Follows boost config/detail/suffix.hpp +#if defined(JSONCONS_HAS_INT128) && defined(__cplusplus) +namespace jsoncons{ +# ifdef __GNUC__ + __extension__ typedef __int128 int128_type; + __extension__ typedef unsigned __int128 uint128_type; +# else + typedef __int128 int128_type; + typedef unsigned __int128 uint128_type; +# endif +} +#endif +#if defined(JSONCONS_HAS_FLOAT128) && defined(__cplusplus) +namespace jsoncons { +# ifdef __GNUC__ + __extension__ typedef __float128 float128_type; +# else + typedef __float128 float128_type; +# endif +} +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 + #define JSONCONS_COPY(first,last,d_first) std::copy(first, last, stdext::make_checked_array_iterator(d_first, static_cast(std::distance(first, last)))) +#else + #define JSONCONS_COPY(first,last,d_first) std::copy(first, last, d_first) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +#define JSONCONS_CONSTEXPR +#else +#define JSONCONS_CONSTEXPR constexpr +#endif + +namespace jsoncons { + + class assertion_error : public std::runtime_error + { + public: + assertion_error(const std::string& s) noexcept + : std::runtime_error(s) + { + } + const char* what() const noexcept override + { + return std::runtime_error::what(); + } + }; + +} // namespace jsoncons + +#define JSONCONS_STR2(x) #x +#define JSONCONS_STR(x) JSONCONS_STR2(x) + +#ifdef _DEBUG +#define JSONCONS_ASSERT(x) if (!(x)) { \ + JSONCONS_THROW(jsoncons::assertion_error("assertion '" #x "' failed at " __FILE__ ":" \ + JSONCONS_STR(__LINE__))); } +#else +#define JSONCONS_ASSERT(x) if (!(x)) { \ + JSONCONS_THROW(jsoncons::assertion_error("assertion '" #x "' failed at <> :" \ + JSONCONS_STR( 0 ))); } +#endif // _DEBUG + +#endif // JSONCONS_COMPILER_SUPPORT_HPP diff --git a/include/jsoncons/config/jsoncons_config.hpp b/include/jsoncons/config/jsoncons_config.hpp new file mode 100644 index 0000000..63c437b --- /dev/null +++ b/include/jsoncons/config/jsoncons_config.hpp @@ -0,0 +1,308 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CONFIG_JSONCONS_CONFIG_HPP +#define JSONCONS_CONFIG_JSONCONS_CONFIG_HPP + +#include +#include +#include +#include + +#if !defined(JSONCONS_HAS_STD_STRING_VIEW) +#include +namespace jsoncons { +using jsoncons::detail::basic_string_view; +using string_view = basic_string_view>; +using wstring_view = basic_string_view>; +} +#else +#include +namespace jsoncons { +using std::basic_string_view; +using std::string_view; +using std::wstring_view; +} +#endif + +#if !defined(JSONCONS_HAS_STD_SPAN) +#include +namespace jsoncons { +using jsoncons::detail::span; +} +#else +#include +namespace jsoncons { +using std::span; +} +#endif + +#if defined(JSONCONS_HAS_STD_OPTIONAL) + #include + namespace jsoncons { + using std::optional; + } +#elif defined(JSONCONS_HAS_BOOST_OPTIONAL) + #include + namespace jsoncons { + using boost::optional; + } +#else + #include + namespace jsoncons { + using jsoncons::detail::optional; +} +#endif // !defined(JSONCONS_HAS_STD_OPTIONAL) + +#if !defined(JSONCONS_HAS_STD_ENDIAN) +#include +namespace jsoncons { +using jsoncons::detail::endian; +} +#else +#include +namespace jsoncons +{ + using std::endian; +} +#endif + +#if !defined(JSONCONS_HAS_STD_MAKE_UNIQUE) + +#include +#include +#include +#include + +namespace jsoncons { + + template + struct unique_if + { + using value_is_not_array = std::unique_ptr; + }; + + template + struct unique_if + { + typedef std::unique_ptr value_is_array_of_unknown_bound; + }; + + template + struct unique_if { + using value_is_array_of_known_bound = void; + }; + + template + typename unique_if::value_is_not_array + make_unique(Args&&... args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename unique_if::value_is_array_of_unknown_bound + make_unique(std::size_t n) + { + using U = typename std::remove_extent::type; + return std::unique_ptr(new U[n]()); + } + + template + typename unique_if::value_is_array_of_known_bound + make_unique(Args&&...) = delete; +} // jsoncons + +#else + +#include +namespace jsoncons +{ + using std::make_unique; +} + +#endif // !defined(JSONCONS_HAS_STD_MAKE_UNIQUE) + +namespace jsoncons { +namespace binary { + + // native_to_big + + template + typename std::enable_if::type + native_to_big(T val, OutputIt d_first) + { + uint8_t buf[sizeof(T)]; + std::memcpy(buf, &val, sizeof(T)); + for (auto item : buf) + { + *d_first++ = item; + } + } + + template + typename std::enable_if::type + native_to_big(T val, OutputIt d_first) + { + T val2 = byte_swap(val); + uint8_t buf[sizeof(T)]; + std::memcpy(buf, &val2, sizeof(T)); + for (auto item : buf) + { + *d_first++ = item; + } + } + + // native_to_little + + template + typename std::enable_if::type + native_to_little(T val, OutputIt d_first) + { + uint8_t buf[sizeof(T)]; + std::memcpy(buf, &val, sizeof(T)); + for (auto item : buf) + { + *d_first++ = item; + } + } + + template + typename std::enable_if::type + native_to_little(T val, OutputIt d_first) + { + T val2 = byte_swap(val); + uint8_t buf[sizeof(T)]; + std::memcpy(buf, &val2, sizeof(T)); + for (auto item : buf) + { + *d_first++ = item; + } + } + + // big_to_native + + template + typename std::enable_if::type + big_to_native(const uint8_t* first, std::size_t count) + { + if (sizeof(T) > count) + { + return T{}; + } + T val; + std::memcpy(&val,first,sizeof(T)); + return val; + } + + template + typename std::enable_if::type + big_to_native(const uint8_t* first, std::size_t count) + { + if (sizeof(T) > count) + { + return T{}; + } + T val; + std::memcpy(&val,first,sizeof(T)); + return byte_swap(val); + } + + // little_to_native + + template + typename std::enable_if::type + little_to_native(const uint8_t* first, std::size_t count) + { + if (sizeof(T) > count) + { + return T{}; + } + T val; + std::memcpy(&val,first,sizeof(T)); + return val; + } + + template + typename std::enable_if::type + little_to_native(const uint8_t* first, std::size_t count) + { + if (sizeof(T) > count) + { + return T{}; + } + T val; + std::memcpy(&val,first,sizeof(T)); + return byte_swap(val); + } + +} // binary +} // jsoncons + +namespace jsoncons { + + template + constexpr const CharT* cstring_constant_of_type(const char* c, const wchar_t* w); + + template<> inline + constexpr const char* cstring_constant_of_type(const char* c, const wchar_t*) + { + return c; + } + template<> inline + constexpr const wchar_t* cstring_constant_of_type(const char*, const wchar_t* w) + { + return w; + } + + template + std::basic_string string_constant_of_type(const char* c, const wchar_t* w); + + template<> inline + std::string string_constant_of_type(const char* c, const wchar_t*) + { + return std::string(c); + } + template<> inline + std::wstring string_constant_of_type(const char*, const wchar_t* w) + { + return std::wstring(w); + } + + template + jsoncons::basic_string_view string_view_constant_of_type(const char* c, const wchar_t* w); + + template<> inline + jsoncons::string_view string_view_constant_of_type(const char* c, const wchar_t*) + { + return jsoncons::string_view(c); + } + template<> inline + jsoncons::wstring_view string_view_constant_of_type(const char*, const wchar_t* w) + { + return jsoncons::wstring_view(w); + } + +} // jsoncons + +#define JSONCONS_EXPAND(X) X +#define JSONCONS_QUOTE(Prefix, A) JSONCONS_EXPAND(Prefix ## #A) + +#define JSONCONS_CSTRING_CONSTANT(CharT, Str) cstring_constant_of_type(Str, JSONCONS_QUOTE(L, Str)) +#define JSONCONS_STRING_CONSTANT(CharT, Str) string_constant_of_type(Str, JSONCONS_QUOTE(L, Str)) +#define JSONCONS_STRING_VIEW_CONSTANT(CharT, Str) string_view_constant_of_type(Str, JSONCONS_QUOTE(L, Str)) + +#if defined(__clang__) +#define JSONCONS_HAS_STD_REGEX 1 +#elif (defined(__GNUC__) && (__GNUC__ == 4)) && (defined(__GNUC__) && __GNUC_MINOR__ < 9) +// GCC 4.8 has broken regex support: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631 +#else +#define JSONCONS_HAS_STD_REGEX 1 +#endif + +#endif // JSONCONS_CONFIG_JSONCONS_CONFIG_HPP + + diff --git a/include/jsoncons/config/version.hpp b/include/jsoncons/config/version.hpp new file mode 100644 index 0000000..7f11eef --- /dev/null +++ b/include/jsoncons/config/version.hpp @@ -0,0 +1,40 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_VERSION_HPP +#define JSONCONS_VERSION_HPP + +#include + +#define JSONCONS_VERSION_MAJOR 0 +#define JSONCONS_VERSION_MINOR 168 +#define JSONCONS_VERSION_PATCH 7 + +namespace jsoncons { + +struct versioning_info +{ + unsigned int const major; + unsigned int const minor; + unsigned int const patch; + + friend std::ostream& operator<<(std::ostream& os, const versioning_info& ver) + { + os << ver.major << '.' + << ver.minor << '.' + << ver.patch; + return os; + } +}; + +constexpr versioning_info version() +{ + return versioning_info{JSONCONS_VERSION_MAJOR, JSONCONS_VERSION_MINOR, JSONCONS_VERSION_PATCH}; +} + +} + +#endif diff --git a/include/jsoncons/conv_error.hpp b/include/jsoncons/conv_error.hpp new file mode 100644 index 0000000..cad5460 --- /dev/null +++ b/include/jsoncons/conv_error.hpp @@ -0,0 +1,218 @@ +/// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CONV_ERROR_HPP +#define JSONCONS_CONV_ERROR_HPP + +#include +#include + +namespace jsoncons { + + class conv_error : public std::system_error, public virtual json_exception + { + std::size_t line_number_; + std::size_t column_number_; + mutable std::string what_; + public: + conv_error(std::error_code ec) + : std::system_error(ec), line_number_(0), column_number_(0) + { + } + conv_error(std::error_code ec, const std::string& what_arg) + : std::system_error(ec, what_arg), line_number_(0), column_number_(0) + { + } + conv_error(std::error_code ec, std::size_t position) + : std::system_error(ec), line_number_(0), column_number_(position) + { + } + conv_error(std::error_code ec, std::size_t line, std::size_t column) + : std::system_error(ec), line_number_(line), column_number_(column) + { + } + conv_error(const conv_error& other) = default; + + conv_error(conv_error&& other) = default; + + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::system_error::what()); + if (line_number_ != 0 && column_number_ != 0) + { + what_.append(" at line "); + what_.append(std::to_string(line_number_)); + what_.append(" and column "); + what_.append(std::to_string(column_number_)); + } + else if (column_number_ != 0) + { + what_.append(" at position "); + what_.append(std::to_string(column_number_)); + } + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::system_error::what(); + } + } + else + { + return what_.c_str(); + } + } + + std::size_t line() const noexcept + { + return line_number_; + } + + std::size_t column() const noexcept + { + return column_number_; + } + }; + + enum class conv_errc + { + success = 0, + conversion_failed, + not_utf8, + not_wide_char, + not_vector, + not_array, + not_map, + not_pair, + not_string, + not_string_view, + not_byte_string, + not_byte_string_view, + not_integer, + not_signed_integer, + not_unsigned_integer, + not_bigint, + not_double, + not_bool, + not_variant, + not_nullptr, + not_jsoncons_null_type, + not_bitset, + not_base64, + not_base64url, + not_base16 + }; + + template + struct decode_result + { + InputIt it; + conv_errc ec; + }; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use conv_error") typedef conv_error convert_error; +#endif + +} // namespace jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace jsoncons { + +namespace detail { + class conv_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/convert"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case conv_errc::conversion_failed: + return "Unable to convert into the provided type"; + case conv_errc::not_utf8: + return "Cannot convert string to UTF-8"; + case conv_errc::not_wide_char: + return "Cannot convert string to wide characters"; + case conv_errc::not_vector: + return "Cannot convert to vector"; + case conv_errc::not_array: + return "Cannot convert to std::array"; + case conv_errc::not_map: + return "Cannot convert to map"; + case conv_errc::not_pair: + return "Cannot convert to std::pair"; + case conv_errc::not_string: + return "Cannot convert to string"; + case conv_errc::not_string_view: + return "Cannot convert to string_view"; + case conv_errc::not_byte_string: + return "Cannot convert to byte_string"; + case conv_errc::not_byte_string_view: + return "Cannot convert to byte_string_view"; + case conv_errc::not_integer: + return "Cannot convert to integer"; + case conv_errc::not_signed_integer: + return "Cannot convert to signed integer"; + case conv_errc::not_unsigned_integer: + return "Cannot convert to unsigned integer"; + case conv_errc::not_bigint: + return "Cannot convert to bigint"; + case conv_errc::not_double: + return "Cannot convert to double"; + case conv_errc::not_bool: + return "Cannot convert to bool"; + case conv_errc::not_variant: + return "Cannot convert to std::variant"; + case conv_errc::not_nullptr: + return "Cannot convert to std::nullptr_t"; + case conv_errc::not_jsoncons_null_type: + return "Cannot convert to jsoncons::null_type"; + case conv_errc::not_bitset: + return "Cannot convert to std::bitset"; + case conv_errc::not_base64: + return "Input is not a base64 encoded string"; + case conv_errc::not_base64url: + return "Input is not a base64url encoded string"; + case conv_errc::not_base16: + return "Input is not a base16 encoded string"; + default: + return "Unknown conversion error"; + } + } + }; +} // detail + +extern inline +const std::error_category& conv_error_category() +{ + static detail::conv_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(conv_errc result) +{ + return std::error_code(static_cast(result),conv_error_category()); +} + +} + +#endif diff --git a/include/jsoncons/converter.hpp b/include/jsoncons/converter.hpp new file mode 100644 index 0000000..15d1917 --- /dev/null +++ b/include/jsoncons/converter.hpp @@ -0,0 +1,296 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CONVERTER_HPP +#define JSONCONS_CONVERTER_HPP + +#include // std::error_code +#include +#include +#include +#include +#include // from_integer + +namespace jsoncons { + + template + class converter + { + }; + + // Into list like of bytes + template + class converter::value && + type_traits::is_back_insertable_byte_container::value) || + type_traits::is_basic_byte_string::value>::type> + { + using value_type = typename Into::value_type; + using allocator_type = typename Into::allocator_type; + + public: + + JSONCONS_CPP14_CONSTEXPR + Into from(const byte_string_view& bstr, semantic_tag tag, std::error_code& ec) const + { + Into bytes; + from_(bytes, bstr, tag, ec); + return bytes; + } + + JSONCONS_CPP14_CONSTEXPR + Into from(const byte_string_view& bstr, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const + { + Into bytes(alloc); + from_(bytes, bstr, tag, ec); + return bytes; + } + + template + JSONCONS_CPP14_CONSTEXPR + Into from(const jsoncons::basic_string_view& s, semantic_tag tag, std::error_code& ec) const + { + Into bytes; + from_(bytes, s, tag, ec); + return bytes; + } + + template + JSONCONS_CPP14_CONSTEXPR + Into from(const jsoncons::basic_string_view& s, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const + { + Into bytes(alloc); + from_(bytes, s, tag, ec); + return bytes; + } + + private: + JSONCONS_CPP14_CONSTEXPR + void from_(Into& bytes, const byte_string_view& bstr, semantic_tag, std::error_code&) const + { + for (auto ch : bstr) + { + bytes.push_back(static_cast(ch)); + } + } + + template + JSONCONS_CPP14_CONSTEXPR + typename std::enable_if::value>::type + from_(Into& bytes, const jsoncons::basic_string_view& s, semantic_tag tag, std::error_code& ec) const + { + switch (tag) + { + case semantic_tag::base16: + { + auto res = decode_base16(s.begin(), s.end(), bytes); + if (res.ec != conv_errc::success) + { + ec = conv_errc::not_byte_string; + } + break; + } + case semantic_tag::base64: + { + decode_base64(s.begin(), s.end(), bytes); + break; + } + case semantic_tag::base64url: + { + decode_base64url(s.begin(), s.end(), bytes); + break; + } + default: + { + ec = conv_errc::not_byte_string; + break; + } + } + } + + template + typename std::enable_if::value>::type + from_(Into& bytes, const jsoncons::basic_string_view& s, semantic_tag tag, std::error_code& ec) const + { + std::string u; + auto retval = unicode_traits::convert(s.data(), s.size(), u); + if (retval.ec != unicode_traits::conv_errc()) + { + ec = conv_errc::not_utf8; + return; + } + from_(bytes, jsoncons::string_view(u), tag, ec); + } + }; + + // Into string + template + class converter::value>::type> + { + using char_type = typename Into::value_type; + using allocator_type = typename Into::allocator_type; + int dummy_; + public: + explicit converter(int dummy = int()) + : dummy_(dummy) + {} + template + JSONCONS_CPP14_CONSTEXPR + typename std::enable_if::value,Into>::type + from(Integer val, semantic_tag, std::error_code&) const + { + Into s; + jsoncons::detail::from_integer(val, s); + return s; + } + + template + JSONCONS_CPP14_CONSTEXPR + typename std::enable_if::value,Into>::type + from(Integer val, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + Into s(alloc); + jsoncons::detail::from_integer(val, s); + return s; + } + + Into from(double val, semantic_tag, std::error_code&) const + { + Into s; + jsoncons::detail::write_double f{float_chars_format::general,0}; + f(val, s); + return s; + } + + Into from(double val, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + Into s(alloc); + jsoncons::detail::write_double f{float_chars_format::general,0}; + f(val, s); + return s; + } + + Into from(half_arg_t, uint16_t val, semantic_tag, std::error_code&) const + { + Into s; + jsoncons::detail::write_double f{float_chars_format::general,0}; + double x = binary::decode_half(val); + f(x, s); + return s; + } + + Into from(half_arg_t, uint16_t val, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + Into s(alloc); + jsoncons::detail::write_double f{float_chars_format::general,0}; + double x = binary::decode_half(val); + f(x, s); + return s; + } + + template + JSONCONS_CPP14_CONSTEXPR + Into from(const byte_string_view& bytes, semantic_tag tag, std::error_code& ec) const + { + Into s; + from_(s, bytes, tag, ec); + return s; + } + + template + JSONCONS_CPP14_CONSTEXPR + Into from(const byte_string_view& bytes, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const + { + Into s(alloc); + from_(s, bytes, tag, ec); + return s; + } + + constexpr + Into from(const jsoncons::basic_string_view& s, semantic_tag, std::error_code&) const + { + return Into(s.data(), s.size()); + } + + constexpr + Into from(const jsoncons::basic_string_view& s, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + return Into(s.data(), s.size(), alloc); + } + + JSONCONS_CPP14_CONSTEXPR + Into from(bool val, semantic_tag, std::error_code&) const + { + constexpr const char_type* true_constant = JSONCONS_CSTRING_CONSTANT(char_type,"true"); + constexpr const char_type* false_constant = JSONCONS_CSTRING_CONSTANT(char_type,"false"); + + return val ? Into(true_constant,4) : Into(false_constant,5); + } + + JSONCONS_CPP14_CONSTEXPR + Into from(bool val, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + constexpr const char_type* true_constant = JSONCONS_CSTRING_CONSTANT(char_type,"true"); + constexpr const char_type* false_constant = JSONCONS_CSTRING_CONSTANT(char_type,"false"); + + return val ? Into(true_constant,4,alloc) : Into(false_constant,5,alloc); + } + + JSONCONS_CPP14_CONSTEXPR + Into from(null_type, semantic_tag, std::error_code&) const + { + constexpr const char_type* null_constant = JSONCONS_CSTRING_CONSTANT(char_type,"null"); + + return Into(null_constant,4); + } + + JSONCONS_CPP14_CONSTEXPR + Into from(null_type, semantic_tag, const allocator_type& alloc, std::error_code&) const + { + constexpr const char_type* null_constant = JSONCONS_CSTRING_CONSTANT(char_type,"null"); + + return Into(null_constant,4,alloc); + } + private: + + template + JSONCONS_CPP14_CONSTEXPR + typename std::enable_if::value>::type + from_(Into& s, const byte_string_view& bytes, semantic_tag tag, std::error_code&) const + { + switch (tag) + { + case semantic_tag::base64: + encode_base64(bytes.begin(), bytes.end(), s); + break; + case semantic_tag::base16: + encode_base16(bytes.begin(), bytes.end(), s); + break; + default: + encode_base64url(bytes.begin(), bytes.end(), s); + break; + } + } + + template + typename std::enable_if::value>::type + from_(Into& s, const byte_string_view& bytes, semantic_tag tag, std::error_code& ec) const + { + converter convert{ dummy_ }; + std::string u = convert.from(bytes, tag, ec); + + auto retval = unicode_traits::convert(u.data(), u.size(), s); + if (retval.ec != unicode_traits::conv_errc()) + { + ec = conv_errc::not_wide_char; + } + } + + }; + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/decode_json.hpp b/include/jsoncons/decode_json.hpp new file mode 100644 index 0000000..e6ce1a1 --- /dev/null +++ b/include/jsoncons/decode_json.hpp @@ -0,0 +1,209 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DECODE_JSON_HPP +#define JSONCONS_DECODE_JSON_HPP + +#include +#include +#include +#include +#include // std::basic_istream +#include +#include + +namespace jsoncons { + + // decode_json + + template + typename std::enable_if::value && + type_traits::is_sequence_of::value,T>::type + decode_json(const Source& s, + const basic_json_decode_options& options = basic_json_decode_options()) + { + using char_type = typename Source::value_type; + + jsoncons::json_decoder decoder; + basic_json_reader> reader(s, decoder, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_char_sequence::value,T>::type + decode_json(const Source& s, + const basic_json_decode_options& options = basic_json_decode_options()) + { + using char_type = typename Source::value_type; + + basic_json_cursor> cursor(s, options, default_json_parsing()); + jsoncons::json_decoder> decoder; + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_json(std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) + { + jsoncons::json_decoder decoder; + basic_json_reader> reader(is, decoder, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_json(std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) + { + basic_json_cursor cursor(is, options, default_json_parsing()); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_json(InputIt first, InputIt last, + const basic_json_decode_options::value_type>& options = + basic_json_decode_options::value_type>()) + { + using char_type = typename std::iterator_traits::value_type; + + jsoncons::json_decoder decoder; + basic_json_reader> reader(iterator_source(first,last), decoder, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_json(InputIt first, InputIt last, + const basic_json_decode_options::value_type>& options = + basic_json_decode_options::value_type>()) + { + using char_type = typename std::iterator_traits::value_type; + + basic_json_cursor> cursor(iterator_source(first, last), options, default_json_parsing()); + jsoncons::json_decoder> decoder; + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_sequence_of::value,T>::type + decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& s, + const basic_json_decode_options& options = basic_json_decode_options()) + { + using char_type = typename Source::value_type; + + json_decoder decoder(temp_alloc); + + basic_json_reader,TempAllocator> reader(s, decoder, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_char_sequence::value,T>::type + decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& s, + const basic_json_decode_options& options = basic_json_decode_options()) + { + using char_type = typename Source::value_type; + + basic_json_cursor,TempAllocator> cursor(s, options, default_json_parsing(), temp_alloc); + json_decoder,TempAllocator> decoder(result_allocator_arg, temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) + { + json_decoder decoder(temp_alloc); + + basic_json_reader,TempAllocator> reader(is, decoder, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) + { + basic_json_cursor,TempAllocator> cursor(is, options, default_json_parsing(), temp_alloc); + json_decoder,TempAllocator> decoder(result_allocator_arg, temp_alloc,temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // jsoncons + +#endif + diff --git a/include/jsoncons/decode_traits.hpp b/include/jsoncons/decode_traits.hpp new file mode 100644 index 0000000..2c9ee6e --- /dev/null +++ b/include/jsoncons/decode_traits.hpp @@ -0,0 +1,651 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DECODE_TRAITS_HPP +#define JSONCONS_DECODE_TRAITS_HPP + +#include +#include +#include +#include +#include // std::enable_if, std::true_type, std::false_type +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + // decode_traits + + template + struct decode_traits + { + template + static T decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + decoder.reset(); + cursor.read_to(decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + else if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, cursor.context().line(), cursor.context().column())); + } + return decoder.get_result().template as(); + } + }; + + // specializations + + // primitive + + template + struct decode_traits::value + >::type> + { + template + static T decode(basic_staj_cursor& cursor, + json_decoder&, + std::error_code& ec) + { + T v = cursor.current().template get(ec); + return v; + } + }; + + // string + + template + struct decode_traits::value && + std::is_same::value + >::type> + { + template + static T decode(basic_staj_cursor& cursor, + json_decoder&, + std::error_code& ec) + { + T v = cursor.current().template get(ec); + return v; + } + }; + + template + struct decode_traits::value && + !std::is_same::value + >::type> + { + template + static T decode(basic_staj_cursor& cursor, + json_decoder&, + std::error_code& ec) + { + auto val = cursor.current().template get>(ec); + T s; + if (!ec) + { + unicode_traits::convert(val.data(), val.size(), s); + } + return s; + } + }; + + // std::pair + + template + struct decode_traits, CharT> + { + template + static std::pair decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + using value_type = std::pair; + cursor.array_expected(ec); + if (ec) + { + return value_type{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_pair; + return value_type(); + } + cursor.next(ec); // skip past array + if (ec) + { + return value_type(); + } + + T1 v1 = decode_traits::decode(cursor, decoder, ec); + if (ec) {return value_type();} + cursor.next(ec); + if (ec) {return value_type();} + T2 v2 = decode_traits::decode(cursor, decoder, ec); + if (ec) {return value_type();} + cursor.next(ec); + + if (cursor.current().event_type() != staj_event_type::end_array) + { + ec = conv_errc::not_pair; + return value_type(); + } + return std::make_pair(v1, v2); + } + }; + + // vector like + template + struct decode_traits::value && + type_traits::is_list_like::value && + type_traits::is_back_insertable::value && + !type_traits::is_typed_array::value + >::type> + { + using value_type = typename T::value_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + T v; + + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + v.push_back(decode_traits::decode(cursor, decoder, ec)); + if (ec) {return T{};} + cursor.next(ec); + } + return v; + } + }; + + template + struct typed_array_visitor : public default_json_visitor + { + T& v_; + int level_; + public: + using value_type = typename T::value_type; + + typed_array_visitor(T& v) + : default_json_visitor(false,conv_errc::not_vector), v_(v), level_(0) + { + } + private: + bool visit_begin_array(semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (++level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + return true; + } + + bool visit_begin_array(std::size_t size, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (++level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + if (size > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v_, size); + } + return true; + } + + bool visit_end_array(const ser_context&, + std::error_code& ec) override + { + if (level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + return false; + } + + bool visit_uint64(uint64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast(value)); + return true; + } + + bool visit_int64(int64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast(value)); + return true; + } + + bool visit_half(uint16_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + return visit_half_(typename std::integral_constant::value>::type(), value); + } + + bool visit_half_(std::true_type, uint16_t value) + { + v_.push_back(static_cast(value)); + return true; + } + + bool visit_half_(std::false_type, uint16_t value) + { + v_.push_back(static_cast(binary::decode_half(value))); + return true; + } + + bool visit_double(double value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast(value)); + return true; + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_ = std::vector(data.begin(),data.end()); + return false; + } + + static + void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static + void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template + struct decode_traits::value && + type_traits::is_list_like::value && + type_traits::is_back_insertable_byte_container::value && + type_traits::is_typed_array::value + >::type> + { + using value_type = typename T::value_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder&, + std::error_code& ec) + { + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + switch (cursor.current().event_type()) + { + case staj_event_type::byte_string_value: + { + auto bytes = cursor.current().template get(ec); + if (!ec) + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + for (auto ch : bytes) + { + v.push_back(static_cast(ch)); + } + cursor.next(ec); + return v; + } + else + { + return T{}; + } + } + case staj_event_type::begin_array: + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + typed_array_visitor visitor(v); + cursor.read_to(visitor, ec); + return v; + } + default: + { + ec = conv_errc::not_vector; + return T{}; + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template + struct decode_traits::value && + type_traits::is_list_like::value && + type_traits::is_back_insertable::value && + !type_traits::is_back_insertable_byte_container::value && + type_traits::is_typed_array::value + >::type> + { + using value_type = typename T::value_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder&, + std::error_code& ec) + { + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + switch (cursor.current().event_type()) + { + case staj_event_type::begin_array: + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + typed_array_visitor visitor(v); + cursor.read_to(visitor, ec); + return v; + } + default: + { + ec = conv_errc::not_vector; + return T{}; + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + // set like + template + struct decode_traits::value && + type_traits::is_list_like::value && + !type_traits::is_back_insertable::value && + type_traits::is_insertable::value + >::type> + { + using value_type = typename T::value_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + T v; + + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + v.insert(decode_traits::decode(cursor, decoder, ec)); + if (ec) {return T{};} + cursor.next(ec); + if (ec) {return T{};} + } + return v; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + // std::array + + template + struct decode_traits,CharT> + { + using value_type = typename std::array::value_type; + + template + static std::array decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + std::array v; + cursor.array_expected(ec); + if (ec) + { + v.fill(T()); + return v; + } + v.fill(T{}); + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + cursor.next(ec); + for (std::size_t i = 0; i < N && cursor.current().event_type() != staj_event_type::end_array && !ec; ++i) + { + v[i] = decode_traits::decode(cursor, decoder, ec); + if (ec) {return v;} + cursor.next(ec); + if (ec) {return v;} + } + return v; + } + }; + + // map like + + template + struct decode_traits::value && + type_traits::is_map_like::value && + type_traits::is_constructible_from_const_pointer_and_size::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + T val; + if (cursor.current().event_type() != staj_event_type::begin_object) + { + ec = conv_errc::not_map; + return val; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + ec = json_errc::expected_key; + return val; + } + auto key = cursor.current().template get(ec); + if (ec) return val; + cursor.next(ec); + if (ec) return val; + val.emplace(std::move(key),decode_traits::decode(cursor, decoder, ec)); + if (ec) {return val;} + cursor.next(ec); + if (ec) {return val;} + } + return val; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template + struct decode_traits::value && + type_traits::is_map_like::value && + std::is_integral::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static T decode(basic_staj_cursor& cursor, + json_decoder& decoder, + std::error_code& ec) + { + T val; + if (cursor.current().event_type() != staj_event_type::begin_object) + { + ec = conv_errc::not_map; + return val; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + ec = json_errc::expected_key; + return val; + } + auto s = cursor.current().template get>(ec); + if (ec) return val; + key_type n{0}; + auto r = jsoncons::detail::to_integer(s.data(), s.size(), n); + if (r.ec != jsoncons::detail::to_integer_errc()) + { + ec = json_errc::invalid_number; + return val; + } + cursor.next(ec); + if (ec) return val; + val.emplace(n, decode_traits::decode(cursor, decoder, ec)); + if (ec) {return val;} + cursor.next(ec); + if (ec) {return val;} + } + return val; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + +} // jsoncons + +#endif + diff --git a/include/jsoncons/detail/endian.hpp b/include/jsoncons/detail/endian.hpp new file mode 100644 index 0000000..d47a0f7 --- /dev/null +++ b/include/jsoncons/detail/endian.hpp @@ -0,0 +1,44 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_ENDIAN_HPP +#define JSONCONS_DETAIL_ENDIAN_HPP + +#if defined(__sun) +# include +#endif + +namespace jsoncons { +namespace detail { + + enum class endian + { + #if defined(_MSC_VER) + // MSVC, which implies Windows, which implies little-endian + little = 0, + big = 1, + native = little + #elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__) + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__ + #elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) + little = 0, + big = 1, + native = big + #elif !defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) + little = 0, + big = 1, + native = little + #else + #error "Unable to determine byte order!" + #endif + }; + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/grisu3.hpp b/include/jsoncons/detail/grisu3.hpp new file mode 100644 index 0000000..771cf1d --- /dev/null +++ b/include/jsoncons/detail/grisu3.hpp @@ -0,0 +1,312 @@ +/* +Implements the Grisu3 algorithm for printing floating-point numbers. + +Follows Florian Loitsch's grisu3_59_56 implementation, available at +http://florian.loitsch.com/publications, in bench.tar.gz, with +minor modifications. +*/ + +/* + Copyright (c) 2009 Florian Loitsch + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef JSONCONS_GRISU3_HPP +#define JSONCONS_GRISU3_HPP + +#pragma once +#include +#include +#include +#include +#include +#include // std::memmove +#include + +namespace jsoncons { namespace detail { + +// diy_fp + +typedef struct diy_fp_t { + uint64_t f; + int e; +} diy_fp_t; + +inline +diy_fp_t minus(diy_fp_t x, diy_fp_t y) +{ + assert(x.e == y.e); + assert(x.f >= y.f); + diy_fp_t r = { x.f = x.f - y.f, x.e }; + return r; +} + +inline +diy_fp_t multiply(diy_fp_t x, diy_fp_t y) +{ + uint64_t a, b, c, d, ac, bc, ad, bd, tmp; + diy_fp_t r; uint64_t M32 = 0xFFFFFFFF; + a = x.f >> 32; b = x.f & M32; + c = y.f >> 32; d = y.f & M32; + ac = a * c; bc = b * c; ad = a * d; bd = b * d; + tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + r.e = x.e + y.e + 64; + return r; +} + +// k_comp + +inline +int k_comp(int e, int alpha, int /*gamma*/) +{ + constexpr double d_1_log2_10 = 0.30102999566398114; // 1 / lg(10) + int x = alpha - e + 63; + return static_cast(std::ceil(x * d_1_log2_10)); +} + +// powers_ten_round64 + +constexpr int diy_significand_size = 64; + +static constexpr uint64_t powers_ten[] = { 0xbf29dcaba82fdeae, 0xeef453d6923bd65a, 0x9558b4661b6565f8, 0xbaaee17fa23ebf76, 0xe95a99df8ace6f54, 0x91d8a02bb6c10594, 0xb64ec836a47146fa, 0xe3e27a444d8d98b8, 0x8e6d8c6ab0787f73, 0xb208ef855c969f50, 0xde8b2b66b3bc4724, 0x8b16fb203055ac76, 0xaddcb9e83c6b1794, 0xd953e8624b85dd79, 0x87d4713d6f33aa6c, 0xa9c98d8ccb009506, 0xd43bf0effdc0ba48, 0x84a57695fe98746d, 0xa5ced43b7e3e9188, 0xcf42894a5dce35ea, 0x818995ce7aa0e1b2, 0xa1ebfb4219491a1f, 0xca66fa129f9b60a7, 0xfd00b897478238d1, 0x9e20735e8cb16382, 0xc5a890362fddbc63, 0xf712b443bbd52b7c, 0x9a6bb0aa55653b2d, 0xc1069cd4eabe89f9, 0xf148440a256e2c77, 0x96cd2a865764dbca, 0xbc807527ed3e12bd, 0xeba09271e88d976c, 0x93445b8731587ea3, 0xb8157268fdae9e4c, 0xe61acf033d1a45df, 0x8fd0c16206306bac, 0xb3c4f1ba87bc8697, 0xe0b62e2929aba83c, 0x8c71dcd9ba0b4926, 0xaf8e5410288e1b6f, 0xdb71e91432b1a24b, 0x892731ac9faf056f, 0xab70fe17c79ac6ca, 0xd64d3d9db981787d, 0x85f0468293f0eb4e, 0xa76c582338ed2622, 0xd1476e2c07286faa, 0x82cca4db847945ca, 0xa37fce126597973d, 0xcc5fc196fefd7d0c, 0xff77b1fcbebcdc4f, 0x9faacf3df73609b1, 0xc795830d75038c1e, 0xf97ae3d0d2446f25, 0x9becce62836ac577, 0xc2e801fb244576d5, 0xf3a20279ed56d48a, 0x9845418c345644d7, 0xbe5691ef416bd60c, 0xedec366b11c6cb8f, 0x94b3a202eb1c3f39, 0xb9e08a83a5e34f08, 0xe858ad248f5c22ca, 0x91376c36d99995be, 0xb58547448ffffb2e, 0xe2e69915b3fff9f9, 0x8dd01fad907ffc3c, 0xb1442798f49ffb4b, 0xdd95317f31c7fa1d, 0x8a7d3eef7f1cfc52, 0xad1c8eab5ee43b67, 0xd863b256369d4a41, 0x873e4f75e2224e68, 0xa90de3535aaae202, 0xd3515c2831559a83, 0x8412d9991ed58092, 0xa5178fff668ae0b6, 0xce5d73ff402d98e4, 0x80fa687f881c7f8e, 0xa139029f6a239f72, 0xc987434744ac874f, 0xfbe9141915d7a922, 0x9d71ac8fada6c9b5, 0xc4ce17b399107c23, 0xf6019da07f549b2b, 0x99c102844f94e0fb, 0xc0314325637a193a, 0xf03d93eebc589f88, 0x96267c7535b763b5, 0xbbb01b9283253ca3, 0xea9c227723ee8bcb, 0x92a1958a7675175f, 0xb749faed14125d37, 0xe51c79a85916f485, 0x8f31cc0937ae58d3, 0xb2fe3f0b8599ef08, 0xdfbdcece67006ac9, 0x8bd6a141006042be, 0xaecc49914078536d, 0xda7f5bf590966849, 0x888f99797a5e012d, 0xaab37fd7d8f58179, 0xd5605fcdcf32e1d7, 0x855c3be0a17fcd26, 0xa6b34ad8c9dfc070, 0xd0601d8efc57b08c, 0x823c12795db6ce57, 0xa2cb1717b52481ed, 0xcb7ddcdda26da269, 0xfe5d54150b090b03, 0x9efa548d26e5a6e2, 0xc6b8e9b0709f109a, 0xf867241c8cc6d4c1, 0x9b407691d7fc44f8, 0xc21094364dfb5637, 0xf294b943e17a2bc4, 0x979cf3ca6cec5b5b, 0xbd8430bd08277231, 0xece53cec4a314ebe, 0x940f4613ae5ed137, 0xb913179899f68584, 0xe757dd7ec07426e5, 0x9096ea6f3848984f, 0xb4bca50b065abe63, 0xe1ebce4dc7f16dfc, 0x8d3360f09cf6e4bd, 0xb080392cc4349ded, 0xdca04777f541c568, 0x89e42caaf9491b61, 0xac5d37d5b79b6239, 0xd77485cb25823ac7, 0x86a8d39ef77164bd, 0xa8530886b54dbdec, 0xd267caa862a12d67, 0x8380dea93da4bc60, 0xa46116538d0deb78, 0xcd795be870516656, 0x806bd9714632dff6, 0xa086cfcd97bf97f4, 0xc8a883c0fdaf7df0, 0xfad2a4b13d1b5d6c, 0x9cc3a6eec6311a64, 0xc3f490aa77bd60fd, 0xf4f1b4d515acb93c, 0x991711052d8bf3c5, 0xbf5cd54678eef0b7, 0xef340a98172aace5, 0x9580869f0e7aac0f, 0xbae0a846d2195713, 0xe998d258869facd7, 0x91ff83775423cc06, 0xb67f6455292cbf08, 0xe41f3d6a7377eeca, 0x8e938662882af53e, 0xb23867fb2a35b28e, 0xdec681f9f4c31f31, 0x8b3c113c38f9f37f, 0xae0b158b4738705f, 0xd98ddaee19068c76, 0x87f8a8d4cfa417ca, 0xa9f6d30a038d1dbc, 0xd47487cc8470652b, 0x84c8d4dfd2c63f3b, 0xa5fb0a17c777cf0a, 0xcf79cc9db955c2cc, 0x81ac1fe293d599c0, 0xa21727db38cb0030, 0xca9cf1d206fdc03c, 0xfd442e4688bd304b, 0x9e4a9cec15763e2f, 0xc5dd44271ad3cdba, 0xf7549530e188c129, 0x9a94dd3e8cf578ba, 0xc13a148e3032d6e8, 0xf18899b1bc3f8ca2, 0x96f5600f15a7b7e5, 0xbcb2b812db11a5de, 0xebdf661791d60f56, 0x936b9fcebb25c996, 0xb84687c269ef3bfb, 0xe65829b3046b0afa, 0x8ff71a0fe2c2e6dc, 0xb3f4e093db73a093, 0xe0f218b8d25088b8, 0x8c974f7383725573, 0xafbd2350644eead0, 0xdbac6c247d62a584, 0x894bc396ce5da772, 0xab9eb47c81f5114f, 0xd686619ba27255a3, 0x8613fd0145877586, 0xa798fc4196e952e7, 0xd17f3b51fca3a7a1, 0x82ef85133de648c5, 0xa3ab66580d5fdaf6, 0xcc963fee10b7d1b3, 0xffbbcfe994e5c620, 0x9fd561f1fd0f9bd4, 0xc7caba6e7c5382c9, 0xf9bd690a1b68637b, 0x9c1661a651213e2d, 0xc31bfa0fe5698db8, 0xf3e2f893dec3f126, 0x986ddb5c6b3a76b8, 0xbe89523386091466, 0xee2ba6c0678b597f, 0x94db483840b717f0, 0xba121a4650e4ddec, 0xe896a0d7e51e1566, 0x915e2486ef32cd60, 0xb5b5ada8aaff80b8, 0xe3231912d5bf60e6, 0x8df5efabc5979c90, 0xb1736b96b6fd83b4, 0xddd0467c64bce4a1, 0x8aa22c0dbef60ee4, 0xad4ab7112eb3929e, 0xd89d64d57a607745, 0x87625f056c7c4a8b, 0xa93af6c6c79b5d2e, 0xd389b47879823479, 0x843610cb4bf160cc, 0xa54394fe1eedb8ff, 0xce947a3da6a9273e, 0x811ccc668829b887, 0xa163ff802a3426a9, 0xc9bcff6034c13053, 0xfc2c3f3841f17c68, 0x9d9ba7832936edc1, 0xc5029163f384a931, 0xf64335bcf065d37d, 0x99ea0196163fa42e, 0xc06481fb9bcf8d3a, 0xf07da27a82c37088, 0x964e858c91ba2655, 0xbbe226efb628afeb, 0xeadab0aba3b2dbe5, 0x92c8ae6b464fc96f, 0xb77ada0617e3bbcb, 0xe55990879ddcaabe, 0x8f57fa54c2a9eab7, 0xb32df8e9f3546564, 0xdff9772470297ebd, 0x8bfbea76c619ef36, 0xaefae51477a06b04, 0xdab99e59958885c5, 0x88b402f7fd75539b, 0xaae103b5fcd2a882, 0xd59944a37c0752a2, 0x857fcae62d8493a5, 0xa6dfbd9fb8e5b88f, 0xd097ad07a71f26b2, 0x825ecc24c8737830, 0xa2f67f2dfa90563b, 0xcbb41ef979346bca, 0xfea126b7d78186bd, 0x9f24b832e6b0f436, 0xc6ede63fa05d3144, 0xf8a95fcf88747d94, 0x9b69dbe1b548ce7d, 0xc24452da229b021c, 0xf2d56790ab41c2a3, 0x97c560ba6b0919a6, 0xbdb6b8e905cb600f, 0xed246723473e3813, 0x9436c0760c86e30c, 0xb94470938fa89bcf, 0xe7958cb87392c2c3, 0x90bd77f3483bb9ba, 0xb4ecd5f01a4aa828, 0xe2280b6c20dd5232, 0x8d590723948a535f, 0xb0af48ec79ace837, 0xdcdb1b2798182245, 0x8a08f0f8bf0f156b, 0xac8b2d36eed2dac6, 0xd7adf884aa879177, 0x86ccbb52ea94baeb, 0xa87fea27a539e9a5, 0xd29fe4b18e88640f, 0x83a3eeeef9153e89, 0xa48ceaaab75a8e2b, 0xcdb02555653131b6, 0x808e17555f3ebf12, 0xa0b19d2ab70e6ed6, 0xc8de047564d20a8c, 0xfb158592be068d2f, 0x9ced737bb6c4183d, 0xc428d05aa4751e4d, 0xf53304714d9265e0, 0x993fe2c6d07b7fac, 0xbf8fdb78849a5f97, 0xef73d256a5c0f77d, 0x95a8637627989aae, 0xbb127c53b17ec159, 0xe9d71b689dde71b0, 0x9226712162ab070e, 0xb6b00d69bb55c8d1, 0xe45c10c42a2b3b06, 0x8eb98a7a9a5b04e3, 0xb267ed1940f1c61c, 0xdf01e85f912e37a3, 0x8b61313bbabce2c6, 0xae397d8aa96c1b78, 0xd9c7dced53c72256, 0x881cea14545c7575, 0xaa242499697392d3, 0xd4ad2dbfc3d07788, 0x84ec3c97da624ab5, 0xa6274bbdd0fadd62, 0xcfb11ead453994ba, 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3e, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e48, 0xc612062576589ddb, 0xf79687aed3eec551, 0x9abe14cd44753b53, 0xc16d9a0095928a27, 0xf1c90080baf72cb1, 0x971da05074da7bef, 0xbce5086492111aeb, 0xec1e4a7db69561a5, 0x9392ee8e921d5d07, 0xb877aa3236a4b449, 0xe69594bec44de15b, 0x901d7cf73ab0acd9, 0xb424dc35095cd80f, 0xe12e13424bb40e13, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a597, 0xabcc77118461cefd, 0xd6bf94d5e57a42bc, 0x8637bd05af6c69b6, 0xa7c5ac471b478423, 0xd1b71758e219652c, 0x83126e978d4fdf3b, 0xa3d70a3d70a3d70a, 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, 0xa18f07d736b90be5, 0xc9f2c9cd04674edf, 0xfc6f7c4045812296, 0x9dc5ada82b70b59e, 0xc5371912364ce305, 0xf684df56c3e01bc7, 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, 0x92efd1b8d0cf37be, 0xb7abc627050305ae, 0xe596b7b0c643c719, 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f, 0x8c213d9da502de45, 0xaf298d050e4395d7, 0xdaf3f04651d47b4c, 0x88d8762bf324cd10, 0xab0e93b6efee0054, 0xd5d238a4abe98068, 0x85a36366eb71f041, 0xa70c3c40a64e6c52, 0xd0cf4b50cfe20766, 0x82818f1281ed44a0, 0xa321f2d7226895c8, 0xcbea6f8ceb02bb3a, 0xfee50b7025c36a08, 0x9f4f2726179a2245, 0xc722f0ef9d80aad6, 0xf8ebad2b84e0d58c, 0x9b934c3b330c8577, 0xc2781f49ffcfa6d5, 0xf316271c7fc3908b, 0x97edd871cfda3a57, 0xbde94e8e43d0c8ec, 0xed63a231d4c4fb27, 0x945e455f24fb1cf9, 0xb975d6b6ee39e437, 0xe7d34c64a9c85d44, 0x90e40fbeea1d3a4b, 0xb51d13aea4a488dd, 0xe264589a4dcdab15, 0x8d7eb76070a08aed, 0xb0de65388cc8ada8, 0xdd15fe86affad912, 0x8a2dbf142dfcc7ab, 0xacb92ed9397bf996, 0xd7e77a8f87daf7fc, 0x86f0ac99b4e8dafd, 0xa8acd7c0222311bd, 0xd2d80db02aabd62c, 0x83c7088e1aab65db, 0xa4b8cab1a1563f52, 0xcde6fd5e09abcf27, 0x80b05e5ac60b6178, 0xa0dc75f1778e39d6, 0xc913936dd571c84c, 0xfb5878494ace3a5f, 0x9d174b2dcec0e47b, 0xc45d1df942711d9a, 0xf5746577930d6501, 0x9968bf6abbe85f20, 0xbfc2ef456ae276e9, 0xefb3ab16c59b14a3, 0x95d04aee3b80ece6, 0xbb445da9ca61281f, 0xea1575143cf97227, 0x924d692ca61be758, 0xb6e0c377cfa2e12e, 0xe498f455c38b997a, 0x8edf98b59a373fec, 0xb2977ee300c50fe7, 0xdf3d5e9bc0f653e1, 0x8b865b215899f46d, 0xae67f1e9aec07188, 0xda01ee641a708dea, 0x884134fe908658b2, 0xaa51823e34a7eedf, 0xd4e5e2cdc1d1ea96, 0x850fadc09923329e, 0xa6539930bf6bff46, 0xcfe87f7cef46ff17, 0x81f14fae158c5f6e, 0xa26da3999aef774a, 0xcb090c8001ab551c, 0xfdcb4fa002162a63, 0x9e9f11c4014dda7e, 0xc646d63501a1511e, 0xf7d88bc24209a565, 0x9ae757596946075f, 0xc1a12d2fc3978937, 0xf209787bb47d6b85, 0x9745eb4d50ce6333, 0xbd176620a501fc00, 0xec5d3fa8ce427b00, 0x93ba47c980e98ce0, 0xb8a8d9bbe123f018, 0xe6d3102ad96cec1e, 0x9043ea1ac7e41393, 0xb454e4a179dd1877, 0xe16a1dc9d8545e95, 0x8ce2529e2734bb1d, 0xb01ae745b101e9e4, 0xdc21a1171d42645d, 0x899504ae72497eba, 0xabfa45da0edbde69, 0xd6f8d7509292d603, 0x865b86925b9bc5c2, 0xa7f26836f282b733, 0xd1ef0244af2364ff, 0x8335616aed761f1f, 0xa402b9c5a8d3a6e7, 0xcd036837130890a1, 0x802221226be55a65, 0xa02aa96b06deb0fe, 0xc83553c5c8965d3d, 0xfa42a8b73abbf48d, 0x9c69a97284b578d8, 0xc38413cf25e2d70e, 0xf46518c2ef5b8cd1, 0x98bf2f79d5993803, 0xbeeefb584aff8604, 0xeeaaba2e5dbf6785, 0x952ab45cfa97a0b3, 0xba756174393d88e0, 0xe912b9d1478ceb17, 0x91abb422ccb812ef, 0xb616a12b7fe617aa, 0xe39c49765fdf9d95, 0x8e41ade9fbebc27d, 0xb1d219647ae6b31c, 0xde469fbd99a05fe3, 0x8aec23d680043bee, 0xada72ccc20054aea, 0xd910f7ff28069da4, 0x87aa9aff79042287, 0xa99541bf57452b28, 0xd3fa922f2d1675f2, 0x847c9b5d7c2e09b7, 0xa59bc234db398c25, 0xcf02b2c21207ef2f, 0x8161afb94b44f57d, 0xa1ba1ba79e1632dc, 0xca28a291859bbf93, 0xfcb2cb35e702af78, 0x9defbf01b061adab, 0xc56baec21c7a1916, 0xf6c69a72a3989f5c, 0x9a3c2087a63f6399, 0xc0cb28a98fcf3c80, 0xf0fdf2d3f3c30b9f, 0x969eb7c47859e744, 0xbc4665b596706115, 0xeb57ff22fc0c795a, 0x9316ff75dd87cbd8, 0xb7dcbf5354e9bece, 0xe5d3ef282a242e82, 0x8fa475791a569d11, 0xb38d92d760ec4455, 0xe070f78d3927556b, 0x8c469ab843b89563, 0xaf58416654a6babb, 0xdb2e51bfe9d0696a, 0x88fcf317f22241e2, 0xab3c2fddeeaad25b, 0xd60b3bd56a5586f2, 0x85c7056562757457, 0xa738c6bebb12d16d, 0xd106f86e69d785c8, 0x82a45b450226b39d, 0xa34d721642b06084, 0xcc20ce9bd35c78a5, 0xff290242c83396ce, 0x9f79a169bd203e41, 0xc75809c42c684dd1, 0xf92e0c3537826146, 0x9bbcc7a142b17ccc, 0xc2abf989935ddbfe, 0xf356f7ebf83552fe, 0x98165af37b2153df, 0xbe1bf1b059e9a8d6, 0xeda2ee1c7064130c, 0x9485d4d1c63e8be8, 0xb9a74a0637ce2ee1, 0xe8111c87c5c1ba9a, 0x910ab1d4db9914a0, 0xb54d5e4a127f59c8, 0xe2a0b5dc971f303a, 0x8da471a9de737e24, 0xb10d8e1456105dad, 0xdd50f1996b947519, 0x8a5296ffe33cc930, 0xace73cbfdc0bfb7b, 0xd8210befd30efa5a, 0x8714a775e3e95c78, 0xa8d9d1535ce3b396, 0xd31045a8341ca07c, 0x83ea2b892091e44e, 0xa4e4b66b68b65d61, 0xce1de40642e3f4b9, 0x80d2ae83e9ce78f4, 0xa1075a24e4421731, 0xc94930ae1d529cfd, 0xfb9b7cd9a4a7443c, 0x9d412e0806e88aa6, 0xc491798a08a2ad4f, 0xf5b5d7ec8acb58a3, 0x9991a6f3d6bf1766, 0xbff610b0cc6edd3f, 0xeff394dcff8a948f, 0x95f83d0a1fb69cd9, 0xbb764c4ca7a44410, 0xea53df5fd18d5514, 0x92746b9be2f8552c, 0xb7118682dbb66a77, 0xe4d5e82392a40515, 0x8f05b1163ba6832d, 0xb2c71d5bca9023f8, 0xdf78e4b2bd342cf7, 0x8bab8eefb6409c1a, 0xae9672aba3d0c321, 0xda3c0f568cc4f3e9, 0x8865899617fb1871, 0xaa7eebfb9df9de8e, 0xd51ea6fa85785631, 0x8533285c936b35df, 0xa67ff273b8460357, 0xd01fef10a657842c, 0x8213f56a67f6b29c, 0xa298f2c501f45f43, 0xcb3f2f7642717713, 0xfe0efb53d30dd4d8, 0x9ec95d1463e8a507, 0xc67bb4597ce2ce49, 0xf81aa16fdc1b81db, 0x9b10a4e5e9913129, 0xc1d4ce1f63f57d73, 0xf24a01a73cf2dcd0, 0x976e41088617ca02, 0xbd49d14aa79dbc82, 0xec9c459d51852ba3, 0x93e1ab8252f33b46, 0xb8da1662e7b00a17, 0xe7109bfba19c0c9d, 0x906a617d450187e2, 0xb484f9dc9641e9db, 0xe1a63853bbd26451, 0x8d07e33455637eb3, 0xb049dc016abc5e60, 0xdc5c5301c56b75f7, 0x89b9b3e11b6329bb, 0xac2820d9623bf429, 0xd732290fbacaf134, 0x867f59a9d4bed6c0, 0xa81f301449ee8c70, 0xd226fc195c6a2f8c, 0x83585d8fd9c25db8, 0xa42e74f3d032f526, 0xcd3a1230c43fb26f, 0x80444b5e7aa7cf85, 0xa0555e361951c367, 0xc86ab5c39fa63441, 0xfa856334878fc151, 0x9c935e00d4b9d8d2, 0xc3b8358109e84f07, 0xf4a642e14c6262c9, 0x98e7e9cccfbd7dbe, 0xbf21e44003acdd2d, 0xeeea5d5004981478, 0x95527a5202df0ccb, 0xbaa718e68396cffe, 0xe950df20247c83fd, 0x91d28b7416cdd27e, 0xb6472e511c81471e, 0xe3d8f9e563a198e5, 0x8e679c2f5e44ff8f, 0xb201833b35d63f73, 0xde81e40a034bcf50, 0x8b112e86420f6192, 0xadd57a27d29339f6, 0xd94ad8b1c7380874, 0x87cec76f1c830549, 0xa9c2794ae3a3c69b, 0xd433179d9c8cb841, 0x849feec281d7f329, 0xa5c7ea73224deff3, 0xcf39e50feae16bf0, 0x81842f29f2cce376, 0xa1e53af46f801c53, 0xca5e89b18b602368, 0xfcf62c1dee382c42, 0x9e19db92b4e31ba9, 0xc5a05277621be294, 0xf70867153aa2db39, 0x9a65406d44a5c903, 0xc0fe908895cf3b44, 0xf13e34aabb430a15, 0x96c6e0eab509e64d, 0xbc789925624c5fe1, 0xeb96bf6ebadf77d9, 0x933e37a534cbaae8, 0xb80dc58e81fe95a1, 0xe61136f2227e3b0a, 0x8fcac257558ee4e6, 0xb3bd72ed2af29e20, 0xe0accfa875af45a8, 0x8c6c01c9498d8b89, 0xaf87023b9bf0ee6b, 0xdb68c2ca82ed2a06, 0x892179be91d43a44, 0xab69d82e364948d4 }; +static constexpr int powers_ten_e[] = { -1203, -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166, -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130, -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093, -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057, -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020, -1017, -1014, -1010, -1007, -1004, -1000, -997, -994, -990, -987, -984, -980, -977, -974, -970, -967, -964, -960, -957, -954, -950, -947, -944, -940, -937, -934, -931, -927, -924, -921, -917, -914, -911, -907, -904, -901, -897, -894, -891, -887, -884, -881, -877, -874, -871, -867, -864, -861, -857, -854, -851, -847, -844, -841, -838, -834, -831, -828, -824, -821, -818, -814, -811, -808, -804, -801, -798, -794, -791, -788, -784, -781, -778, -774, -771, -768, -764, -761, -758, -754, -751, -748, -744, -741, -738, -735, -731, -728, -725, -721, -718, -715, -711, -708, -705, -701, -698, -695, -691, -688, -685, -681, -678, -675, -671, -668, -665, -661, -658, -655, -651, -648, -645, -642, -638, -635, -632, -628, -625, -622, -618, -615, -612, -608, -605, -602, -598, -595, -592, -588, -585, -582, -578, -575, -572, -568, -565, -562, -558, -555, -552, -549, -545, -542, -539, -535, -532, -529, -525, -522, -519, -515, -512, -509, -505, -502, -499, -495, -492, -489, -485, -482, -479, -475, -472, -469, -465, -462, -459, -455, -452, -449, -446, -442, -439, -436, -432, -429, -426, -422, -419, -416, -412, -409, -406, -402, -399, -396, -392, -389, -386, -382, -379, -376, -372, -369, -366, -362, -359, -356, -353, -349, -346, -343, -339, -336, -333, -329, -326, -323, -319, -316, -313, -309, -306, -303, -299, -296, -293, -289, -286, -283, -279, -276, -273, -269, -266, -263, -259, -256, -253, -250, -246, -243, -240, -236, -233, -230, -226, -223, -220, -216, -213, -210, -206, -203, -200, -196, -193, -190, -186, -183, -180, -176, -173, -170, -166, -163, -160, -157, -153, -150, -147, -143, -140, -137, -133, -130, -127, -123, -120, -117, -113, -110, -107, -103, -100, -97, -93, -90, -87, -83, -80, -77, -73, -70, -67, -63, -60, -57, -54, -50, -47, -44, -40, -37, -34, -30, -27, -24, -20, -17, -14, -10, -7, -4, 0, 3, 6, 10, 13, 16, 20, 23, 26, 30, 33, 36, 39, 43, 46, 49, 53, 56, 59, 63, 66, 69, 73, 76, 79, 83, 86, 89, 93, 96, 99, 103, 106, 109, 113, 116, 119, 123, 126, 129, 132, 136, 139, 142, 146, 149, 152, 156, 159, 162, 166, 169, 172, 176, 179, 182, 186, 189, 192, 196, 199, 202, 206, 209, 212, 216, 219, 222, 226, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259, 262, 265, 269, 272, 275, 279, 282, 285, 289, 292, 295, 299, 302, 305, 309, 312, 315, 319, 322, 325, 328, 332, 335, 338, 342, 345, 348, 352, 355, 358, 362, 365, 368, 372, 375, 378, 382, 385, 388, 392, 395, 398, 402, 405, 408, 412, 415, 418, 422, 425, 428, 431, 435, 438, 441, 445, 448, 451, 455, 458, 461, 465, 468, 471, 475, 478, 481, 485, 488, 491, 495, 498, 501, 505, 508, 511, 515, 518, 521, 524, 528, 531, 534, 538, 541, 544, 548, 551, 554, 558, 561, 564, 568, 571, 574, 578, 581, 584, 588, 591, 594, 598, 601, 604, 608, 611, 614, 617, 621, 624, 627, 631, 634, 637, 641, 644, 647, 651, 654, 657, 661, 664, 667, 671, 674, 677, 681, 684, 687, 691, 694, 697, 701, 704, 707, 711, 714, 717, 720, 724, 727, 730, 734, 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770, 774, 777, 780, 784, 787, 790, 794, 797, 800, 804, 807, 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, 847, 850, 853, 857, 860, 863, 867, 870, 873, 877, 880, 883, 887, 890, 893, 897, 900, 903, 907, 910, 913, 916, 920, 923, 926, 930, 933, 936, 940, 943, 946, 950, 953, 956, 960, 963, 966, 970, 973, 976, 980, 983, 986, 990, 993, 996, 1000, 1003, 1006, 1009, 1013, 1016, 1019, 1023, 1026, 1029, 1033, 1036, 1039, 1043, 1046, 1049, 1053, 1056, 1059, 1063, 1066, 1069, 1073, 1076 }; + +inline +diy_fp_t cached_power(int k) +{ + diy_fp_t res; + int index = 343 + k; + res.f = powers_ten[index]; + res.e = powers_ten_e[index]; + return res; +} + +// double + +/* +typedef union { + double d; + uint64_t n; +} converter_t; + +inline +uint64_t double_to_uint64(double d) { converter_t tmp; tmp.d = d; return tmp.n; } + +inline +double uint64_to_double(uint64_t d64) { converter_t tmp; tmp.n = d64; return tmp.d; } +*/ +inline +uint64_t double_to_uint64(double d) {uint64_t d64; std::memcpy(&d64,&d,sizeof(double)); return d64; } + +inline +double uint64_to_double(uint64_t d64) {double d; std::memcpy(&d,&d64,sizeof(double)); return d; } + +constexpr int dp_significand_size = 52; +constexpr int dp_exponent_bias = (0x3FF + dp_significand_size); +constexpr int dp_min_exponent = (-dp_exponent_bias); +constexpr uint64_t dp_exponent_mask = 0x7FF0000000000000; +constexpr uint64_t dp_significand_mask = 0x000FFFFFFFFFFFFF; +constexpr uint64_t dp_hidden_bit = 0x0010000000000000; + +inline +diy_fp_t normalize_diy_fp(diy_fp_t in) +{ + diy_fp_t res = in; + /* Normalize now */ + /* the original number could have been a denormal. */ + while (!(res.f & dp_hidden_bit)) + { + res.f <<= 1; + res.e--; + } + /* do the final shifts in one go. Don't forget the hidden bit (the '-1') */ + res.f <<= (diy_significand_size - dp_significand_size - 1); + res.e = res.e - (diy_significand_size - dp_significand_size - 1); + return res; +} + +inline +diy_fp_t double2diy_fp(double d) +{ + uint64_t d64 = double_to_uint64(d); + int biased_e = (d64 & dp_exponent_mask) >> dp_significand_size; + uint64_t significand = (d64 & dp_significand_mask); + diy_fp_t res; + if (biased_e != 0) + { + res.f = significand + dp_hidden_bit; + res.e = biased_e - dp_exponent_bias; + } + else + { + res.f = significand; + res.e = dp_min_exponent + 1; + } + return res; +} + +inline +diy_fp_t normalize_boundary(diy_fp_t in) +{ + diy_fp_t res = in; + /* Normalize now */ + /* the original number could have been a denormal. */ + while (!(res.f & (dp_hidden_bit << 1))) + { + res.f <<= 1; + res.e--; + } + /* do the final shifts in one go. Don't forget the hidden bit (the '-1') */ + res.f <<= (diy_significand_size - dp_significand_size - 2); + res.e = res.e - (diy_significand_size - dp_significand_size - 2); + return res; +} + +inline +void normalized_boundaries(double d, diy_fp_t *out_m_minus, diy_fp_t *out_m_plus) +{ + diy_fp_t v = double2diy_fp(d); + diy_fp_t pl, mi; + bool significand_is_zero = v.f == dp_hidden_bit; + pl.f = (v.f << 1) + 1; pl.e = v.e - 1; + pl = normalize_boundary(pl); + if (significand_is_zero) + { + mi.f = (v.f << 2) - 1; + mi.e = v.e - 2; + } else + { + mi.f = (v.f << 1) - 1; + mi.e = v.e - 1; + } + int x = mi.e - pl.e; + mi.f <<= x; + mi.e = pl.e; + *out_m_plus = pl; + *out_m_minus = mi; +} + +inline +double random_double() +{ + uint64_t tmp = 0; + int i; + for (i = 0; i < 8; i++) + { + tmp <<= 8; + tmp += rand() % 256; + } + return uint64_to_double(tmp); +} + +// grisu3 + +inline +bool round_weed(char *buffer, int len, + uint64_t wp_W, uint64_t Delta, + uint64_t rest, uint64_t ten_kappa, + uint64_t ulp) +{ + uint64_t wp_Wup = wp_W - ulp; + uint64_t wp_Wdown = wp_W + ulp; + while (rest < wp_Wup && /// round1 + Delta - rest >= ten_kappa && + (rest + ten_kappa < wp_Wup || /// closer + wp_Wup - rest >= rest + ten_kappa - wp_Wup)) + { + buffer[len - 1]--; rest += ten_kappa; + } + if (rest < wp_Wdown && /// round2 + Delta - rest >= ten_kappa && + (rest + ten_kappa < wp_Wdown || + wp_Wdown - rest > rest + ten_kappa - wp_Wdown)) return 0; + return 2 * ulp <= rest && rest <= Delta - 4 * ulp; /// weed +} + +inline +bool digit_gen(diy_fp_t Wm, diy_fp_t W, diy_fp_t Wp, + char *buffer, int *len, int *K) +{ + const uint32_t TEN2 = 100; + + uint32_t div, p1; uint64_t p2, tmp, unit = 1; + int d, kappa; + diy_fp_t one, wp_W, Delta; + Delta = minus(Wp, Wm); Delta.f += 2 * unit; + wp_W = minus(Wp, W); wp_W.f += unit; + one.f = ((uint64_t)1) << -Wp.e; one.e = Wp.e; + p1 = static_cast((Wp.f + 1) >> -one.e); + p2 = (Wp.f + 1) & (one.f - 1); + *len = 0; kappa = 3; div = TEN2; + while (kappa > 0) + { + d = p1 / div; + if (d || *len) buffer[(*len)++] = (char)('0' + d); + p1 %= div; kappa--; + tmp = (((uint64_t)p1) << -one.e) + p2; + if (tmp < Delta.f) + { + *K += kappa; + return round_weed(buffer, *len, wp_W.f, Delta.f, tmp, + ((uint64_t)div) << -one.e, unit); + } + div /= 10; + } + while (1) + { + p2 *= 10; Delta.f *= 10; unit *= 10; + d = static_cast(p2 >> -one.e); + if (d || *len) buffer[(*len)++] = (char)('0' + d); + p2 &= one.f - 1; kappa--; + if (p2 < Delta.f) + { + *K += kappa; + return round_weed(buffer, *len, wp_W.f * unit, Delta.f, p2, + one.f, unit); + } + } +} + +inline +bool grisu3(double v, char *buffer, int *length, int *K) +{ + diy_fp_t w_m, w_p; + int q = 64, alpha = -59, gamma = -56; + normalized_boundaries(v, &w_m, &w_p); + diy_fp_t w = normalize_diy_fp(double2diy_fp(v)); + int mk = k_comp(w_p.e + q, alpha, gamma); + diy_fp_t c_mk = cached_power(mk); + diy_fp_t W = multiply(w, c_mk); + diy_fp_t Wp = multiply(w_p, c_mk); + diy_fp_t Wm = multiply(w_m, c_mk); + *K = -mk; + bool result = digit_gen(Wm, W, Wp, buffer, length, K); + buffer[*length] = 0; + return result; +} + +}} // namespace detail namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/optional.hpp b/include/jsoncons/detail/optional.hpp new file mode 100644 index 0000000..492cd76 --- /dev/null +++ b/include/jsoncons/detail/optional.hpp @@ -0,0 +1,483 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_OPTIONAL_HPP +#define JSONCONS_DETAIL_OPTIONAL_HPP + +#include // placement new +#include +#include // std::swap +#include +#include + +namespace jsoncons +{ +namespace detail +{ + template + class optional; + + template + struct is_constructible_or_convertible_from_optional + : std::integral_constant< + bool, std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_convertible&, T1>::value || + std::is_convertible&&, T1>::value || + std::is_convertible&, T1>::value || + std::is_convertible&&, T1>::value> {}; + + template + struct is_constructible_convertible_or_assignable_from_optional + : std::integral_constant< + bool, is_constructible_or_convertible_from_optional::value || + std::is_assignable&>::value || + std::is_assignable&&>::value || + std::is_assignable&>::value || + std::is_assignable&&>::value> {}; + + template + class optional + { + public: + using value_type = T; + private: + bool has_value_; + union { + char dummy_; + T value_; + }; + public: + constexpr optional() noexcept + : has_value_(false), dummy_{} + { + } + + // copy constructors + optional(const optional& other) + : has_value_(false), dummy_{} + { + if (other) + { + construct(*other); + } + } + + // converting + template ::value && + std::is_constructible::value && + std::is_convertible::value && + !is_constructible_or_convertible_from_optional::value && + std::is_copy_constructible::type>::value,int>::type = 0> + optional(const optional& other) + : has_value_(false), dummy_{} + { + if (other) + { + construct(*other); + } + } + + template ::value && + std::is_constructible::value && + !std::is_convertible::value && + !is_constructible_or_convertible_from_optional::value && + std::is_copy_constructible::type>::value,int>::type = 0> + explicit optional(const optional& other) + : has_value_(false), dummy_{} + { + if (other) + { + construct(*other); + } + } + + // move constructors + template + optional(optional&& other, + typename std::enable_if::type>::value>::type* = 0) + : has_value_(false), dummy_{} + { + if (other) + { + construct(std::move(other.value_)); + } + } + + // converting + template + optional(optional&& value, + typename std::enable_if::value && + std::is_constructible::value && + !is_constructible_or_convertible_from_optional::value && + std::is_convertible::value,int>::type = 0) // (8) + : has_value_(true), value_(std::forward(value)) + { + } + + template + explicit optional(optional&& value, + typename std::enable_if::value && + std::is_constructible::value && + !is_constructible_or_convertible_from_optional::value && + !std::is_convertible::value,int>::type = 0) // (8) + : has_value_(true), value_(std::forward(value)) + { + } + + + // value constructors + template + optional(T2&& value, + typename std::enable_if,typename std::decay::type>::value && + std::is_constructible::value && + std::is_convertible::value,int>::type = 0) // (8) + : has_value_(true), value_(std::forward(value)) + { + } + + template + explicit optional(T2&& value, + typename std::enable_if,typename std::decay::type>::value && + std::is_constructible::value && + !std::is_convertible::value,int>::type = 0) // (8) + : has_value_(true), value_(std::forward(value)) + { + } + + ~optional() noexcept + { + destroy(); + } + + optional& operator=(const optional& other) + { + if (other) + { + assign(*other); + } + else + { + reset(); + } + return *this; + } + + optional& operator=(optional&& other ) + { + if (other) + { + assign(std::move(*other)); + } + else + { + reset(); + } + return *this; + } + + template + typename std::enable_if, U>::value && + std::is_constructible::value && + !is_constructible_convertible_or_assignable_from_optional::value && + std::is_assignable::value, + optional&>::type + operator=(const optional& other) + { + if (other) + { + assign(*other); + } + else + { + destroy(); + } + return *this; + } + + template + typename std::enable_if, U>::value && + std::is_constructible::value && + !is_constructible_convertible_or_assignable_from_optional::value && + std::is_assignable::value, + optional&>::type + operator=(optional&& other) + { + if (other) + { + assign(std::move(*other)); + } + else + { + destroy(); + } + return *this; + } + + // value assignment + template + typename std::enable_if, typename std::decay::type>::value && + std::is_constructible::value && + std::is_assignable::value && + !(std::is_scalar::value && std::is_same::type>::value), + optional&>::type + operator=(T2&& v) + { + assign(std::forward(v)); + return *this; + } + + constexpr explicit operator bool() const noexcept + { + return has_value_; + } + constexpr bool has_value() const noexcept + { + return has_value_; + } + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4702) +#endif // _MSC_VER + + T& value() & + { + return static_cast(*this) + ? get() + : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); + } + + constexpr const T& value() const & + { + return static_cast(*this) + ? get() + : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); + } + + template + constexpr T value_or(U&& default_value) const & + { + static_assert(std::is_copy_constructible::value, + "get_value_or: T must be copy constructible"); + static_assert(std::is_convertible::value, + "get_value_or: U must be convertible to T"); + return static_cast(*this) + ? **this + : static_cast(std::forward(default_value)); + } + + template + T value_or(U&& default_value) && + { + static_assert(std::is_move_constructible::value, + "get_value_or: T must be move constructible"); + static_assert(std::is_convertible::value, + "get_value_or: U must be convertible to T"); + return static_cast(*this) ? std::move(**this) + : static_cast(std::forward(default_value)); + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + + const T* operator->() const + { + return std::addressof(this->value_); + } + + T* operator->() + { + return std::addressof(this->value_); + } + + constexpr const T& operator*() const& + { + return value(); + } + + T& operator*() & + { + return value(); + } + + void reset() noexcept + { + destroy(); + } + + void swap(optional& other) noexcept(std::is_nothrow_move_constructible::value /*&& + std::is_nothrow_swappable::value*/) + { + const bool contains_a_value = has_value(); + if (contains_a_value == other.has_value()) + { + if (contains_a_value) + { + using std::swap; + swap(**this, *other); + } + } + else + { + optional& source = contains_a_value ? *this : other; + optional& target = contains_a_value ? other : *this; + target = optional(*source); + source.reset(); + } + } + private: + constexpr const T& get() const { return this->value_; } + T& get() { return this->value_; } + + template + void construct(Args&&... args) + { + ::new (static_cast(&this->value_)) T(std::forward(args)...); + has_value_ = true; + } + + void destroy() noexcept + { + if (has_value_) + { + value_.~T(); + has_value_ = false; + } + } + + template + void assign(U&& u) + { + if (has_value_) + { + value_ = std::forward(u); + } + else + { + construct(std::forward(u)); + } + } + }; + + template + typename std::enable_if::value,void>::type + swap(optional& lhs, optional& rhs) noexcept + { + lhs.swap(rhs); + } + + template + constexpr bool operator==(const optional& lhs, const optional& rhs) noexcept + { + return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); + } + + template + constexpr bool operator!=(const optional& lhs, const optional& rhs) noexcept + { + return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); + } + + template + constexpr bool operator<(const optional& lhs, const optional& rhs) noexcept + { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); + } + + template + constexpr bool operator>(const optional& lhs, const optional& rhs) noexcept + { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); + } + + template + constexpr bool operator<=(const optional& lhs, const optional& rhs) noexcept + { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); + } + + template + constexpr bool operator>=(const optional& lhs, const optional& rhs) noexcept + { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); + } + + template + constexpr bool operator==(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs == rhs : false; + } + template + constexpr bool operator==(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs == *rhs : false; + } + + template + constexpr bool operator!=(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs != rhs : true; + } + template + constexpr bool operator!=(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs != *rhs : true; + } + + template + constexpr bool operator<(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs < rhs : true; + } + template + constexpr bool operator<(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs < *rhs : false; + } + + template + constexpr bool operator<=(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs <= rhs : true; + } + template + constexpr bool operator<=(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs <= *rhs : false; + } + + template + constexpr bool operator>(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs > rhs : false; + } + + template + constexpr bool operator>(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs > *rhs : true; + } + + template + constexpr bool operator>=(const optional& lhs, const T2& rhs) noexcept + { + return lhs ? *lhs >= rhs : false; + } + template + constexpr bool operator>=(const T1& lhs, const optional& rhs) noexcept + { + return rhs ? lhs >= *rhs : true; + } + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/parse_number.hpp b/include/jsoncons/detail/parse_number.hpp new file mode 100644 index 0000000..7417ff2 --- /dev/null +++ b/include/jsoncons/detail/parse_number.hpp @@ -0,0 +1,1133 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_PARSE_NUMBER_HPP +#define JSONCONS_DETAIL_PARSE_NUMBER_HPP + +#include +#include +#include +#include +#include +#include +#include // std::numeric_limits +#include // std::enable_if +#include +#include +#include +#include + +namespace jsoncons { namespace detail { + + enum class to_integer_errc : uint8_t {success=0, overflow, invalid_digit, invalid_number}; + + class to_integer_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/to_integer_unchecked"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case to_integer_errc::overflow: + return "Integer overflow"; + case to_integer_errc::invalid_digit: + return "Invalid digit"; + case to_integer_errc::invalid_number: + return "Invalid number"; + default: + return "Unknown to_integer_unchecked error"; + } + } + }; + + inline + const std::error_category& to_integer_error_category() + { + static to_integer_error_category_impl instance; + return instance; + } + + inline + std::error_code make_error_code(to_integer_errc e) + { + return std::error_code(static_cast(e),to_integer_error_category()); + } + +} // namespace detail +} // namespace jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace jsoncons { namespace detail { + +template +struct to_integer_result +{ + const CharT* ptr; + to_integer_errc ec; + constexpr to_integer_result(const CharT* ptr_) + : ptr(ptr_), ec(to_integer_errc()) + { + } + constexpr to_integer_result(const CharT* ptr_, to_integer_errc ec_) + : ptr(ptr_), ec(ec_) + { + } + + to_integer_result(const to_integer_result&) = default; + + to_integer_result& operator=(const to_integer_result&) = default; + + constexpr explicit operator bool() const noexcept + { + return ec == to_integer_errc(); + } + std::error_code error_code() const + { + return make_error_code(ec); + } +}; + +enum class integer_chars_format : uint8_t {decimal=1,hex}; +enum class integer_chars_state {initial,minus,integer,binary,octal,decimal,base16}; + +template +bool is_base10(const CharT* s, std::size_t length) +{ + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + for (;s < end; ++s) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + case '-': + state = integer_chars_state::minus; + break; + default: + return false; + } + break; + } + case integer_chars_state::minus: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + default: + return false; + } + break; + } + case integer_chars_state::decimal: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + break; + default: + return false; + } + break; + } + default: + break; + } + } + return state == integer_chars_state::decimal ? true : false; +} + +template +typename std::enable_if::is_specialized && !type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_decimal(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + if (++s == end) + { + return (++s == end) ? to_integer_result(s) : to_integer_result(s, to_integer_errc()); + } + else + { + return to_integer_result(s, to_integer_errc::invalid_digit); + } + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be decimal + state = integer_chars_state::decimal; + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::decimal: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result(s, to_integer_errc::invalid_number) : to_integer_result(s, to_integer_errc()); +} + +template +typename std::enable_if::is_specialized && type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_decimal(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned::type; + + U u; + auto ru = to_integer_decimal(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast(-((type_traits::integer_limits::lowest)()+T(1))) + U(1)) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(U(0) - u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast((type_traits::integer_limits::max)())) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } +} + +template +typename std::enable_if::is_specialized && !type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_base16(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + if (++s == end) + { + return (++s == end) ? to_integer_result(s) : to_integer_result(s, to_integer_errc()); + } + else + { + return to_integer_result(s, to_integer_errc::invalid_digit); + } + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be base16 + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + state = integer_chars_state::base16; + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::base16: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result(s, to_integer_errc::invalid_number) : to_integer_result(s, to_integer_errc()); +} + +template +typename std::enable_if::is_specialized && type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_base16(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned::type; + + U u; + auto ru = to_integer_base16(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast(-((type_traits::integer_limits::lowest)()+T(1))) + U(1)) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(U(0) - u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast((type_traits::integer_limits::max)())) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } +} + +template +typename std::enable_if::is_specialized && !type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + state = integer_chars_state::integer; // Could be binary, octal, hex + ++s; + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be decimal + state = integer_chars_state::decimal; + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::integer: + { + switch(*s) + { + case 'b':case 'B': + { + state = integer_chars_state::binary; + ++s; + break; + } + case 'x':case 'X': + { + state = integer_chars_state::base16; + ++s; + break; + } + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + { + state = integer_chars_state::octal; + break; + } + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::binary: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_2 = max_value / 2; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_2) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 2; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::octal: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_8 = max_value / 8; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_8) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 8; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::decimal: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::base16: + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result(s, to_integer_errc::invalid_number) : to_integer_result(s, to_integer_errc()); +} + +template +typename std::enable_if::is_specialized && type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned::type; + + U u; + auto ru = to_integer(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast(-((type_traits::integer_limits::lowest)()+T(1))) + U(1)) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(U(0) - u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast((type_traits::integer_limits::max)())) + { + return to_integer_result(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast(u); + return to_integer_result(ru.ptr, to_integer_errc()); + } + } +} + +template +typename std::enable_if::is_specialized,to_integer_result>::type +to_integer(const CharT* s, T& n) +{ + return to_integer(s, std::char_traits::length(s), n); +} + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template +typename std::enable_if::is_specialized && !type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_unchecked(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits::lowest)(); + static constexpr T min_value_div_10 = min_value / 10; + ++s; + for (; s < end; ++s) + { + T x = (T)*s - (T)('0'); + if (n < min_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n < min_value + x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = static_cast(*s) - static_cast('0'); + if (n > max_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result(s, to_integer_errc()); +} + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template +typename std::enable_if::is_specialized && type_traits::integer_limits::is_signed,to_integer_result>::type +to_integer_unchecked(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits::lowest)(); + static constexpr T min_value_div_10 = min_value / 10; + ++s; + for (; s < end; ++s) + { + T x = (T)*s - (T)('0'); + if (n < min_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n < min_value + x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = static_cast(*s) - static_cast('0'); + if (n > max_value_div_10) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result(s, to_integer_errc()); +} + +// base16_to_integer + +template +typename std::enable_if::is_specialized && type_traits::integer_limits::is_signed,to_integer_result>::type +base16_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits::lowest)(); + static constexpr T min_value_div_16 = min_value / 16; + ++s; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n < min_value_div_16) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 16; + if (n < min_value + x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result(s, to_integer_errc()); +} + +template +typename std::enable_if::is_specialized && !type_traits::integer_limits::is_signed,to_integer_result>::type +base16_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + const CharT* end = s + length; + + static constexpr T max_value = (type_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = *s; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result(s, to_integer_errc::overflow); + } + + n += x; + } + + return to_integer_result(s, to_integer_errc()); +} + + +#if defined(JSONCONS_HAS_STD_FROM_CHARS) + +class chars_to +{ +public: + + char get_decimal_point() const + { + return '.'; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t len) const + { + double val = 0; + const auto res = std::from_chars(s, s+len, val); + if (res.ec != std::errc()) + { + JSONCONS_THROW(json_runtime_error("Convert chars to double failed")); + } + return val; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t len) const + { + std::string input(len,'0'); + for (size_t i = 0; i < len; ++i) + { + input[i] = static_cast(s[i]); + } + + double val = 0; + const auto res = std::from_chars(input.data(), input.data() + len, val); + if (res.ec != std::errc()) + { + JSONCONS_THROW(json_runtime_error("Convert chars to double failed")); + } + return val; + } +}; +#elif defined(JSONCONS_HAS_MSC_STRTOD_L) + +class chars_to +{ +private: + _locale_t locale_; +public: + chars_to() + { + locale_ = _create_locale(LC_NUMERIC, "C"); + } + ~chars_to() noexcept + { + _free_locale(locale_); + } + + chars_to(const chars_to&) + { + locale_ = _create_locale(LC_NUMERIC, "C"); + } + + chars_to& operator=(const chars_to&) + { + // Don't assign locale + return *this; + } + + char get_decimal_point() const + { + return '.'; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = _strtod_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = _wcstod_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } +}; + +#elif defined(JSONCONS_HAS_STRTOLD_L) + +class chars_to +{ +private: + locale_t locale_; +public: + chars_to() + { + locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + } + ~chars_to() noexcept + { + freelocale(locale_); + } + + chars_to(const chars_to&) + { + locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + } + + chars_to& operator=(const chars_to&) + { + return *this; + } + + char get_decimal_point() const + { + return '.'; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t) const + { + char *end = nullptr; + double val = strtold_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = wcstold_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } +}; + +#else +class chars_to +{ +private: + std::vector buffer_; + char decimal_point_; +public: + chars_to() + : buffer_() + { + struct lconv * lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } + else + { + decimal_point_ = '.'; + } + buffer_.reserve(100); + } + + chars_to(const chars_to&) = default; + chars_to& operator=(const chars_to&) = default; + + char get_decimal_point() const + { + return decimal_point_; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t /*length*/) const + { + CharT *end = nullptr; + double val = strtod(s, &end); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } + + template + typename std::enable_if::value,double>::type + operator()(const CharT* s, std::size_t /*length*/) const + { + CharT *end = nullptr; + double val = wcstod(s, &end); + if (s == end) + { + JSONCONS_THROW(json_runtime_error("Convert string to double failed")); + } + return val; + } +}; +#endif + +}} + +#endif diff --git a/include/jsoncons/detail/span.hpp b/include/jsoncons/detail/span.hpp new file mode 100644 index 0000000..02c24ee --- /dev/null +++ b/include/jsoncons/detail/span.hpp @@ -0,0 +1,188 @@ +#ifndef JSONCONS_DETAIL_SPAN_HPP +#define JSONCONS_DETAIL_SPAN_HPP + +#include // std::swap +#include // std::addressof +#include // std::enable_if, std::true_type, std::false_type +#include +#include +#include +#include + +namespace jsoncons +{ +namespace detail +{ + constexpr std::size_t dynamic_extent = (std::numeric_limits::max)(); + + template< class T, std::size_t Extent = dynamic_extent> + class span; + + template + struct is_span : std::false_type{}; + + template< class T> + struct is_span> : std::true_type{}; + + template< + class T, + std::size_t Extent + > class span + { + public: + using element_type = T; + using value_type = typename std::remove_volatile::type>::type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + private: + pointer data_; + size_type size_; + public: + static constexpr std::size_t extent = Extent; + + constexpr span() noexcept + : data_(nullptr), size_(0) + { + } + constexpr span(pointer data, size_type size) + : data_(data), size_(size) + { + } + + template + constexpr span(C& c, + typename std::enable_if::value && !type_traits::is_std_array::value && type_traits::is_compatible_element::value && type_traits::has_data_and_size::value>::type* = 0) + : data_(c.data()), size_(c.size()) + { + } + + template + span(element_type (&arr)[N]) noexcept + : data_(std::addressof(arr[0])), size_(N) + { + } + + template + span(std::array& arr, + typename std::enable_if<(extent == dynamic_extent || extent == N)>::type* = 0) noexcept + : data_(arr.data()), size_(arr.size()) + { + } + + template + span(const std::array& arr, + typename std::enable_if<(extent == dynamic_extent || extent == N)>::type* = 0) noexcept + : data_(arr.data()), size_(arr.size()) + { + } + + template + constexpr span(const C& c, + typename std::enable_if::value && !type_traits::is_std_array::value && type_traits::is_compatible_element::value && type_traits::has_data_and_size::value>::type* = 0) + : data_(c.data()), size_(c.size()) + { + } + + template + constexpr span(const span& s, + typename std::enable_if<(N == dynamic_extent || N == extent) && std::is_convertible::value>::type* = 0) noexcept + : data_(s.data()), size_(s.size()) + { + } + + constexpr span(const span& other) noexcept = default; + + span& operator=( const span& other ) noexcept = default; + + constexpr pointer data() const noexcept + { + return data_; + } + + constexpr size_type size() const noexcept + { + return size_; + } + + constexpr bool empty() const noexcept + { + return size_ == 0; + } + + constexpr reference operator[](size_type index) const + { + return data_[index]; + } + + // iterator support + const_iterator begin() const noexcept + { + return data_; + } + const_iterator end() const noexcept + { + return data_ + size_; + } + const_iterator cbegin() const noexcept + { + return data_; + } + const_iterator cend() const noexcept + { + return data_ + size_; + } + const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(end()); + } + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(begin()); + } + + span + first(std::size_t count) const + { + JSONCONS_ASSERT(count <= size()); + + return span< element_type, dynamic_extent >( data(), count ); + } + + span + last(std::size_t count) const + { + JSONCONS_ASSERT(count <= size()); + + return span(data() + ( size() - count ), count); + } + + span + subspan(std::size_t offset, std::size_t count = dynamic_extent) const + { + //JSONCONS_ASSERT((offset <= size() && (count == dynamic_extent || (offset + count <= size())))); + + return span( + data() + offset, count == dynamic_extent ? size() - offset : count ); + } + }; + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/string_view.hpp b/include/jsoncons/detail/string_view.hpp new file mode 100644 index 0000000..fc568a1 --- /dev/null +++ b/include/jsoncons/detail/string_view.hpp @@ -0,0 +1,537 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_STRING_VIEW_HPP +#define JSONCONS_STRING_VIEW_HPP + +#include +#include +#include +#include +#include +#include // std::find, std::min, std::reverse +#include +#include +#include +#include +#include // std::basic_istream +#include + +namespace jsoncons { +namespace detail { + + template > + class basic_string_view + { + private: + const CharT* data_; + std::size_t length_; + public: + using value_type = CharT; + using const_reference = const CharT&; + using traits_type = Traits; + using size_type = std::size_t; + static constexpr size_type npos = size_type(-1); + using const_iterator = const CharT*; + using iterator = const CharT*; + using const_reverse_iterator = std::reverse_iterator; + + constexpr basic_string_view() noexcept + : data_(nullptr), length_(0) + { + } + constexpr basic_string_view(const CharT* data, std::size_t length) + : data_(data), length_(length) + { + } + + basic_string_view(const CharT* data) + : data_(data), length_(Traits::length(data)) + { + } + constexpr basic_string_view(const basic_string_view& other) noexcept = default; + + template + JSONCONS_CPP14_CONSTEXPR basic_string_view(const std::basic_string& s) noexcept + : data_(s.data()), length_(s.length()) + { + } + + JSONCONS_CPP14_CONSTEXPR basic_string_view& operator=( const basic_string_view& view ) noexcept + { + data_ = view.data(); + length_ = view.length(); + + return *this; + } + + template + explicit operator std::basic_string() const + { + return std::basic_string(data_,length_); + } + + // iterator support + const_iterator begin() const noexcept + { + return data_; + } + const_iterator end() const noexcept + { + return data_ + length_; + } + const_iterator cbegin() const noexcept + { + return data_; + } + const_iterator cend() const noexcept + { + return data_ + length_; + } + const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(end()); + } + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(begin()); + } + + // capacity + + std::size_t size() const + { + return length_; + } + + std::size_t length() const + { + return length_; + } + size_type max_size() const noexcept + { + return length_; + } + bool empty() const noexcept + { + return length_ == 0; + } + + // element access + + const_reference operator[](size_type pos) const + { + return data_[pos]; + } + + const_reference at(std::size_t pos) const + { + if (pos >= length_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds length")); + } + return data_[pos]; + } + + const_reference front() const + { + return data_[0]; + } + const_reference back() const + { + return data_[length_-1]; + } + + const CharT* data() const + { + return data_; + } + + // string operations + + basic_string_view substr(size_type pos, size_type n=npos) const + { + if (pos > length_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds size")); + } + if (n == npos || pos + n > length_) + { + n = length_ - pos; + } + return basic_string_view(data_ + pos, n); + } + + int compare(const basic_string_view& s) const noexcept + { + const int rc = Traits::compare(data_, s.data_, (std::min)(length_, s.length_)); + return rc != 0 ? rc : (length_ == s.length_ ? 0 : length_ < s.length_ ? -1 : 1); + } + + int compare(const CharT* data) const noexcept + { + const size_t length = Traits::length(data); + const int rc = Traits::compare(data_, data, (std::min)(length_, length)); + return rc != 0 ? rc : (length_ == length? 0 : length_ < length? -1 : 1); + } + + template + int compare(const std::basic_string& s) const noexcept + { + const int rc = Traits::compare(data_, s.data(), (std::min)(length_, s.length())); + return rc != 0 ? rc : (length_ == s.length() ? 0 : length_ < s.length() ? -1 : 1); + } + + size_type find(basic_string_view s, size_type pos = 0) const noexcept + { + if (pos > length_) + { + return npos; + } + if (s.length_ == 0) + { + return pos; + } + const_iterator it = std::search(cbegin() + pos, cend(), + s.cbegin(), s.cend(), Traits::eq); + return it == cend() ? npos : std::distance(cbegin(), it); + } + size_type find(CharT ch, size_type pos = 0) const noexcept + { + return find(basic_string_view(&ch, 1), pos); + } + size_type find(const CharT* s, size_type pos, size_type n) const noexcept + { + return find(basic_string_view(s, n), pos); + } + size_type find(const CharT* s, size_type pos = 0) const noexcept + { + return find(basic_string_view(s), pos); + } + + size_type rfind(basic_string_view s, size_type pos = npos) const noexcept + { + if (length_ < s.length_) + { + return npos; + } + if (pos > length_ - s.length_) + { + pos = length_ - s.length_; + } + if (s.length_ == 0) + { + return pos; + } + for (const CharT* p = data_ + pos; true; --p) + { + if (Traits::compare(p, s.data_, s.length_) == 0) + { + return p - data_; + } + if (p == data_) + { + return npos; + } + }; + } + size_type rfind(CharT ch, size_type pos = npos) const noexcept + { + return rfind(basic_string_view(&ch, 1), pos); + } + size_type rfind(const CharT* s, size_type pos, size_type n) const noexcept + { + return rfind(basic_string_view(s, n), pos); + } + size_type rfind(const CharT* s, size_type pos = npos) const noexcept + { + return rfind(basic_string_view(s), pos); + } + + size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept + { + if (pos >= length_ || s.length_ == 0) + { + return npos; + } + const_iterator it = std::find_first_of + (cbegin() + pos, cend(), s.cbegin(), s.cend(), Traits::eq); + return it == cend() ? npos : std::distance (cbegin(), it); + } + size_type find_first_of(CharT ch, size_type pos = 0) const noexcept + { + return find_first_of(basic_string_view(&ch, 1), pos); + } + size_type find_first_of(const CharT* s, size_type pos, size_type n) const noexcept + { + return find_first_of(basic_string_view(s, n), pos); + } + size_type find_first_of(const CharT* s, size_type pos = 0) const noexcept + { + return find_first_of(basic_string_view(s), pos); + } + + size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept + { + if (s.length_ == 0) + { + return npos; + } + if (pos >= length_) + { + pos = 0; + } + else + { + pos = length_ - (pos+1); + } + const_reverse_iterator it = std::find_first_of + (crbegin() + pos, crend(), s.cbegin(), s.cend(), Traits::eq); + return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it)); + } + size_type find_last_of(CharT ch, size_type pos = npos) const noexcept + { + return find_last_of(basic_string_view(&ch, 1), pos); + } + size_type find_last_of(const CharT* s, size_type pos, size_type n) const noexcept + { + return find_last_of(basic_string_view(s, n), pos); + } + size_type find_last_of(const CharT* s, size_type pos = npos) const noexcept + { + return find_last_of(basic_string_view(s), pos); + } + + size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept + { + if (pos >= length_) + return npos; + if (s.length_ == 0) + return pos; + + const_iterator it = cend(); + for (auto p = cbegin()+pos; p != cend(); ++p) + { + if (Traits::find(s.data_, s.length_, *p) == 0) + { + it = p; + break; + } + } + return it == cend() ? npos : std::distance (cbegin(), it); + } + size_type find_first_not_of(CharT ch, size_type pos = 0) const noexcept + { + return find_first_not_of(basic_string_view(&ch, 1), pos); + } + size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const noexcept + { + return find_first_not_of(basic_string_view(s, n), pos); + } + size_type find_first_not_of(const CharT* s, size_type pos = 0) const noexcept + { + return find_first_not_of(basic_string_view(s), pos); + } + + size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept + { + if (pos >= length_) + { + pos = length_ - 1; + } + if (s.length_ == 0) + { + return pos; + } + pos = length_ - (pos+1); + + const_iterator it = crend(); + for (auto p = crbegin()+pos; p != crend(); ++p) + { + if (Traits::find(s.data_, s.length_, *p) == 0) + { + it = p; + break; + } + } + return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it)); + } + size_type find_last_not_of(CharT ch, size_type pos = npos) const noexcept + { + return find_last_not_of(basic_string_view(&ch, 1), pos); + } + size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const noexcept + { + return find_last_not_of(basic_string_view(s, n), pos); + } + size_type find_last_not_of(const CharT* s, size_type pos = npos) const noexcept + { + return find_last_not_of(basic_string_view(s), pos); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_string_view& sv) + { + os.write(sv.data_,sv.length_); + return os; + } + }; + + // == + template + bool operator==(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + template + bool operator==(const basic_string_view& lhs, + const CharT* rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const CharT* lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + + // != + template + bool operator!=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + template + bool operator!=(const basic_string_view& lhs, + const CharT* rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const CharT* lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + + // <= + template + bool operator<=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) >= 0; + } + + // < + template + bool operator<(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) > 0; + } + + // >= + template + bool operator>=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) <= 0; + } + + // > + template + bool operator>(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const basic_string_view& lhs, + const std::basic_string& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const std::basic_string& lhs, + const basic_string_view& rhs) noexcept + { + return rhs.compare(lhs) < 0; + } + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/string_wrapper.hpp b/include/jsoncons/detail/string_wrapper.hpp new file mode 100644 index 0000000..88634b0 --- /dev/null +++ b/include/jsoncons/detail/string_wrapper.hpp @@ -0,0 +1,370 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_STRING_WRAPPER_HPP +#define JSONCONS_DETAIL_STRING_WRAPPER_HPP + +#include +#include +#include +#include +#include // std::memcpy +#include // std::allocator +#include + +namespace jsoncons { +namespace detail { + + // From boost 1_71 + template + T launder_cast(U* u) + { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + return std::launder(reinterpret_cast(u)); + #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 800 + return __builtin_launder(reinterpret_cast(u)); + #else + return reinterpret_cast(u); + #endif + } + + // string_wrapper + + template + class string_wrapper + { + public: + using char_type = CharT; + private: + struct str_base_t + { + Allocator alloc_; + + Allocator& get_allocator() + { + return alloc_; + } + + const Allocator& get_allocator() const + { + return alloc_; + } + + str_base_t(const Allocator& alloc) + : alloc_(alloc) + { + } + + ~str_base_t() noexcept = default; + }; + + struct str_t : public str_base_t + { + typedef typename std::allocator_traits::template rebind_alloc allocator_type; + using allocator_traits_type = std::allocator_traits; + using pointer = typename allocator_traits_type::pointer; + + pointer p_; + std::size_t length_; + + ~str_t() noexcept = default; + + const char_type* c_str() const { return type_traits::to_plain_pointer(p_); } + const char_type* data() const { return type_traits::to_plain_pointer(p_); } + std::size_t length() const { return length_; } + + str_t(const Allocator& alloc) + : str_base_t(alloc), p_(nullptr), length_(0) + { + + } + + str_t(const str_t&) = delete; + str_t& operator=(const str_t&) = delete; + + }; + + typedef typename std::allocator_traits::template rebind_alloc byte_allocator_type; + using byte_pointer = typename std::allocator_traits::pointer; + + typedef typename std::allocator_traits::template rebind_alloc string_allocator_type; + using string_pointer = typename std::allocator_traits::pointer; + + struct storage_t + { + str_t data; + char_type c[1]; + }; + typedef typename std::aligned_storage::type json_storage_kind; + + string_pointer ptr_; + public: + string_wrapper() = default; + + string_wrapper(string_pointer ptr) + : ptr_(ptr) + { + } + + string_wrapper(const char_type* data, std::size_t length, const Allocator& a) + { + ptr_ = create(data,length,a); + } + + string_wrapper(const string_wrapper& val) + { + ptr_ = create(val.data(),val.length(),val.get_allocator()); + } + + string_wrapper(const string_wrapper& val, const Allocator& a) + { + ptr_ = create(val.data(),val.length(),a); + } + + ~string_wrapper() noexcept + { + if (ptr_ != nullptr) + { + destroy(ptr_); + } + } + + void swap(string_wrapper& other) noexcept + { + std::swap(ptr_,other.ptr_); + } + + const char_type* data() const + { + return ptr_->data(); + } + + const char_type* c_str() const + { + return ptr_->c_str(); + } + + std::size_t length() const + { + return ptr_->length(); + } + + Allocator get_allocator() const + { + return ptr_->get_allocator(); + } + private: + static size_t aligned_size(std::size_t n) + { + return sizeof(json_storage_kind) + n; + } + + static string_pointer create(const char_type* s, std::size_t length, const Allocator& alloc) + { + std::size_t mem_size = aligned_size(length*sizeof(char_type)); + + byte_allocator_type byte_alloc(alloc); + byte_pointer ptr = byte_alloc.allocate(mem_size); + + char* storage = type_traits::to_plain_pointer(ptr); + str_t* ps = new(storage)str_t(byte_alloc); + + auto psa = launder_cast(storage); + + CharT* p = new(&psa->c)char_type[length + 1]; + std::memcpy(p, s, length*sizeof(char_type)); + p[length] = 0; + ps->p_ = std::pointer_traits::pointer_to(*p); + ps->length_ = length; + return std::pointer_traits::pointer_to(*ps); + } + + static void destroy(string_pointer ptr) + { + str_t* rawp = type_traits::to_plain_pointer(ptr); + + char* p = launder_cast(rawp); + + std::size_t mem_size = aligned_size(ptr->length_*sizeof(char_type)); + byte_allocator_type byte_alloc(ptr->get_allocator()); + byte_alloc.deallocate(p,mem_size); + } + }; + + // tagged_string_wrapper + + template + class tagged_string_wrapper + { + public: + using char_type = CharT; + private: + struct str_base_t + { + Allocator alloc_; + + Allocator& get_allocator() + { + return alloc_; + } + + const Allocator& get_allocator() const + { + return alloc_; + } + + str_base_t(const Allocator& alloc) + : alloc_(alloc) + { + } + + ~str_base_t() noexcept = default; + }; + + struct str_t : public str_base_t + { + typedef typename std::allocator_traits::template rebind_alloc allocator_type; + using allocator_traits_type = std::allocator_traits; + using pointer = typename allocator_traits_type::pointer; + + pointer p_; + std::size_t length_; + uint64_t tag_; + + ~str_t() noexcept = default; + + const char_type* c_str() const { return type_traits::to_plain_pointer(p_); } + const char_type* data() const { return type_traits::to_plain_pointer(p_); } + std::size_t length() const { return length_; } + uint64_t tag() const { return tag_; } + + str_t(uint64_t tag, const Allocator& alloc) + : str_base_t(alloc), p_(nullptr), length_(0), tag_(tag) + { + + } + + str_t(const str_t&) = delete; + str_t& operator=(const str_t&) = delete; + + }; + + typedef typename std::allocator_traits::template rebind_alloc byte_allocator_type; + using byte_pointer = typename std::allocator_traits::pointer; + + typedef typename std::allocator_traits::template rebind_alloc string_allocator_type; + using string_pointer = typename std::allocator_traits::pointer; + + struct storage_t + { + str_t data; + char_type c[1]; + }; + typedef typename std::aligned_storage::type json_storage_kind; + + string_pointer ptr_; + public: + tagged_string_wrapper() = default; + + tagged_string_wrapper(string_pointer ptr) + : ptr_(ptr) + { + } + + tagged_string_wrapper(const char_type* data, std::size_t length, uint64_t tag, const Allocator& alloc) + { + ptr_ = create(data, length, tag, alloc); + } + + tagged_string_wrapper(const tagged_string_wrapper& val) + { + ptr_ = create(val.data(), val.length(), val.tag(), val.get_allocator()); + } + + tagged_string_wrapper(const tagged_string_wrapper& val, const Allocator& alloc) + { + ptr_ = create(val.data(), val.length(), val.tag(), alloc); + } + + ~tagged_string_wrapper() noexcept + { + if (ptr_ != nullptr) + { + destroy(ptr_); + } + } + + void swap(tagged_string_wrapper& other) noexcept + { + std::swap(ptr_,other.ptr_); + } + + const char_type* data() const + { + return ptr_->data(); + } + + const char_type* c_str() const + { + return ptr_->c_str(); + } + + std::size_t length() const + { + return ptr_->length(); + } + + uint64_t tag() const + { + return ptr_->tag(); + } + + Allocator get_allocator() const + { + return ptr_->get_allocator(); + } + private: + static size_t aligned_size(std::size_t n) + { + return sizeof(json_storage_kind) + n; + } + + static string_pointer create(const char_type* s, std::size_t length, uint64_t tag, const Allocator& alloc) + { + std::size_t mem_size = aligned_size(length*sizeof(char_type)); + + byte_allocator_type byte_alloc(alloc); + byte_pointer ptr = byte_alloc.allocate(mem_size); + + char* storage = type_traits::to_plain_pointer(ptr); + str_t* ps = new(storage)str_t(tag, byte_alloc); + + auto psa = launder_cast(storage); + + CharT* p = new(&psa->c)char_type[length + 1]; + std::memcpy(p, s, length*sizeof(char_type)); + p[length] = 0; + ps->p_ = std::pointer_traits::pointer_to(*p); + ps->length_ = length; + return std::pointer_traits::pointer_to(*ps); + } + + static void destroy(string_pointer ptr) + { + str_t* rawp = type_traits::to_plain_pointer(ptr); + + char* p = launder_cast(rawp); + + std::size_t mem_size = aligned_size(ptr->length_*sizeof(char_type)); + byte_allocator_type byte_alloc(ptr->get_allocator()); + byte_alloc.deallocate(p,mem_size); + } + }; + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/detail/write_number.hpp b/include/jsoncons/detail/write_number.hpp new file mode 100644 index 0000000..2613467 --- /dev/null +++ b/include/jsoncons/detail/write_number.hpp @@ -0,0 +1,567 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_WRITE_NUMBER_HPP +#define JSONCONS_DETAIL_WRITE_NUMBER_HPP + +#include +#include +#include +#include +#include // std::numeric_limits +#include +#include // snprintf +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace detail { + + inline + char to_hex_character(uint8_t c) + { + return (char)((c < 10) ? ('0' + c) : ('A' - 10 + c)); + } + + // from_integer + + template + typename std::enable_if::value,std::size_t>::type + from_integer(Integer value, Result& result) + { + using char_type = typename Result::value_type; + + char_type buf[255]; + char_type *p = buf; + const char_type* last = buf+255; + + bool is_negative = value < 0; + + if (value < 0) + { + do + { + *p++ = static_cast(48 - (value % 10)); + } + while ((value /= 10) && (p < last)); + } + else + { + + do + { + *p++ = static_cast(48 + value % 10); + } + while ((value /= 10) && (p < last)); + } + JSONCONS_ASSERT(p != last); + + std::size_t count = (p - buf); + if (is_negative) + { + result.push_back('-'); + ++count; + } + while (--p >= buf) + { + result.push_back(*p); + } + + return count; + } + + // integer_to_string_hex + + template + typename std::enable_if::value,std::size_t>::type + integer_to_string_hex(Integer value, Result& result) + { + using char_type = typename Result::value_type; + + char_type buf[255]; + char_type *p = buf; + const char_type* last = buf+255; + + bool is_negative = value < 0; + + if (value < 0) + { + do + { + *p++ = to_hex_character(0-(value % 16)); + } + while ((value /= 16) && (p < last)); + } + else + { + + do + { + *p++ = to_hex_character(value % 16); + } + while ((value /= 16) && (p < last)); + } + JSONCONS_ASSERT(p != last); + + std::size_t count = (p - buf); + if (is_negative) + { + result.push_back('-'); + ++count; + } + while (--p >= buf) + { + result.push_back(*p); + } + + return count; + } + + // write_double + + // fast exponent + template + void fill_exponent(int K, Result& result) + { + if (K < 0) + { + result.push_back('-'); + K = -K; + } + else + { + result.push_back('+'); // compatibility with sprintf + } + + if (K < 10) + { + result.push_back('0'); // compatibility with sprintf + result.push_back((char)('0' + K)); + } + else if (K < 100) + { + result.push_back((char)('0' + K / 10)); K %= 10; + result.push_back((char)('0' + K)); + } + else if (K < 1000) + { + result.push_back((char)('0' + K / 100)); K %= 100; + result.push_back((char)('0' + K / 10)); K %= 10; + result.push_back((char)('0' + K)); + } + else + { + jsoncons::detail::from_integer(K, result); + } + } + + template + void prettify_string(const char *buffer, std::size_t length, int k, int min_exp, int max_exp, Result& result) + { + int nb_digits = (int)length; + int offset; + /* v = buffer * 10^k + kk is such that 10^(kk-1) <= v < 10^kk + this way kk gives the position of the decimal point. + */ + int kk = nb_digits + k; + + if (nb_digits <= kk && kk <= max_exp) + { + /* the first digits are already in. Add some 0s and call it a day. */ + /* the max_exp is a personal choice. Only 16 digits could possibly be relevant. + * Basically we want to print 12340000000 rather than 1234.0e7 or 1.234e10 */ + for (int i = 0; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + for (int i = nb_digits; i < kk; ++i) + { + result.push_back('0'); + } + result.push_back('.'); + result.push_back('0'); + } + else if (0 < kk && kk <= max_exp) + { + /* comma number. Just insert a '.' at the correct location. */ + for (int i = 0; i < kk; ++i) + { + result.push_back(buffer[i]); + } + result.push_back('.'); + for (int i = kk; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + } + else if (min_exp < kk && kk <= 0) + { + offset = 2 - kk; + + result.push_back('0'); + result.push_back('.'); + for (int i = 2; i < offset; ++i) + result.push_back('0'); + for (int i = 0; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + } + else if (nb_digits == 1) + { + result.push_back(buffer[0]); + result.push_back('e'); + fill_exponent(kk - 1, result); + } + else + { + result.push_back(buffer[0]); + result.push_back('.'); + for (int i = 1; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + result.push_back('e'); + fill_exponent(kk - 1, result); + } + } + + template + void dump_buffer(const char *buffer, std::size_t length, char decimal_point, Result& result) + { + const char *sbeg = buffer; + const char *send = sbeg + length; + + if (sbeg != send) + { + bool needs_dot = true; + for (const char* q = sbeg; q < send; ++q) + { + switch (*q) + { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + result.push_back(*q); + break; + case 'e': + case 'E': + result.push_back('e'); + needs_dot = false; + break; + default: + if (*q == decimal_point) + { + needs_dot = false; + result.push_back('.'); + } + break; + } + } + if (needs_dot) + { + result.push_back('.'); + result.push_back('0'); + needs_dot = true; + } + } + } + + template + bool dtoa_scientific(double val, char decimal_point, Result& result) + { + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + jsoncons::detail::chars_to to_double_; + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*e", precision, val); + if (length < 0) + { + return false; + } + if (to_double_(buffer, sizeof(buffer)) != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*e", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, static_cast(length), decimal_point, result); + return true; + } + + template + bool dtoa_general(double val, char decimal_point, Result& result, std::false_type) + { + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + jsoncons::detail::chars_to to_double_; + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*g", precision, val); + if (length < 0) + { + return false; + } + if (to_double_(buffer, sizeof(buffer)) != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*g", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, length, decimal_point, result); + return true; + } + + template + bool dtoa_general(double v, char decimal_point, Result& result, std::true_type) + { + if (v == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + int length = 0; + int k; + + char buffer[100]; + + double u = std::signbit(v) ? -v : v; + if (jsoncons::detail::grisu3(u, buffer, &length, &k)) + { + if (std::signbit(v)) + { + result.push_back('-'); + } + // min exp: -4 is consistent with sprintf + // max exp: std::numeric_limits::max_digits10 + jsoncons::detail::prettify_string(buffer, length, k, -4, std::numeric_limits::max_digits10, result); + return true; + } + else + { + return dtoa_general(v, decimal_point, result, std::false_type()); + } + } + + template + bool dtoa_fixed(double val, char decimal_point, Result& result, std::false_type) + { + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + jsoncons::detail::chars_to to_double_; + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*f", precision, val); + if (length < 0) + { + return false; + } + if (to_double_(buffer, sizeof(buffer)) != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*f", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, length, decimal_point, result); + return true; + } + + template + bool dtoa_fixed(double v, char decimal_point, Result& result, std::true_type) + { + if (v == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + int length = 0; + int k; + + char buffer[100]; + + double u = std::signbit(v) ? -v : v; + if (jsoncons::detail::grisu3(u, buffer, &length, &k)) + { + if (std::signbit(v)) + { + result.push_back('-'); + } + jsoncons::detail::prettify_string(buffer, length, k, std::numeric_limits::lowest(), (std::numeric_limits::max)(), result); + return true; + } + else + { + return dtoa_fixed(v, decimal_point, result, std::false_type()); + } + } + + template + bool dtoa_fixed(double v, char decimal_point, Result& result) + { + return dtoa_fixed(v, decimal_point, result, std::integral_constant::is_iec559>()); + } + + template + bool dtoa_general(double v, char decimal_point, Result& result) + { + return dtoa_general(v, decimal_point, result, std::integral_constant::is_iec559>()); + } + + class write_double + { + private: + chars_to to_double_; + float_chars_format float_format_; + int precision_; + char decimal_point_; + public: + write_double(float_chars_format float_format, int precision) + : float_format_(float_format), precision_(precision), decimal_point_('.') + { + #if !defined(JSONCONS_NO_LOCALECONV) + struct lconv *lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } + #endif + } + write_double(const write_double&) = default; + + write_double& operator=(const write_double&) = default; + + template + std::size_t operator()(double val, Result& result) + { + std::size_t count = 0; + + char number_buffer[200]; + int length = 0; + + switch (float_format_) + { + case float_chars_format::fixed: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*f", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_fixed(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + } + break; + case float_chars_format::scientific: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*e", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_scientific(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + } + break; + case float_chars_format::general: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*g", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_general(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + break; + } + default: + JSONCONS_THROW(json_runtime_error("write_double failed.")); + break; + } + return count; + } + }; + +} // namespace detail +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/encode_json.hpp b/include/jsoncons/encode_json.hpp new file mode 100644 index 0000000..706dc44 --- /dev/null +++ b/include/jsoncons/encode_json.hpp @@ -0,0 +1,315 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_ENCODE_JSON_HPP +#define JSONCONS_ENCODE_JSON_HPP + +#include +#include +#include +#include +#include // std::basic_istream +#include +#include + +namespace jsoncons { + + // to string + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json(const T& val, + Container& s, + const basic_json_encode_options& options = + basic_json_encode_options()) + { + using char_type = typename Container::value_type; + + basic_compact_json_encoder> encoder(s, options); + val.dump(encoder); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json(const T& val, + Container& s, + const basic_json_encode_options& options = basic_json_encode_options()) + { + using char_type = typename Container::value_type; + + basic_compact_json_encoder> encoder(s, options); + encode_json(val, encoder); + } + + // to stream + + template + typename std::enable_if::value>::type + encode_json(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) + { + basic_compact_json_encoder encoder(os, options); + val.dump(encoder); + } + + template + typename std::enable_if::value>::type + encode_json(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) + { + basic_compact_json_encoder encoder(os, options); + encode_json(val, encoder); + } + + // encode_json_pretty + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json_pretty(const T& val, + Container& s, + const basic_json_encode_options& options = basic_json_encode_options()) + { + using char_type = typename Container::value_type; + + basic_json_encoder> encoder(s, options); + val.dump(encoder); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json_pretty(const T& val, + Container& s, + const basic_json_encode_options& options = basic_json_encode_options()) + { + using char_type = typename Container::value_type; + basic_json_encoder> encoder(s, options); + encode_json(val, encoder); + } + + template + typename std::enable_if::value>::type + encode_json_pretty(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) + { + basic_json_encoder encoder(os, options); + val.dump(encoder); + } + + template + typename std::enable_if::value>::type + encode_json_pretty(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options = basic_json_encode_options()) + { + basic_json_encoder encoder(os, options); + encode_json(val, encoder); + } + + template + void encode_json(const T& val, basic_json_visitor& encoder) + { + std::error_code ec; + encode_traits::encode(val, encoder, basic_json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + encoder.flush(); + } + + template + void encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& s, + indenting line_indent = indenting::no_indent) + { + encode_json(temp_allocator_arg, temp_alloc, val, s, basic_json_encode_options(), line_indent); + } + +// legacy + + template + void encode_json(const T& val, Container& s, indenting line_indent) + { + if (line_indent == indenting::indent) + { + encode_json_pretty(val,s); + } + else + { + encode_json(val,s); + } + } + + template + void encode_json(const T& val, + Container& s, + const basic_json_encode_options& options, + indenting line_indent) + { + if (line_indent == indenting::indent) + { + encode_json_pretty(val,s,options); + } + else + { + encode_json(val,s,options); + } + } + + template + void encode_json(const T& val, + std::basic_ostream& os, + indenting line_indent) + { + if (line_indent == indenting::indent) + { + encode_json_pretty(val, os); + } + else + { + encode_json(val, os); + } + } + + template + void encode_json(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options, + indenting line_indent) + { + if (line_indent == indenting::indent) + { + encode_json_pretty(val, os, options); + } + else + { + encode_json(val, os, options); + } + } + +//end legacy + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& s, + const basic_json_encode_options& options, + indenting line_indent = indenting::no_indent) + { + using char_type = typename Container::value_type; + if (line_indent == indenting::indent) + { + basic_json_encoder,TempAllocator> encoder(s, options, temp_alloc); + val.dump(encoder); + } + else + { + basic_compact_json_encoder,TempAllocator> encoder(s, options, temp_alloc); + val.dump(encoder); + } + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& s, + const basic_json_encode_options& options, + indenting line_indent) + { + using char_type = typename Container::value_type; + if (line_indent == indenting::indent) + { + basic_json_encoder,TempAllocator> encoder(s, options, temp_alloc); + encode_json(temp_allocator_arg, temp_alloc, val, encoder); + } + else + { + basic_compact_json_encoder,TempAllocator> encoder(s, options, temp_alloc); + encode_json(temp_allocator_arg, temp_alloc, val, encoder); + } + } + + template + void encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::basic_ostream& os, + indenting line_indent = indenting::no_indent) + { + encode_json(temp_allocator_arg, temp_alloc, val, os, basic_json_encode_options(), line_indent); + } + + template + typename std::enable_if::value>::type + encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options, + indenting line_indent = indenting::no_indent) + { + if (line_indent == indenting::indent) + { + basic_json_encoder,TempAllocator> encoder(os, options, temp_alloc); + val.dump(encoder); + } + else + { + basic_compact_json_encoder,TempAllocator> encoder(os, options, temp_alloc); + val.dump(encoder); + } + } + + template + typename std::enable_if::value>::type + encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options, + indenting line_indent) + { + if (line_indent == indenting::indent) + { + basic_json_encoder encoder(os, options); + encode_json(temp_allocator_arg, temp_alloc, val, encoder); + } + else + { + basic_compact_json_encoder encoder(os, options); + encode_json(temp_allocator_arg, temp_alloc, val, encoder); + } + } + + template + typename std::enable_if::value>::type + encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + basic_json proto(temp_alloc); + encode_traits::encode(val, encoder, proto, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + encoder.flush(); + } + +} // jsoncons + +#endif + diff --git a/include/jsoncons/encode_traits.hpp b/include/jsoncons/encode_traits.hpp new file mode 100644 index 0000000..ae981af --- /dev/null +++ b/include/jsoncons/encode_traits.hpp @@ -0,0 +1,378 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_ENCODE_TRAITS_HPP +#define JSONCONS_ENCODE_TRAITS_HPP + +#include +#include +#include +#include +#include // std::enable_if, std::true_type, std::false_type +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + // encode_traits + + template + struct encode_traits + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encode(std::integral_constant::value>(), + val, encoder, proto, ec); + } + private: + template + static void encode(std::true_type, + const T& val, + basic_json_visitor& encoder, + const Json& /*proto*/, + std::error_code& ec) + { + auto j = json_type_traits::to_json(val); + j.dump(encoder, ec); + } + template + static void encode(std::false_type, + const T& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + auto j = json_type_traits::to_json(val, proto.get_allocator()); + j.dump(encoder, ec); + } + }; + + // specializations + + // bool + template + struct encode_traits::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.bool_value(val,semantic_tag::none,ser_context(),ec); + } + }; + + // uint + template + struct encode_traits::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.uint64_value(val,semantic_tag::none,ser_context(),ec); + } + }; + + // int + template + struct encode_traits::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.int64_value(val,semantic_tag::none,ser_context(),ec); + } + }; + + // float or double + template + struct encode_traits::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.double_value(val,semantic_tag::none,ser_context(),ec); + } + }; + + // string + template + struct encode_traits::value && + std::is_same::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.string_value(val,semantic_tag::none,ser_context(),ec); + } + }; + template + struct encode_traits::value && + !std::is_same::value + >::type> + { + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + std::basic_string s; + unicode_traits::convert(val.data(), val.size(), s); + encoder.string_value(s,semantic_tag::none,ser_context(),ec); + } + }; + + // std::pair + + template + struct encode_traits, CharT> + { + using value_type = std::pair; + + template + static void encode(const value_type& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encoder.begin_array(2,semantic_tag::none,ser_context(),ec); + if (ec) return; + encode_traits::encode(val.first, encoder, proto, ec); + if (ec) return; + encode_traits::encode(val.second, encoder, proto, ec); + if (ec) return; + encoder.end_array(ser_context(),ec); + } + }; + + // std::tuple + + namespace detail + { + template + struct json_serialize_tuple_helper + { + using char_type = typename Json::char_type; + using element_type = typename std::tuple_element::type; + using next = json_serialize_tuple_helper; + + static void encode(const Tuple& tuple, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encode_traits::encode(std::get(tuple), encoder, proto, ec); + if (ec) return; + next::encode(tuple, encoder, proto, ec); + } + }; + + template + struct json_serialize_tuple_helper<0, Size, Json, Tuple> + { + using char_type = typename Json::char_type; + static void encode(const Tuple&, + basic_json_visitor&, + const Json&, + std::error_code&) + { + } + }; + } // namespace detail + + + template + struct encode_traits, CharT> + { + using value_type = std::tuple; + static constexpr std::size_t size = sizeof...(E); + + template + static void encode(const value_type& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + using helper = jsoncons::detail::json_serialize_tuple_helper>; + encoder.begin_array(size,semantic_tag::none,ser_context(),ec); + if (ec) return; + helper::encode(val, encoder, proto, ec); + if (ec) return; + encoder.end_array(ser_context(),ec); + } + }; + + // vector like + template + struct encode_traits::value && + type_traits::is_list_like::value && + !type_traits::is_typed_array::value + >::type> + { + using value_type = typename T::value_type; + + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec); + if (ec) return; + for (auto it = std::begin(val); it != std::end(val); ++it) + { + encode_traits::encode(*it, encoder, proto, ec); + if (ec) return; + } + encoder.end_array(ser_context(), ec); + } + }; + + template + struct encode_traits::value && + type_traits::is_list_like::value && + type_traits::is_typed_array::value + >::type> + { + using value_type = typename T::value_type; + + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json&, + std::error_code& ec) + { + encoder.typed_array(jsoncons::span(val), semantic_tag::none, ser_context(), ec); + } + }; + + // std::array + + template + struct encode_traits,CharT> + { + using value_type = typename std::array::value_type; + + template + static void encode(const std::array& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec); + if (ec) return; + for (auto it = std::begin(val); it != std::end(val); ++it) + { + encode_traits::encode(*it, encoder, proto, ec); + if (ec) return; + } + encoder.end_array(ser_context(),ec); + } + }; + + // map like + + template + struct encode_traits::value && + type_traits::is_map_like::value && + type_traits::is_constructible_from_const_pointer_and_size::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec); + if (ec) return; + for (auto it = std::begin(val); it != std::end(val); ++it) + { + encoder.key(it->first); + encode_traits::encode(it->second, encoder, proto, ec); + if (ec) return; + } + encoder.end_object(ser_context(), ec); + if (ec) return; + } + }; + + template + struct encode_traits::value && + type_traits::is_map_like::value && + std::is_integral::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static void encode(const T& val, + basic_json_visitor& encoder, + const Json& proto, + std::error_code& ec) + { + encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec); + if (ec) return; + for (auto it = std::begin(val); it != std::end(val); ++it) + { + std::basic_string s; + jsoncons::detail::from_integer(it->first,s); + encoder.key(s); + encode_traits::encode(it->second, encoder, proto, ec); + if (ec) return; + } + encoder.end_object(ser_context(), ec); + if (ec) return; + } + }; + +} // jsoncons + +#endif + diff --git a/include/jsoncons/json.hpp b/include/jsoncons/json.hpp new file mode 100644 index 0000000..8362e77 --- /dev/null +++ b/include/jsoncons/json.hpp @@ -0,0 +1,18 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_HPP +#define JSONCONS_JSON_HPP + +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/include/jsoncons/json_array.hpp b/include/jsoncons/json_array.hpp new file mode 100644 index 0000000..5877f4d --- /dev/null +++ b/include/jsoncons/json_array.hpp @@ -0,0 +1,324 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_ARRAY_HPP +#define JSONCONS_JSON_ARRAY_HPP + +#include +#include +#include +#include +#include // std::sort, std::stable_sort, std::lower_bound, std::unique +#include +#include +#include // std::iterator_traits +#include // std::allocator +#include // std::move +#include // assert +#include // std::enable_if +#include +#include + +namespace jsoncons { + + // json_array + + template class SequenceContainer = std::vector> + class json_array : public allocator_holder + { + public: + using allocator_type = typename Json::allocator_type; + using value_type = Json; + private: + using value_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using value_container_type = SequenceContainer; + value_container_type elements_; + public: + using iterator = typename value_container_type::iterator; + using const_iterator = typename value_container_type::const_iterator; + using reference = typename std::iterator_traits::reference; + using const_reference = typename std::iterator_traits::reference; + + using allocator_holder::get_allocator; + + json_array() + { + } + + explicit json_array(const allocator_type& alloc) + : allocator_holder(alloc), + elements_(value_allocator_type(alloc)) + { + } + + explicit json_array(std::size_t n, + const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + elements_(n,Json(),value_allocator_type(alloc)) + { + } + + explicit json_array(std::size_t n, + const Json& value, + const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + elements_(n,value,value_allocator_type(alloc)) + { + } + + template + json_array(InputIterator begin, InputIterator end, const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + elements_(begin,end,value_allocator_type(alloc)) + { + } + json_array(const json_array& val) + : allocator_holder(val.get_allocator()), + elements_(val.elements_) + { + } + json_array(const json_array& val, const allocator_type& alloc) + : allocator_holder(alloc), + elements_(val.elements_,value_allocator_type(alloc)) + { + } + + json_array(json_array&& val) noexcept + : allocator_holder(val.get_allocator()), + elements_(std::move(val.elements_)) + { + } + json_array(json_array&& val, const allocator_type& alloc) + : allocator_holder(alloc), + elements_(std::move(val.elements_),value_allocator_type(alloc)) + { + } + + json_array(const std::initializer_list& init, + const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + elements_(init,value_allocator_type(alloc)) + { + } + ~json_array() noexcept + { + flatten_and_destroy(); + } + + reference back() + { + return elements_.back(); + } + + const_reference back() const + { + return elements_.back(); + } + + void pop_back() + { + elements_.pop_back(); + } + + bool empty() const + { + return elements_.empty(); + } + + void swap(json_array& val) noexcept + { + elements_.swap(val.elements_); + } + + std::size_t size() const {return elements_.size();} + + std::size_t capacity() const {return elements_.capacity();} + + void clear() {elements_.clear();} + + void shrink_to_fit() + { + for (std::size_t i = 0; i < elements_.size(); ++i) + { + elements_[i].shrink_to_fit(); + } + elements_.shrink_to_fit(); + } + + void reserve(std::size_t n) {elements_.reserve(n);} + + void resize(std::size_t n) {elements_.resize(n);} + + void resize(std::size_t n, const Json& val) {elements_.resize(n,val);} + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use erase(const_iterator, const_iterator)") + void remove_range(std::size_t from_index, std::size_t to_index) + { + JSONCONS_ASSERT(from_index <= to_index); + JSONCONS_ASSERT(to_index <= elements_.size()); + elements_.erase(elements_.cbegin()+from_index,elements_.cbegin()+to_index); + } + #endif + + iterator erase(const_iterator pos) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.erase(it); + #else + return elements_.erase(pos); + #endif + } + + iterator erase(const_iterator first, const_iterator last) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it1 = elements_.begin() + (first - elements_.begin()); + iterator it2 = elements_.begin() + (last - elements_.begin()); + return elements_.erase(it1,it2); + #else + return elements_.erase(first,last); + #endif + } + + Json& operator[](std::size_t i) {return elements_[i];} + + const Json& operator[](std::size_t i) const {return elements_[i];} + + // push_back + + template + typename std::enable_if::value,void>::type + push_back(T&& value) + { + elements_.emplace_back(std::forward(value)); + } + + template + typename std::enable_if::value,void>::type + push_back(T&& value) + { + elements_.emplace_back(std::forward(value),get_allocator()); + } + + template + typename std::enable_if::value,iterator>::type + insert(const_iterator pos, T&& value) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(value)); + #else + return elements_.emplace(pos, std::forward(value)); + #endif + } + template + typename std::enable_if::value,iterator>::type + insert(const_iterator pos, T&& value) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(value), get_allocator()); + #else + return elements_.emplace(pos, std::forward(value), get_allocator()); + #endif + } + + template + iterator insert(const_iterator pos, InputIt first, InputIt last) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = elements_.begin() + (pos - elements_.begin()); + elements_.insert(it, first, last); + return first == last ? it : it + 1; + #else + return elements_.insert(pos, first, last); + #endif + } + + template + typename std::enable_if::value,iterator>::type + emplace(const_iterator pos, Args&&... args) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(args)...); + #else + return elements_.emplace(pos, std::forward(args)...); + #endif + } + + template + Json& emplace_back(Args&&... args) + { + elements_.emplace_back(std::forward(args)...); + return elements_.back(); + } + + iterator begin() {return elements_.begin();} + + iterator end() {return elements_.end();} + + const_iterator begin() const {return elements_.begin();} + + const_iterator end() const {return elements_.end();} + + bool operator==(const json_array& rhs) const noexcept + { + return elements_ == rhs.elements_; + } + + bool operator<(const json_array& rhs) const noexcept + { + return elements_ < rhs.elements_; + } + private: + + json_array& operator=(const json_array&) = delete; + + void flatten_and_destroy() noexcept + { + while (!elements_.empty()) + { + value_type current = std::move(elements_.back()); + elements_.pop_back(); + switch (current.storage_kind()) + { + case json_storage_kind::array_value: + { + for (auto&& item : current.array_range()) + { + if (item.size() > 0) // non-empty object or array + { + elements_.push_back(std::move(item)); + } + } + current.clear(); + break; + } + case json_storage_kind::object_value: + { + for (auto&& kv : current.object_range()) + { + if (kv.value().size() > 0) // non-empty object or array + { + elements_.push_back(std::move(kv.value())); + } + } + current.clear(); + break; + } + default: + break; + } + } + } + }; + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/json_content_handler.hpp b/include/jsoncons/json_content_handler.hpp new file mode 100644 index 0000000..edec19e --- /dev/null +++ b/include/jsoncons/json_content_handler.hpp @@ -0,0 +1,12 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_CONTENT_HANDLER_HPP +#define JSONCONS_JSON_CONTENT_HANDLER_HPP + +#include + +#endif diff --git a/include/jsoncons/json_cursor.hpp b/include/jsoncons/json_cursor.hpp new file mode 100644 index 0000000..a30c351 --- /dev/null +++ b/include/jsoncons/json_cursor.hpp @@ -0,0 +1,448 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_CURSOR_HPP +#define JSONCONS_JSON_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +template,class Allocator=std::allocator> +class basic_json_cursor : public basic_staj_cursor, private virtual ser_context +{ +public: + using source_type = Source; + using char_type = CharT; + using allocator_type = Allocator; + using string_view_type = jsoncons::basic_string_view; +private: + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + static constexpr size_t default_max_buffer_size = 16384; + + json_source_adaptor source_; + basic_json_parser parser_; + basic_staj_visitor cursor_visitor_; + bool done_; + + // Noncopyable and nonmoveable + basic_json_cursor(const basic_json_cursor&) = delete; + basic_json_cursor& operator=(const basic_json_cursor&) = delete; + +public: + + // Constructors that throw parse exceptions + + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing(), + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all), + done_(false) + { + if (!done()) + { + next(); + } + } + + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options = basic_json_decode_options(), + std::function err_handler = default_json_parsing(), + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options, err_handler, alloc), + cursor_visitor_(accept_all), + done_(false) + { + initialize_with_string_view(std::forward(source)); + } + + + // Constructors that set parse error codes + template + basic_json_cursor(Sourceable&& source, + std::error_code& ec) + : basic_json_cursor(std::allocator_arg, Allocator(), + std::forward(source), + basic_json_decode_options(), + default_json_parsing(), + ec) + { + } + + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options, + std::error_code& ec) + : basic_json_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + default_json_parsing(), + ec) + { + } + + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + std::error_code& ec) + : basic_json_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + err_handler, + ec) + { + } + + template + basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all), + done_(false) + { + if (!done()) + { + next(ec); + } + } + + template + basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options, err_handler, alloc), + cursor_visitor_(accept_all), + done_(false) + { + initialize_with_string_view(std::forward(source), ec); + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + done_ = false; + if (!done()) + { + next(); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source) + { + source_ = std::forward(source); + parser_.reinitialize(); + cursor_visitor_.reset(); + done_ = false; + if (!done()) + { + next(); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source) + { + source_ = {}; + parser_.reinitialize(); + cursor_visitor_.reset(); + done_ = false; + initialize_with_string_view(std::forward(source)); + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + done_ = false; + if (!done()) + { + next(ec); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source, std::error_code& ec) + { + source_ = std::forward(source); + parser_.reinitialize(); + cursor_visitor_.reset(); + done_ = false; + if (!done()) + { + next(ec); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source, std::error_code& ec) + { + source_ = {}; + parser_.reinitialize(); + done_ = false; + initialize_with_string_view(std::forward(source), ec); + } + + bool done() const override + { + return parser_.done() || done_; + } + + const basic_staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + const ser_context& context() const override + { + return *this; + } + + void check_done(std::error_code& ec) + { + if (source_.is_error()) + { + ec = json_errc::source_error; + return; + } + if (source_.eof()) + { + parser_.check_done(ec); + if (ec) return; + } + else + { + do + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + if (!parser_.source_exhausted()) + { + parser_.check_done(ec); + if (ec) return; + } + } + while (!eof()); + } + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + basic_staj_filter_view operator|(basic_json_cursor& cursor, + std::function&, const ser_context&)> pred) + { + return basic_staj_filter_view(cursor, pred); + } + +private: + + static bool accept_all(const basic_staj_event&, const ser_context&) + { + return true; + } + + void initialize_with_string_view(string_view_type sv) + { + auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column())); + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + if (!done()) + { + next(); + } + } + + void initialize_with_string_view(string_view_type sv, std::error_code& ec) + { + auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return; + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + if (!done()) + { + next(ec); + } + } + + void read_next(std::error_code& ec) + { + read_next(cursor_visitor_, ec); + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + if (ec) return; + } + } + bool eof = parser_.source_exhausted() && source_.eof(); + parser_.parse_some(visitor, ec); + if (ec) return; + if (eof) + { + if (parser_.enter()) + { + done_ = true; + break; + } + else if (!parser_.accept()) + { + ec = json_errc::unexpected_eof; + return; + } + } + } + } +}; + +using json_stream_cursor = basic_json_cursor>; +using json_string_cursor = basic_json_cursor>; +using wjson_stream_cursor = basic_json_cursor>; +using wjson_string_cursor = basic_json_cursor>; + +using json_cursor = basic_json_cursor; +using wjson_cursor = basic_json_cursor; + +#if !defined(JSONCONS_NO_DEPRECATED) +template> +using basic_json_pull_reader = basic_json_cursor; + +JSONCONS_DEPRECATED_MSG("Instead, use json_cursor") typedef json_cursor json_pull_reader; +JSONCONS_DEPRECATED_MSG("Instead, use wjson_cursor") typedef wjson_cursor wjson_pull_reader; + +template> +using basic_json_stream_reader = basic_json_cursor; + +template> +using basic_json_staj_cursor = basic_json_cursor; + +JSONCONS_DEPRECATED_MSG("Instead, use json_cursor") typedef json_cursor json_staj_cursor; +JSONCONS_DEPRECATED_MSG("Instead, use wjson_cursor") typedef wjson_cursor wjson_staj_cursor; +#endif + +} + +#endif + diff --git a/include/jsoncons/json_decoder.hpp b/include/jsoncons/json_decoder.hpp new file mode 100644 index 0000000..c5aa0b2 --- /dev/null +++ b/include/jsoncons/json_decoder.hpp @@ -0,0 +1,420 @@ +// Copyright 2013-2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_DECODER_HPP +#define JSONCONS_JSON_DECODER_HPP + +#include +#include +#include // std::true_type +#include // std::allocator +#include // std::make_move_iterator +#include // std::move +#include +#include + +namespace jsoncons { + +template > +class json_decoder final : public basic_json_visitor +{ +public: + using char_type = typename Json::char_type; + using typename basic_json_visitor::string_view_type; + + using key_value_type = typename Json::key_value_type; + using key_type = typename Json::key_type; + using array = typename Json::array; + using object = typename Json::object; + using result_allocator_type = typename Json::allocator_type; + using json_string_allocator = typename key_type::allocator_type; + using json_array_allocator = typename array::allocator_type; + using json_object_allocator = typename object::allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc json_byte_allocator_type; +private: + struct stack_item + { + key_type name_; + Json value_; + + template + stack_item(key_type&& name, Args&& ... args) + : name_(std::move(name)), value_(std::forward(args)...) + { + } + + stack_item() = default; + stack_item(const stack_item&) = default; + stack_item(stack_item&&) = default; + stack_item& operator=(const stack_item&) = default; + stack_item& operator=(stack_item&&) = default; + }; + + enum class structure_type {root_t, array_t, object_t}; + + struct structure_info + { + structure_type type_; + std::size_t container_index_; + + structure_info(structure_type type, std::size_t offset) noexcept + : type_(type), container_index_(offset) + { + } + + }; + + using temp_allocator_type = TempAllocator; + typedef typename std::allocator_traits:: template rebind_alloc stack_item_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc structure_info_allocator_type; + + result_allocator_type result_allocator_; + temp_allocator_type temp_allocator_; + + Json result_; + + key_type name_; + std::vector item_stack_; + std::vector structure_stack_; + bool is_valid_; + +public: + json_decoder(const temp_allocator_type& temp_alloc = temp_allocator_type()) + : result_allocator_(result_allocator_type()), + temp_allocator_(temp_alloc), + result_(), + name_(result_allocator_), + item_stack_(temp_allocator_), + structure_stack_(temp_allocator_), + is_valid_(false) + { + item_stack_.reserve(1000); + structure_stack_.reserve(100); + structure_stack_.emplace_back(structure_type::root_t, 0); + } + + json_decoder(result_allocator_arg_t, + const result_allocator_type& result_alloc) + : result_allocator_(result_alloc), + temp_allocator_(), + result_(), + name_(result_allocator_), + item_stack_(), + structure_stack_(), + is_valid_(false) + { + item_stack_.reserve(1000); + structure_stack_.reserve(100); + structure_stack_.emplace_back(structure_type::root_t, 0); + } + + json_decoder(result_allocator_arg_t, + const result_allocator_type& result_alloc, + const temp_allocator_type& temp_alloc) + : result_allocator_(result_alloc), + temp_allocator_(temp_alloc), + result_(), + name_(result_allocator_), + item_stack_(temp_allocator_), + structure_stack_(temp_allocator_), + is_valid_(false) + { + item_stack_.reserve(1000); + structure_stack_.reserve(100); + structure_stack_.emplace_back(structure_type::root_t, 0); + } + + void reset() + { + is_valid_ = false; + item_stack_.clear(); + structure_stack_.clear(); + structure_stack_.emplace_back(structure_type::root_t, 0); + } + + bool is_valid() const + { + return is_valid_; + } + + Json get_result() + { + JSONCONS_ASSERT(is_valid_); + is_valid_ = false; + return std::move(result_); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use get_result()") + Json& root() + { + return result_; + } +#endif + +private: + + void visit_flush() override + { + } + + bool visit_begin_object(semantic_tag tag, const ser_context&, std::error_code&) override + { + if (structure_stack_.back().type_ == structure_type::root_t) + { + item_stack_.clear(); + is_valid_ = false; + } + item_stack_.emplace_back(std::forward(name_), json_object_arg, tag, result_allocator_); + structure_stack_.emplace_back(structure_type::object_t, item_stack_.size()-1); + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(structure_stack_.size() > 0); + JSONCONS_ASSERT(structure_stack_.back().type_ == structure_type::object_t); + const size_t structure_index = structure_stack_.back().container_index_; + JSONCONS_ASSERT(item_stack_.size() > structure_index); + const size_t count = item_stack_.size() - (structure_index + 1); + auto first = item_stack_.begin() + (structure_index+1); + auto last = first + count; + item_stack_[structure_index].value_.object_value().insert( + std::make_move_iterator(first), + std::make_move_iterator(last), + [](stack_item&& val){return key_value_type(std::move(val.name_), std::move(val.value_));} + ); + item_stack_.erase(item_stack_.begin()+structure_index+1, item_stack_.end()); + structure_stack_.pop_back(); + if (structure_stack_.back().type_ == structure_type::root_t) + { + result_.swap(item_stack_.front().value_); + item_stack_.pop_back(); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_begin_array(semantic_tag tag, const ser_context&, std::error_code&) override + { + if (structure_stack_.back().type_ == structure_type::root_t) + { + item_stack_.clear(); + is_valid_ = false; + } + item_stack_.emplace_back(std::forward(name_), json_array_arg, tag, result_allocator_); + structure_stack_.emplace_back(structure_type::array_t, item_stack_.size()-1); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(structure_stack_.size() > 1); + JSONCONS_ASSERT(structure_stack_.back().type_ == structure_type::array_t); + const size_t container_index = structure_stack_.back().container_index_; + JSONCONS_ASSERT(item_stack_.size() > container_index); + + auto& container = item_stack_[container_index].value_; + + const size_t size = item_stack_.size() - (container_index + 1); + //std::cout << "size on item stack: " << size << "\n"; + + if (size > 0) + { + container.reserve(size); + auto first = item_stack_.begin() + (container_index+1); + auto last = first + size; + for (auto it = first; it != last; ++it) + { + container.push_back(std::move(it->value_)); + } + item_stack_.erase(first, item_stack_.end()); + } + + structure_stack_.pop_back(); + if (structure_stack_.back().type_ == structure_type::root_t) + { + result_.swap(item_stack_.front().value_); + item_stack_.pop_back(); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + name_ = key_type(name.data(),name.length(),result_allocator_); + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), sv, tag, result_allocator_); + break; + case structure_type::root_t: + result_ = Json(sv, tag, result_allocator_); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), byte_string_arg, b, tag, result_allocator_); + break; + case structure_type::root_t: + result_ = Json(byte_string_arg, b, tag, result_allocator_); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), byte_string_arg, b, ext_tag, result_allocator_); + break; + case structure_type::root_t: + result_ = Json(byte_string_arg, b, ext_tag, result_allocator_); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), value, tag); + break; + case structure_type::root_t: + result_ = Json(value,tag); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), value, tag); + break; + case structure_type::root_t: + result_ = Json(value,tag); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), half_arg, value, tag); + break; + case structure_type::root_t: + result_ = Json(half_arg, value, tag); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_double(double value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), value, tag); + break; + case structure_type::root_t: + result_ = Json(value, tag); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context&, std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), value, tag); + break; + case structure_type::root_t: + result_ = Json(value, tag); + is_valid_ = true; + return false; + } + return true; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override + { + switch (structure_stack_.back().type_) + { + case structure_type::object_t: + case structure_type::array_t: + item_stack_.emplace_back(std::forward(name_), null_type(), tag); + break; + case structure_type::root_t: + result_ = Json(null_type(), tag); + is_valid_ = true; + return false; + } + return true; + } +}; + +} + +#endif diff --git a/include/jsoncons/json_encoder.hpp b/include/jsoncons/json_encoder.hpp new file mode 100644 index 0000000..df0d3fa --- /dev/null +++ b/include/jsoncons/json_encoder.hpp @@ -0,0 +1,1587 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_ENCODER_HPP +#define JSONCONS_JSON_ENCODER_HPP + +#include // std::array +#include +#include +#include // std::isfinite, std::isnan +#include // std::numeric_limits +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace detail { + + inline + bool is_control_character(uint32_t c) + { + return c <= 0x1F || c == 0x7f; + } + + inline + bool is_non_ascii_codepoint(uint32_t cp) + { + return cp >= 0x80; + } + + template + std::size_t escape_string(const CharT* s, std::size_t length, + bool escape_all_non_ascii, bool escape_solidus, + Sink& sink) + { + std::size_t count = 0; + const CharT* begin = s; + const CharT* end = s + length; + for (const CharT* it = begin; it != end; ++it) + { + CharT c = *it; + switch (c) + { + case '\\': + sink.push_back('\\'); + sink.push_back('\\'); + count += 2; + break; + case '"': + sink.push_back('\\'); + sink.push_back('\"'); + count += 2; + break; + case '\b': + sink.push_back('\\'); + sink.push_back('b'); + count += 2; + break; + case '\f': + sink.push_back('\\'); + sink.push_back('f'); + count += 2; + break; + case '\n': + sink.push_back('\\'); + sink.push_back('n'); + count += 2; + break; + case '\r': + sink.push_back('\\'); + sink.push_back('r'); + count += 2; + break; + case '\t': + sink.push_back('\\'); + sink.push_back('t'); + count += 2; + break; + default: + if (escape_solidus && c == '/') + { + sink.push_back('\\'); + sink.push_back('/'); + count += 2; + } + else if (is_control_character(c) || escape_all_non_ascii) + { + // convert to codepoint + uint32_t cp; + auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict); + if (r.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(json_errc::illegal_codepoint)); + } + it = r.ptr - 1; + if (is_non_ascii_codepoint(cp) || is_control_character(c)) + { + if (cp > 0xFFFF) + { + cp -= 0x10000; + uint32_t first = (cp >> 10) + 0xD800; + uint32_t second = ((cp & 0x03FF) + 0xDC00); + + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::detail::to_hex_character(first >> 12 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(first >> 8 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(first >> 4 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(first & 0x000F)); + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::detail::to_hex_character(second >> 12 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(second >> 8 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(second >> 4 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(second & 0x000F)); + count += 12; + } + else + { + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::detail::to_hex_character(cp >> 12 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(cp >> 8 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(cp >> 4 & 0x000F)); + sink.push_back(jsoncons::detail::to_hex_character(cp & 0x000F)); + count += 6; + } + } + else + { + sink.push_back(c); + ++count; + } + } + else + { + sink.push_back(c); + ++count; + } + break; + } + } + return count; + } + + inline + byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1, + byte_string_chars_format format2, + byte_string_chars_format default_format = byte_string_chars_format::base64url) + { + byte_string_chars_format sink; + switch (format1) + { + case byte_string_chars_format::base16: + case byte_string_chars_format::base64: + case byte_string_chars_format::base64url: + sink = format1; + break; + default: + switch (format2) + { + case byte_string_chars_format::base64url: + case byte_string_chars_format::base64: + case byte_string_chars_format::base16: + sink = format2; + break; + default: // base64url + { + sink = default_format; + break; + } + } + break; + } + return sink; + } + +} // namespace detail + + template,class Allocator=std::allocator> + class basic_json_encoder final : public basic_json_visitor + { + static const jsoncons::basic_string_view null_constant() + { + static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null"); + return k; + } + static const jsoncons::basic_string_view true_constant() + { + static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true"); + return k; + } + static const jsoncons::basic_string_view false_constant() + { + static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false"); + return k; + } + public: + using allocator_type = Allocator; + using char_type = CharT; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + using string_type = typename basic_json_encode_options::string_type; + + private: + enum class container_type {object, array}; + + class encoding_context + { + container_type type_; + std::size_t count_; + line_split_kind line_splits_; + bool indent_before_; + bool new_line_after_; + std::size_t begin_pos_; + std::size_t data_pos_; + public: + encoding_context(container_type type, line_split_kind split_lines, bool indent_once, + std::size_t begin_pos, std::size_t data_pos) noexcept + : type_(type), count_(0), line_splits_(split_lines), indent_before_(indent_once), new_line_after_(false), + begin_pos_(begin_pos), data_pos_(data_pos) + { + } + + encoding_context(const encoding_context&) = default; + encoding_context& operator=(const encoding_context&) = default; + + void set_position(std::size_t pos) + { + data_pos_ = pos; + } + + std::size_t begin_pos() const + { + return begin_pos_; + } + + std::size_t data_pos() const + { + return data_pos_; + } + + std::size_t count() const + { + return count_; + } + + void increment_count() + { + ++count_; + } + + bool new_line_after() const + { + return new_line_after_; + } + + void new_line_after(bool value) + { + new_line_after_ = value; + } + + bool is_object() const + { + return type_ == container_type::object; + } + + bool is_array() const + { + return type_ == container_type::array; + } + + bool is_same_line() const + { + return line_splits_ == line_split_kind::same_line; + } + + bool is_new_line() const + { + return line_splits_ == line_split_kind::new_line; + } + + bool is_multi_line() const + { + return line_splits_ == line_split_kind::multi_line; + } + + bool is_indent_once() const + { + return count_ == 0 ? indent_before_ : false; + } + + }; + typedef typename std::allocator_traits:: template rebind_alloc encoding_context_allocator_type; + + Sink sink_; + basic_json_encode_options options_; + jsoncons::detail::write_double fp_; + + std::vector stack_; + int indent_amount_; + std::size_t column_; + std::basic_string colon_str_; + std::basic_string comma_str_; + std::basic_string open_object_brace_str_; + std::basic_string close_object_brace_str_; + std::basic_string open_array_bracket_str_; + std::basic_string close_array_bracket_str_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_json_encoder(const basic_json_encoder&) = delete; + basic_json_encoder& operator=(const basic_json_encoder&) = delete; + public: + basic_json_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_json_encoder(std::forward(sink), basic_json_encode_options(), alloc) + { + } + + basic_json_encoder(Sink&& sink, + const basic_json_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + fp_(options.float_format(), options.precision()), + stack_(alloc), + indent_amount_(0), + column_(0), + nesting_depth_(0) + { + switch (options.spaces_around_colon()) + { + case spaces_option::space_after: + colon_str_ = std::basic_string({':',' '}); + break; + case spaces_option::space_before: + colon_str_ = std::basic_string({' ',':'}); + break; + case spaces_option::space_before_and_after: + colon_str_ = std::basic_string({' ',':',' '}); + break; + default: + colon_str_.push_back(':'); + break; + } + switch (options.spaces_around_comma()) + { + case spaces_option::space_after: + comma_str_ = std::basic_string({',',' '}); + break; + case spaces_option::space_before: + comma_str_ = std::basic_string({' ',','}); + break; + case spaces_option::space_before_and_after: + comma_str_ = std::basic_string({' ',',',' '}); + break; + default: + comma_str_.push_back(','); + break; + } + if (options.pad_inside_object_braces()) + { + open_object_brace_str_ = std::basic_string({'{', ' '}); + close_object_brace_str_ = std::basic_string({' ', '}'}); + } + else + { + open_object_brace_str_.push_back('{'); + close_object_brace_str_.push_back('}'); + } + if (options.pad_inside_array_brackets()) + { + open_array_bracket_str_ = std::basic_string({'[', ' '}); + close_array_bracket_str_ = std::basic_string({' ', ']'}); + } + else + { + open_array_bracket_str_.push_back('['); + close_array_bracket_str_.push_back(']'); + } + } + + ~basic_json_encoder() noexcept + { + JSONCONS_TRY + { + sink_.flush(); + } + JSONCONS_CATCH(...) + { + } + } + + void reset() + { + stack_.clear(); + indent_amount_ = 0; + column_ = 0; + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + + private: + // Implementing methods + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = json_errc::max_nesting_depth_exceeded; + return false; + } + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.append(comma_str_.data(),comma_str_.length()); + column_ += comma_str_.length(); + } + + if (!stack_.empty()) // object or array + { + if (stack_.back().is_object()) + { + switch (options_.object_object_line_splits()) + { + case line_split_kind::same_line: + if (column_ >= options_.line_length_limit()) + { + break_line(); + } + break; + case line_split_kind::new_line: + if (column_ >= options_.line_length_limit()) + { + break_line(); + } + break; + default: // multi_line + break; + } + stack_.emplace_back(container_type::object,options_.object_object_line_splits(), false, + column_, column_+open_object_brace_str_.length()); + } + else // array + { + switch (options_.array_object_line_splits()) + { + case line_split_kind::same_line: + if (column_ >= options_.line_length_limit()) + { + //stack_.back().new_line_after(true); + new_line(); + } + break; + case line_split_kind::new_line: + stack_.back().new_line_after(true); + new_line(); + break; + default: // multi_line + stack_.back().new_line_after(true); + new_line(); + break; + } + stack_.emplace_back(container_type::object,options_.array_object_line_splits(), false, + column_, column_+open_object_brace_str_.length()); + } + } + else + { + stack_.emplace_back(container_type::object, line_split_kind::multi_line, false, + column_, column_+open_object_brace_str_.length()); + } + indent(); + + sink_.append(open_object_brace_str_.data(), open_object_brace_str_.length()); + column_ += open_object_brace_str_.length(); + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + unindent(); + if (stack_.back().new_line_after()) + { + new_line(); + } + stack_.pop_back(); + sink_.append(close_object_brace_str_.data(), close_object_brace_str_.length()); + column_ += close_object_brace_str_.length(); + + end_value(); + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = json_errc::max_nesting_depth_exceeded; + return false; + } + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.append(comma_str_.data(),comma_str_.length()); + column_ += comma_str_.length(); + } + if (!stack_.empty()) + { + if (stack_.back().is_object()) + { + switch (options_.object_array_line_splits()) + { + case line_split_kind::same_line: + stack_.emplace_back(container_type::array,options_.object_array_line_splits(),false, + column_, column_ + open_array_bracket_str_.length()); + break; + case line_split_kind::new_line: + { + stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true, + column_, column_+open_array_bracket_str_.length()); + break; + } + default: // multi_line + stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true, + column_, column_+open_array_bracket_str_.length()); + break; + } + } + else // array + { + switch (options_.array_array_line_splits()) + { + case line_split_kind::same_line: + if (stack_.back().is_multi_line()) + { + stack_.back().new_line_after(true); + new_line(); + } + stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, + column_, column_+open_array_bracket_str_.length()); + break; + case line_split_kind::new_line: + stack_.back().new_line_after(true); + new_line(); + stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, + column_, column_+open_array_bracket_str_.length()); + break; + default: // multi_line + stack_.back().new_line_after(true); + new_line(); + stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, + column_, column_+open_array_bracket_str_.length()); + //new_line(); + break; + } + } + } + else + { + stack_.emplace_back(container_type::array, line_split_kind::multi_line, false, + column_, column_+open_array_bracket_str_.length()); + } + indent(); + sink_.append(open_array_bracket_str_.data(), open_array_bracket_str_.length()); + column_ += open_array_bracket_str_.length(); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + unindent(); + if (stack_.back().new_line_after()) + { + new_line(); + } + stack_.pop_back(); + sink_.append(close_array_bracket_str_.data(), close_array_bracket_str_.length()); + column_ += close_array_bracket_str_.length(); + end_value(); + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + if (stack_.back().count() > 0) + { + sink_.append(comma_str_.data(),comma_str_.length()); + column_ += comma_str_.length(); + } + + if (stack_.back().is_multi_line()) + { + stack_.back().new_line_after(true); + new_line(); + } + else if (stack_.back().count() > 0 && column_ >= options_.line_length_limit()) + { + //stack_.back().new_line_after(true); + new_line(stack_.back().data_pos()); + } + + if (stack_.back().count() == 0) + { + stack_.back().set_position(column_); + } + sink_.push_back('\"'); + std::size_t length = jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + sink_.append(colon_str_.data(),colon_str_.length()); + column_ += (length+2+colon_str_.length()); + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + + sink_.append(null_constant().data(), null_constant().size()); + column_ += null_constant().size(); + + end_value(); + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + + switch (tag) + { + case semantic_tag::bigint: + write_bigint_value(sv); + break; + default: + { + sink_.push_back('\"'); + std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + column_ += (length+2); + break; + } + } + + end_value(); + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + + byte_string_chars_format encoding_hint; + switch (tag) + { + case semantic_tag::base16: + encoding_hint = byte_string_chars_format::base16; + break; + case semantic_tag::base64: + encoding_hint = byte_string_chars_format::base64; + break; + case semantic_tag::base64url: + encoding_hint = byte_string_chars_format::base64url; + break; + default: + encoding_hint = byte_string_chars_format::none; + break; + } + + byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), + encoding_hint, + byte_string_chars_format::base64url); + switch (format) + { + case byte_string_chars_format::base16: + { + sink_.push_back('\"'); + std::size_t length = encode_base16(b.begin(),b.end(),sink_); + sink_.push_back('\"'); + column_ += (length + 2); + break; + } + case byte_string_chars_format::base64: + { + sink_.push_back('\"'); + std::size_t length = encode_base64(b.begin(), b.end(), sink_); + sink_.push_back('\"'); + column_ += (length + 2); + break; + } + case byte_string_chars_format::base64url: + { + sink_.push_back('\"'); + std::size_t length = encode_base64url(b.begin(),b.end(),sink_); + sink_.push_back('\"'); + column_ += (length + 2); + break; + } + default: + { + JSONCONS_UNREACHABLE(); + } + } + + end_value(); + return true; + } + + bool visit_double(double value, + semantic_tag, + const ser_context& context, + std::error_code& ec) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + + if (!std::isfinite(value)) + { + if ((std::isnan)(value)) + { + if (options_.enable_nan_to_num()) + { + sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length()); + column_ += options_.nan_to_num().length(); + } + else if (options_.enable_nan_to_str()) + { + visit_string(options_.nan_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + column_ += null_constant().size(); + } + } + else if (value == std::numeric_limits::infinity()) + { + if (options_.enable_inf_to_num()) + { + sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length()); + column_ += options_.inf_to_num().length(); + } + else if (options_.enable_inf_to_str()) + { + visit_string(options_.inf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + column_ += null_constant().size(); + } + } + else + { + if (options_.enable_neginf_to_num()) + { + sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length()); + column_ += options_.neginf_to_num().length(); + } + else if (options_.enable_neginf_to_str()) + { + visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + column_ += null_constant().size(); + } + } + } + else + { + std::size_t length = fp_(value, sink_); + column_ += length; + } + + end_value(); + return true; + } + + bool visit_int64(int64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + std::size_t length = jsoncons::detail::from_integer(value, sink_); + column_ += length; + end_value(); + return true; + } + + bool visit_uint64(uint64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + std::size_t length = jsoncons::detail::from_integer(value, sink_); + column_ += length; + end_value(); + return true; + } + + bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty()) + { + if (stack_.back().is_array()) + { + begin_scalar_value(); + } + if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit()) + { + break_line(); + } + } + + if (value) + { + sink_.append(true_constant().data(), true_constant().size()); + column_ += true_constant().size(); + } + else + { + sink_.append(false_constant().data(), false_constant().size()); + column_ += false_constant().size(); + } + + end_value(); + return true; + } + + void begin_scalar_value() + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + sink_.append(comma_str_.data(),comma_str_.length()); + column_ += comma_str_.length(); + } + if (stack_.back().is_multi_line() || stack_.back().is_indent_once()) + { + stack_.back().new_line_after(true); + new_line(); + } + } + } + + void write_bigint_value(const string_view_type& sv) + { + switch (options_.bigint_format()) + { + case bigint_chars_format::number: + { + sink_.append(sv.data(),sv.size()); + column_ += sv.size(); + break; + } + case bigint_chars_format::base64: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + bool is_neg = n < 0; + if (is_neg) + { + n = - n -1; + } + int signum; + std::vector v; + n.write_bytes_be(signum, v); + + sink_.push_back('\"'); + if (is_neg) + { + sink_.push_back('~'); + ++column_; + } + std::size_t length = encode_base64(v.begin(), v.end(), sink_); + sink_.push_back('\"'); + column_ += (length+2); + break; + } + case bigint_chars_format::base64url: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + bool is_neg = n < 0; + if (is_neg) + { + n = - n -1; + } + int signum; + std::vector v; + n.write_bytes_be(signum, v); + + sink_.push_back('\"'); + if (is_neg) + { + sink_.push_back('~'); + ++column_; + } + std::size_t length = encode_base64url(v.begin(), v.end(), sink_); + sink_.push_back('\"'); + column_ += (length+2); + break; + } + default: + { + sink_.push_back('\"'); + sink_.append(sv.data(),sv.size()); + sink_.push_back('\"'); + column_ += (sv.size() + 2); + break; + } + } + } + + void end_value() + { + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + } + + void indent() + { + indent_amount_ += static_cast(options_.indent_size()); + } + + void unindent() + { + indent_amount_ -= static_cast(options_.indent_size()); + } + + void new_line() + { + sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length()); + for (int i = 0; i < indent_amount_; ++i) + { + sink_.push_back(' '); + } + column_ = indent_amount_; + } + + void new_line(std::size_t len) + { + sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length()); + for (std::size_t i = 0; i < len; ++i) + { + sink_.push_back(' '); + } + column_ = len; + } + + void break_line() + { + stack_.back().new_line_after(true); + new_line(); + } + }; + + template,class Allocator=std::allocator> + class basic_compact_json_encoder final : public basic_json_visitor + { + static const std::array& null_constant() + { + static constexpr std::array k{'n','u','l','l'}; + return k; + } + static const std::array& true_constant() + { + static constexpr std::array k{'t','r','u','e'}; + return k; + } + static const std::array& false_constant() + { + static constexpr std::array k{'f','a','l','s','e'}; + return k; + } + public: + using allocator_type = Allocator; + using char_type = CharT; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + using string_type = typename basic_json_encode_options::string_type; + + private: + enum class container_type {object, array}; + + class encoding_context + { + container_type type_; + std::size_t count_; + public: + encoding_context(container_type type) noexcept + : type_(type), count_(0) + { + } + + std::size_t count() const + { + return count_; + } + + void increment_count() + { + ++count_; + } + + bool is_array() const + { + return type_ == container_type::array; + } + }; + typedef typename std::allocator_traits:: template rebind_alloc encoding_context_allocator_type; + + Sink sink_; + basic_json_encode_options options_; + jsoncons::detail::write_double fp_; + std::vector stack_; + int nesting_depth_; + + // Noncopyable + basic_compact_json_encoder(const basic_compact_json_encoder&) = delete; + basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete; + public: + basic_compact_json_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_compact_json_encoder(std::forward(sink), basic_json_encode_options(), alloc) + { + } + + basic_compact_json_encoder(Sink&& sink, + const basic_json_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + fp_(options.float_format(), options.precision()), + stack_(alloc), + nesting_depth_(0) + { + } + + basic_compact_json_encoder(basic_compact_json_encoder&&) = default; + basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = default; + + ~basic_compact_json_encoder() noexcept + { + JSONCONS_TRY + { + sink_.flush(); + } + JSONCONS_CATCH(...) + { + } + } + + void reset() + { + stack_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + + private: + // Implementing methods + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = json_errc::max_nesting_depth_exceeded; + return false; + } + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + stack_.emplace_back(container_type::object); + sink_.push_back('{'); + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + stack_.pop_back(); + sink_.push_back('}'); + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = json_errc::max_nesting_depth_exceeded; + return false; + } + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + stack_.emplace_back(container_type::array); + sink_.push_back('['); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + stack_.pop_back(); + sink_.push_back(']'); + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + if (!stack_.empty() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + sink_.push_back('\"'); + jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + sink_.push_back(':'); + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + sink_.append(null_constant().data(), null_constant().size()); + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + void write_bigint_value(const string_view_type& sv) + { + switch (options_.bigint_format()) + { + case bigint_chars_format::number: + { + sink_.append(sv.data(),sv.size()); + break; + } + case bigint_chars_format::base64: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + bool is_neg = n < 0; + if (is_neg) + { + n = - n -1; + } + int signum; + std::vector v; + n.write_bytes_be(signum, v); + + sink_.push_back('\"'); + if (is_neg) + { + sink_.push_back('~'); + } + encode_base64(v.begin(), v.end(), sink_); + sink_.push_back('\"'); + break; + } + case bigint_chars_format::base64url: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + bool is_neg = n < 0; + if (is_neg) + { + n = - n -1; + } + int signum; + std::vector v; + n.write_bytes_be(signum, v); + + sink_.push_back('\"'); + if (is_neg) + { + sink_.push_back('~'); + } + encode_base64url(v.begin(), v.end(), sink_); + sink_.push_back('\"'); + break; + } + default: + { + sink_.push_back('\"'); + sink_.append(sv.data(),sv.size()); + sink_.push_back('\"'); + break; + } + } + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + switch (tag) + { + case semantic_tag::bigint: + write_bigint_value(sv); + break; + default: + { + sink_.push_back('\"'); + jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + break; + } + } + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + byte_string_chars_format encoding_hint; + switch (tag) + { + case semantic_tag::base16: + encoding_hint = byte_string_chars_format::base16; + break; + case semantic_tag::base64: + encoding_hint = byte_string_chars_format::base64; + break; + case semantic_tag::base64url: + encoding_hint = byte_string_chars_format::base64url; + break; + default: + encoding_hint = byte_string_chars_format::none; + break; + } + + byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), + encoding_hint, + byte_string_chars_format::base64url); + switch (format) + { + case byte_string_chars_format::base16: + { + sink_.push_back('\"'); + encode_base16(b.begin(),b.end(),sink_); + sink_.push_back('\"'); + break; + } + case byte_string_chars_format::base64: + { + sink_.push_back('\"'); + encode_base64(b.begin(), b.end(), sink_); + sink_.push_back('\"'); + break; + } + case byte_string_chars_format::base64url: + { + sink_.push_back('\"'); + encode_base64url(b.begin(),b.end(),sink_); + sink_.push_back('\"'); + break; + } + default: + { + JSONCONS_UNREACHABLE(); + } + } + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_double(double value, + semantic_tag, + const ser_context& context, + std::error_code& ec) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + if (JSONCONS_UNLIKELY(!std::isfinite(value))) + { + if ((std::isnan)(value)) + { + if (options_.enable_nan_to_num()) + { + sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length()); + } + else if (options_.enable_nan_to_str()) + { + visit_string(options_.nan_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + } + } + else if (value == std::numeric_limits::infinity()) + { + if (options_.enable_inf_to_num()) + { + sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length()); + } + else if (options_.enable_inf_to_str()) + { + visit_string(options_.inf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + } + } + else + { + if (options_.enable_neginf_to_num()) + { + sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length()); + } + else if (options_.enable_neginf_to_str()) + { + visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink_.append(null_constant().data(), null_constant().size()); + } + } + } + else + { + fp_(value, sink_); + } + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_int64(int64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + jsoncons::detail::from_integer(value, sink_); + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_uint64(uint64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + jsoncons::detail::from_integer(value, sink_); + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + + bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override + { + if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) + { + sink_.push_back(','); + } + + if (value) + { + sink_.append(true_constant().data(), true_constant().size()); + } + else + { + sink_.append(false_constant().data(), false_constant().size()); + } + + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + return true; + } + }; + + using json_stream_encoder = basic_json_encoder>; + using wjson_stream_encoder = basic_json_encoder>; + using compact_json_stream_encoder = basic_compact_json_encoder>; + using compact_wjson_stream_encoder = basic_compact_json_encoder>; + + using json_string_encoder = basic_json_encoder>; + using wjson_string_encoder = basic_json_encoder>; + using compact_json_string_encoder = basic_compact_json_encoder>; + using compact_wjson_string_encoder = basic_compact_json_encoder>; + + #if !defined(JSONCONS_NO_DEPRECATED) + template> + using basic_json_serializer = basic_json_encoder; + + template> + using basic_json_compressed_serializer = basic_compact_json_encoder; + + template> + using basic_json_compressed_encoder = basic_compact_json_encoder; + + JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder> json_compressed_stream_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder")typedef basic_compact_json_encoder> wjson_compressed_stream_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder> json_compressed_string_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_string_encoder")typedef basic_compact_json_encoder> wjson_compressed_string_encoder; + + JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef json_stream_encoder json_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef wjson_stream_encoder wjson_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef compact_json_stream_encoder compact_json_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef compact_wjson_stream_encoder wcompact_json_encoder; + + JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef basic_json_encoder> json_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef basic_json_encoder> wjson_serializer; + + JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder> json_compressed_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef basic_compact_json_encoder> wjson_compressed_serializer; + + JSONCONS_DEPRECATED_MSG("Instead, use json_string_encoder") typedef basic_json_encoder> json_string_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use wjson_string_encoder") typedef basic_json_encoder> wjson_string_serializer; + + JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder> json_compressed_string_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use wcompact_json_string_encoder") typedef basic_compact_json_encoder> wjson_compressed_string_serializer; + #endif + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/json_error.hpp b/include/jsoncons/json_error.hpp new file mode 100644 index 0000000..f305948 --- /dev/null +++ b/include/jsoncons/json_error.hpp @@ -0,0 +1,156 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_ERROR_HPP +#define JSONCONS_JSON_ERROR_HPP + +#include +#include + +namespace jsoncons { + + enum class json_errc + { + success = 0, + unexpected_eof = 1, + source_error, + syntax_error, + extra_character, + max_nesting_depth_exceeded, + single_quote, + illegal_character_in_string, + extra_comma, + expected_key, + expected_value, + invalid_value, + expected_colon, + illegal_control_character, + illegal_escaped_character, + expected_codepoint_surrogate_pair, + invalid_hex_escape_sequence, + invalid_unicode_escape_sequence, + leading_zero, + invalid_number, + expected_comma_or_rbrace, + expected_comma_or_rbracket, + unexpected_rbracket, + unexpected_rbrace, + illegal_comment, + expected_continuation_byte, + over_long_utf8_sequence, + illegal_codepoint, + illegal_surrogate_value, + unpaired_high_surrogate, + illegal_unicode_character + }; + + class json_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/json"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case json_errc::unexpected_eof: + return "Unexpected end of file"; + case json_errc::source_error: + return "Source error"; + case json_errc::syntax_error: + return "JSON syntax_error"; + case json_errc::extra_character: + return "Unexpected non-whitespace character after JSON text"; + case json_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case json_errc::single_quote: + return "JSON strings cannot be quoted with single quotes"; + case json_errc::illegal_character_in_string: + return "Illegal character in string"; + case json_errc::extra_comma: + return "Extra comma"; + case json_errc::expected_key: + return "Expected object member key"; + case json_errc::expected_value: + return "Expected value"; + case json_errc::invalid_value: + return "Invalid value"; + case json_errc::expected_colon: + return "Expected name separator ':'"; + case json_errc::illegal_control_character: + return "Illegal control character in string"; + case json_errc::illegal_escaped_character: + return "Illegal escaped character in string"; + case json_errc::expected_codepoint_surrogate_pair: + return "Invalid codepoint, expected another \\u token to begin the second half of a codepoint surrogate pair."; + case json_errc::invalid_hex_escape_sequence: + return "Invalid codepoint, expected hexadecimal digit."; + case json_errc::invalid_unicode_escape_sequence: + return "Invalid codepoint, expected four hexadecimal digits."; + case json_errc::leading_zero: + return "A number cannot have a leading zero"; + case json_errc::invalid_number: + return "Invalid number"; + case json_errc::expected_comma_or_rbrace: + return "Expected comma or right brace '}'"; + case json_errc::expected_comma_or_rbracket: + return "Expected comma or right bracket ']'"; + case json_errc::unexpected_rbrace: + return "Unexpected right brace '}'"; + case json_errc::unexpected_rbracket: + return "Unexpected right bracket ']'"; + case json_errc::illegal_comment: + return "Illegal comment"; + case json_errc::expected_continuation_byte: + return "Expected continuation byte"; + case json_errc::over_long_utf8_sequence: + return "Over long UTF-8 sequence"; + case json_errc::illegal_codepoint: + return "Illegal codepoint (>= 0xd800 && <= 0xdfff)"; + case json_errc::illegal_surrogate_value: + return "UTF-16 surrogate values are illegal in UTF-32"; + case json_errc::unpaired_high_surrogate: + return "Expected low surrogate following the high surrogate"; + case json_errc::illegal_unicode_character: + return "Illegal unicode character"; + default: + return "Unknown JSON parser error"; + } + } + }; + + inline + const std::error_category& json_error_category() + { + static json_error_category_impl instance; + return instance; + } + + inline + std::error_code make_error_code(json_errc result) + { + return std::error_code(static_cast(result),json_error_category()); + } + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use json_errc") typedef json_errc json_parser_errc; + +JSONCONS_DEPRECATED_MSG("Instead, use json_errc") typedef json_errc json_parse_errc; +#endif + +} // jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons/json_exception.hpp b/include/jsoncons/json_exception.hpp new file mode 100644 index 0000000..e25d401 --- /dev/null +++ b/include/jsoncons/json_exception.hpp @@ -0,0 +1,241 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSON_EXCEPTION_HPP +#define JSON_EXCEPTION_HPP + +#include // std::string +#include // std::ostringstream +#include // std::error_code +#include // unicode_traits::convert +#include +#include + +namespace jsoncons { + + // json_exception + + class json_exception + { + public: + virtual ~json_exception() noexcept = default; + virtual const char* what() const noexcept = 0; + }; + + // json_runtime_error + + template + class json_runtime_error + { + }; + + template + class json_runtime_error::value && + type_traits::is_constructible_from_string::value>::type> + : public Base, public virtual json_exception + { + public: + json_runtime_error(const std::string& s) noexcept + : Base(s) + { + } + ~json_runtime_error() noexcept + { + } + const char* what() const noexcept override + { + return Base::what(); + } + }; + + class key_not_found : public std::out_of_range, public virtual json_exception + { + std::string name_; + mutable std::string what_; + public: + template + explicit key_not_found(const CharT* key, std::size_t length) noexcept + : std::out_of_range("Key not found") + { + JSONCONS_TRY + { + unicode_traits::convert(key, length, name_, + unicode_traits::conv_flags::strict); + } + JSONCONS_CATCH(...) + { + } + } + + virtual ~key_not_found() noexcept + { + } + + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::out_of_range::what()); + what_.append(": '"); + what_.append(name_); + what_.append("'"); + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::out_of_range::what(); + } + } + else + { + return what_.c_str(); + } + } + }; + + class not_an_object : public std::runtime_error, public virtual json_exception + { + std::string name_; + mutable std::string what_; + public: + template + explicit not_an_object(const CharT* key, std::size_t length) noexcept + : std::runtime_error("Attempting to access a member of a value that is not an object") + { + JSONCONS_TRY + { + unicode_traits::convert(key, length, name_, + unicode_traits::conv_flags::strict); + } + JSONCONS_CATCH(...) + { + } + } + + virtual ~not_an_object() noexcept + { + } + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::runtime_error::what()); + what_.append(": '"); + what_.append(name_); + what_.append("'"); + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::runtime_error::what(); + } + } + else + { + return what_.c_str(); + } + } + }; + + class ser_error : public std::system_error, public virtual json_exception + { + std::size_t line_number_; + std::size_t column_number_; + mutable std::string what_; + public: + ser_error(std::error_code ec) + : std::system_error(ec), line_number_(0), column_number_(0) + { + } + ser_error(std::error_code ec, const std::string& what_arg) + : std::system_error(ec, what_arg), line_number_(0), column_number_(0) + { + } + ser_error(std::error_code ec, std::size_t position) + : std::system_error(ec), line_number_(0), column_number_(position) + { + } + ser_error(std::error_code ec, std::size_t line, std::size_t column) + : std::system_error(ec), line_number_(line), column_number_(column) + { + } + ser_error(const ser_error& other) = default; + + ser_error(ser_error&& other) = default; + + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::system_error::what()); + if (line_number_ != 0 && column_number_ != 0) + { + what_.append(" at line "); + what_.append(std::to_string(line_number_)); + what_.append(" and column "); + what_.append(std::to_string(column_number_)); + } + else if (column_number_ != 0) + { + what_.append(" at position "); + what_.append(std::to_string(column_number_)); + } + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::system_error::what(); + } + } + else + { + return what_.c_str(); + } + } + + std::size_t line() const noexcept + { + return line_number_; + } + + std::size_t column() const noexcept + { + return column_number_; + } + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use line()") + std::size_t line_number() const noexcept + { + return line(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use column()") + std::size_t column_number() const noexcept + { + return column(); + } + #endif + }; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error serialization_error; +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error json_parse_exception; +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error parse_exception; +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error parse_error; +typedef ser_error codec_error; +#endif + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/json_filter.hpp b/include/jsoncons/json_filter.hpp new file mode 100644 index 0000000..cc93c73 --- /dev/null +++ b/include/jsoncons/json_filter.hpp @@ -0,0 +1,653 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_FILTER_HPP +#define JSONCONS_JSON_FILTER_HPP + +#include + +#include + +namespace jsoncons { + +template +class basic_json_filter : public basic_json_visitor +{ +public: + using typename basic_json_visitor::char_type; + using typename basic_json_visitor::string_view_type; +private: + basic_json_visitor* destination_; + + // noncopyable + basic_json_filter(const basic_json_filter&) = delete; + basic_json_filter& operator=(const basic_json_filter&) = delete; +public: + basic_json_filter(basic_json_visitor& visitor) + : destination_(std::addressof(visitor)) + { + } + + // moveable + basic_json_filter(basic_json_filter&&) = default; + basic_json_filter& operator=(basic_json_filter&&) = default; + + basic_json_visitor& destination() + { + return *destination_; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use destination()") + basic_json_visitor& to_handler() + { + return destination_; + } + JSONCONS_DEPRECATED_MSG("Instead, use destination") + basic_json_visitor& input_handler() + { + return destination_; + } + + JSONCONS_DEPRECATED_MSG("Instead, use destination") + basic_json_visitor& downstream_handler() + { + return destination_; + } + + JSONCONS_DEPRECATED_MSG("Instead, use destination") + basic_json_visitor& destination_handler() + { + return destination_; + } +#endif + +private: + void visit_flush() override + { + destination_->flush(); + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->begin_object(tag, context, ec); + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->begin_object(length, tag, context, ec); + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + return destination_->end_object(context, ec); + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->begin_array(tag, context, ec); + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->begin_array(length, tag, context, ec); + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + return destination_->end_array(context, ec); + } + + bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code& ec) override + { + return destination_->key(name, context, ec); + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->string_value(value, tag, context, ec); + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->byte_string_value(b, tag, context, ec); + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->byte_string_value(b, ext_tag, context, ec); + } + + bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->uint64_value(value, tag, context, ec); + } + + bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->int64_value(value, tag, context, ec); + } + + bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->half_value(value, tag, context, ec); + } + + bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->double_value(value, tag, context, ec); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->bool_value(value, tag, context, ec); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->null_value(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(half_arg, s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_multi_dim(shape, tag, context, ec); + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + return destination_->end_multi_dim(context, ec); + } +}; + +template +class basic_rename_object_key_filter : public basic_json_filter +{ +public: + using typename basic_json_filter::string_view_type; + +private: + std::basic_string name_; + std::basic_string new_name_; +public: + basic_rename_object_key_filter(const std::basic_string& name, + const std::basic_string& new_name, + basic_json_visitor& visitor) + : basic_json_filter(visitor), + name_(name), new_name_(new_name) + { + } + +private: + bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code& ec) override + { + if (name == name_) + { + return this->destination().key(new_name_,context, ec); + } + else + { + return this->destination().key(name,context,ec); + } + } +}; + +template +class json_visitor_adaptor_base : public From +{ +public: + using typename From::string_view_type; +private: + To* destination_; + + // noncopyable + json_visitor_adaptor_base(const json_visitor_adaptor_base&) = delete; + json_visitor_adaptor_base& operator=(const json_visitor_adaptor_base&) = delete; +public: + json_visitor_adaptor_base(To& visitor) + : destination_(std::addressof(visitor)) + { + } + + // moveable + json_visitor_adaptor_base(json_visitor_adaptor_base&&) = default; + json_visitor_adaptor_base& operator=(json_visitor_adaptor_base&&) = default; + + To& destination() + { + return *destination_; + } + +private: + void visit_flush() override + { + destination_->flush(); + } + + bool visit_begin_object(semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_object(tag, context, ec); + } + + bool visit_begin_object(std::size_t length, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_object(length, tag, context, ec); + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + return destination_->end_object(context, ec); + } + + bool visit_begin_array(semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_array(tag, context, ec); + } + + bool visit_begin_array(std::size_t length, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_array(length, tag, context, ec); + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + return destination_->end_array(context, ec); + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->byte_string_value(b, tag, context, ec); + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->byte_string_value(b, ext_tag, context, ec); + } + + bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->half_value(value, tag, context, ec); + } + + bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->double_value(value, tag, context, ec); + } + + bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->int64_value(value, tag, context, ec); + } + + bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->uint64_value(value, tag, context, ec); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->bool_value(value, tag, context, ec); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_->null_value(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(half_arg, s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->typed_array(s, tag, context, ec); + } + + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_->begin_multi_dim(shape, tag, context, ec); + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + return destination_->end_multi_dim(context, ec); + } + +}; + +template +class json_visitor_adaptor +{ +}; + +template +class json_visitor_adaptor::value && + type_traits::is_narrow_character::value>::type> : public json_visitor_adaptor_base +{ + using supertype = json_visitor_adaptor_base; + using to_char_type = typename To::char_type; + using from_char_type = typename From::char_type; +public: + using typename From::string_view_type; + using supertype::destination; +private: + + // noncopyable + json_visitor_adaptor(const json_visitor_adaptor&) = delete; + json_visitor_adaptor& operator=(const json_visitor_adaptor&) = delete; +public: + json_visitor_adaptor(To& visitor) + : supertype(visitor) + { + } + + // moveable + json_visitor_adaptor(json_visitor_adaptor&&) = default; + json_visitor_adaptor& operator=(json_visitor_adaptor&&) = default; + +private: + + bool visit_key(const string_view_type& key, + const ser_context& context, + std::error_code& ec) override + { + return destination().key(string_view_type(reinterpret_cast(key.data()),key.size()), context, ec); + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination().string_value(string_view_type(reinterpret_cast(value.data()),value.size()), tag, context, ec); + } +}; + +template +class json_visitor_adaptor::value && + type_traits::is_narrow_character::value)>::type> : public json_visitor_adaptor_base +{ + using supertype = json_visitor_adaptor_base; +public: + using typename From::string_view_type; + using supertype::destination; +private: + + // noncopyable + json_visitor_adaptor(const json_visitor_adaptor&) = delete; + json_visitor_adaptor& operator=(const json_visitor_adaptor&) = delete; +public: + json_visitor_adaptor(To& visitor) + : supertype(visitor) + { + } + + // moveable + json_visitor_adaptor(json_visitor_adaptor&&) = default; + json_visitor_adaptor& operator=(json_visitor_adaptor&&) = default; + +private: + + bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code& ec) override + { + std::basic_string target; + auto result = unicode_traits::convert(name.data(), name.size(), target, unicode_traits::conv_flags::strict); + if (result.ec != unicode_traits::conv_errc()) + { + ec = result.ec; + } + return destination().key(target, context, ec); + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + std::basic_string target; + auto result = unicode_traits::convert(value.data(), value.size(), + target,unicode_traits::conv_flags::strict); + if (result.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(result.ec)); + } + return destination().string_value(target, tag, context, ec); + } +}; + +template +json_visitor_adaptor make_json_visitor_adaptor(To& to) +{ + return json_visitor_adaptor(to); +} + +using json_filter = basic_json_filter; +using wjson_filter = basic_json_filter; +using rename_object_key_filter = basic_rename_object_key_filter; +using wrename_object_key_filter = basic_rename_object_key_filter; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +using basic_json_content_filter = basic_json_filter; +JSONCONS_DEPRECATED_MSG("Instead, use json_filter") typedef basic_json_filter json_content_filter; +JSONCONS_DEPRECATED_MSG("Instead, use wjson_filter") typedef basic_json_filter wjson_content_filter; +JSONCONS_DEPRECATED_MSG("Instead, use rename_object_key_filter") typedef basic_rename_object_key_filter rename_name_filter; +JSONCONS_DEPRECATED_MSG("Instead, use wrename_object_key_filter") typedef basic_rename_object_key_filter wrename_name_filter; +JSONCONS_DEPRECATED_MSG("Instead, use rename_object_key_filter") typedef basic_rename_object_key_filter rename_object_member_filter; +JSONCONS_DEPRECATED_MSG("Instead, use wrename_object_key_filter") typedef basic_rename_object_key_filter wrename_object_member_filter; +#endif + +} + +#endif diff --git a/include/jsoncons/json_fwd.hpp b/include/jsoncons/json_fwd.hpp new file mode 100644 index 0000000..d9b44cf --- /dev/null +++ b/include/jsoncons/json_fwd.hpp @@ -0,0 +1,23 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_FWD_HPP +#define JSONCONS_JSON_FWD_HPP + +#include // std::allocator + +namespace jsoncons { + +struct sorted_policy; + +template > +class basic_json; + +} + +#endif diff --git a/include/jsoncons/json_object.hpp b/include/jsoncons/json_object.hpp new file mode 100644 index 0000000..76e2cd0 --- /dev/null +++ b/include/jsoncons/json_object.hpp @@ -0,0 +1,1772 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_OBJECT_HPP +#define JSONCONS_JSON_OBJECT_HPP + +#include +#include +#include +#include +#include +#include // std::sort, std::stable_sort, std::lower_bound, std::unique +#include +#include +#include // std::iterator_traits +#include // std::allocator +#include // std::move +#include // assert +#include // std::enable_if +#include +#include +#include + +namespace jsoncons { + + struct sorted_unique_range_tag + { + explicit sorted_unique_range_tag() = default; + }; + + // key_value + + template + class key_value + { + public: + using key_type = KeyT; + using value_type = ValueT; + using allocator_type = typename ValueT::allocator_type; + using string_view_type = typename value_type::string_view_type; + private: + + key_type key_; + value_type value_; + public: + + key_value() noexcept + { + } + + key_value(const key_type& name, const value_type& val) + : key_(name), value_(val) + { + } + + key_value(const string_view_type& name) + : key_(name) + { + } + + template + key_value(const key_type& name, Args&& ... args) + : key_(name), value_(std::forward(args)...) + { + } + + template + key_value(key_type&& name, Args&& ... args) noexcept + : key_(std::forward(name)), value_(std::forward(args)...) + { + } + + key_value(const key_value& member) + : key_(member.key_), value_(member.value_) + { + } + + key_value(key_value&& member) noexcept + : key_(std::move(member.key_)), value_(std::move(member.value_)) + { + } + template + JSONCONS_CONSTEXPR key_value(std::piecewise_construct_t /*unused*/, std::tuple a, + std::tuple + b) noexcept(noexcept(key_value(std::declval&>(), + std::declval&>(), + jsoncons::type_traits::index_sequence_for(), + jsoncons::type_traits::index_sequence_for()))) + : key_value(a, b, jsoncons::type_traits::index_sequence_for(), + jsoncons::type_traits::index_sequence_for()) { + } + + template + key_value(std::tuple& a, std::tuple& b, jsoncons::type_traits::index_sequence /*unused*/, jsoncons::type_traits::index_sequence /*unused*/) noexcept( + noexcept(KeyT(std::forward(std::get( + std::declval&>()))...)) && noexcept(ValueT(std:: + forward(std::get( + std::declval&>()))...))) + : key_(std::forward(std::get(a))...) + , value_(std::forward(std::get(b))...) { + // make visual studio compiler happy about warning about unused a & b. + // Visual studio's key_value implementation disables warning 4100. + (void)a; + (void)b; + } + + const key_type& key() const + { + return key_; + } + + value_type& value() + { + return value_; + } + + const value_type& value() const + { + return value_; + } + + template + void value(T&& value) + { + value_ = std::forward(value); + } + + void swap(key_value& member) noexcept + { + key_.swap(member.key_); + value_.swap(member.value_); + } + + key_value& operator=(const key_value& member) + { + if (this != & member) + { + key_ = member.key_; + value_ = member.value_; + } + return *this; + } + + key_value& operator=(key_value&& member) noexcept + { + if (this != &member) + { + key_.swap(member.key_); + value_.swap(member.value_); + } + return *this; + } + + void shrink_to_fit() + { + key_.shrink_to_fit(); + value_.shrink_to_fit(); + } + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use key()") + const key_type& name() const + { + return key_; + } + #endif + + friend bool operator==(const key_value& lhs, const key_value& rhs) noexcept + { + return lhs.key_ == rhs.key_ && lhs.value_ == rhs.value_; + } + + friend bool operator!=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs == rhs); + } + + friend bool operator<(const key_value& lhs, const key_value& rhs) noexcept + { + if (lhs.key_ < rhs.key_) + { + return true; + } + if (lhs.key_ == rhs.key_ && lhs.value_ < rhs.value_) + { + return true; + } + return false; + } + + friend bool operator<=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(rhs < lhs); + } + + friend bool operator>(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs <= rhs); + } + + friend bool operator>=(const key_value& lhs, const key_value& rhs) noexcept + { + return !(lhs < rhs); + } + + friend void swap(key_value& a, key_value& b) noexcept( + noexcept(std::declval().swap(std::declval()))) + { + a.swap(b); + } + }; + + template + struct get_key_value + { + using key_value_type = key_value; + + template + key_value_type operator()(const std::pair& p) + { + return key_value_type(p.first,p.second); + } + template + key_value_type operator()(std::pair&& p) + { + return key_value_type(std::forward(p.first),std::forward(p.second)); + } + template + const key_value_type& operator()(const key_value& p) + { + return p; + } + template + key_value_type operator()(key_value&& p) + { + return std::move(p); + } + }; + + struct sort_key_order + { + explicit sort_key_order() = default; + }; + + struct preserve_key_order + { + explicit preserve_key_order() = default; + }; + + + // Sort keys + template class SequenceContainer = std::vector> + class sorted_json_object : public allocator_holder + { + public: + using allocator_type = typename Json::allocator_type; + using key_type = KeyT; + using key_value_type = key_value; + using char_type = typename Json::char_type; + using string_view_type = typename Json::string_view_type; + private: + struct Comp + { + bool operator() (const key_value_type& kv, string_view_type k) const { return kv.key() < k; } + bool operator() (string_view_type k, const key_value_type& kv) const { return k < kv.key(); } + }; + + using key_value_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using key_value_container_type = SequenceContainer; + + key_value_container_type members_; + public: + using iterator = typename key_value_container_type::iterator; + using const_iterator = typename key_value_container_type::const_iterator; + + using allocator_holder::get_allocator; + + sorted_json_object() + { + } + + explicit sorted_json_object(const allocator_type& alloc) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)) + { + } + + sorted_json_object(const sorted_json_object& val) + : allocator_holder(val.get_allocator()), + members_(val.members_) + { + } + + sorted_json_object(sorted_json_object&& val) + : allocator_holder(val.get_allocator()), + members_(std::move(val.members_)) + { + } + + sorted_json_object& operator=(const sorted_json_object& val) + { + allocator_holder::operator=(val.get_allocator()); + members_ = val.members_; + return *this; + } + + sorted_json_object& operator=(sorted_json_object&& val) + { + val.swap(*this); + return *this; + } + + sorted_json_object(const sorted_json_object& val, const allocator_type& alloc) + : allocator_holder(alloc), + members_(val.members_,key_value_allocator_type(alloc)) + { + } + + sorted_json_object(sorted_json_object&& val,const allocator_type& alloc) + : allocator_holder(alloc), members_(std::move(val.members_),key_value_allocator_type(alloc)) + { + } + + template + sorted_json_object(InputIt first, InputIt last) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value()(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + template + sorted_json_object(InputIt first, InputIt last, + const allocator_type& alloc) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value()(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + sorted_json_object(const std::initializer_list,Json>>& init, + const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)) + { + members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, item.second); + } + } + + ~sorted_json_object() noexcept + { + flatten_and_destroy(); + } + + bool empty() const + { + return members_.empty(); + } + + void swap(sorted_json_object& val) noexcept + { + members_.swap(val.members_); + } + + iterator begin() + { + return members_.begin(); + } + + iterator end() + { + return members_.end(); + } + + const_iterator begin() const + { + return members_.begin(); + } + + const_iterator end() const + { + return members_.end(); + } + + std::size_t size() const {return members_.size();} + + std::size_t capacity() const {return members_.capacity();} + + void clear() {members_.clear();} + + void shrink_to_fit() + { + for (std::size_t i = 0; i < members_.size(); ++i) + { + members_[i].shrink_to_fit(); + } + members_.shrink_to_fit(); + } + + void reserve(std::size_t n) {members_.reserve(n);} + + Json& at(std::size_t i) + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return members_[i].value(); + } + + const Json& at(std::size_t i) const + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return members_[i].value(); + } + + iterator find(const string_view_type& name) noexcept + { + auto p = std::equal_range(members_.begin(),members_.end(), name, + Comp()); + return p.first == p.second ? members_.end() : p.first; + } + + const_iterator find(const string_view_type& name) const noexcept + { + auto p = std::equal_range(members_.begin(),members_.end(), name, + Comp()); + return p.first == p.second ? members_.end() : p.first; + } + + iterator erase(const_iterator pos) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + return members_.erase(it); + #else + return members_.erase(pos); + #endif + } + + iterator erase(const_iterator first, const_iterator last) + { + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it1 = members_.begin() + (first - members_.begin()); + iterator it2 = members_.begin() + (last - members_.begin()); + return members_.erase(it1,it2); + #else + return members_.erase(first,last); + #endif + } + + void erase(const string_view_type& name) + { + auto it = find(name); + if (it != members_.end()) + { + members_.erase(it); + } + } + + template + void insert(InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + std::stable_sort(members_.begin(),members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); + auto it = std::unique(members_.begin(), members_.end(), + [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));}); + members_.erase(it, members_.end()); + } + + template + void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert) + { + if (first != last) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + + auto it = find(convert(*first).key()); + if (it != members_.end()) + { + for (auto s = first; s != last; ++s) + { + it = members_.emplace(it, convert(*s)); + } + } + else + { + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + } + } + } + + // insert_or_assign + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward(value)); + inserted = true; + it = members_.begin() + members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward(value))); + inserted = false; // assigned + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward(value)); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + inserted = true; + it = members_.begin() + members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward(value), get_allocator())); + inserted = false; // assigned + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + inserted = true; + } + return std::make_pair(it,inserted); + } + + // try_emplace + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward(args)...); + it = members_.begin() + members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + it = members_.begin() + members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it = hint; + + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward(args)...); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward(args)...); + } + + return it; + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it = hint; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + } + return it; + } + + // insert_or_assign + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;}); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;}); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end()), + std::forward(value)); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward(value))); + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end()), + std::forward(value)); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,members_.end(), name, + Comp()); + } + else + { + it = std::lower_bound(members_.begin(),members_.end(), name, + Comp()); + } + + if (it == members_.end()) + { + members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + it = members_.begin() + (members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward(value),get_allocator())); + } + else + { + it = members_.emplace(it, + key_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + } + return it; + } + + // merge + + void merge(const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace(it->key(),it->value()); + } + } + + void merge(sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + if (pos == members_.end() ) + { + members_.emplace_back(*it); + } + else if ((*it).key() != pos->key()) + { + members_.emplace(pos,*it); + } + } + } + + void merge(iterator hint, const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, (*it).key(),(*it).value()); + } + } + + void merge(iterator hint, sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != members_.end() && hint->key() <= (*it).key()) + { + pos = std::lower_bound(hint,members_.end(), (*it).key(), + Comp()); + } + else + { + pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + } + if (pos == members_.end() ) + { + members_.emplace_back(*it); + hint = members_.begin() + (members_.size() - 1); + } + else if ((*it).key() != pos->key()) + { + hint = members_.emplace(pos,*it); + } + } + } + + // merge_or_update + + void merge_or_update(const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign((*it).key(),(*it).value()); + } + } + + void merge_or_update(sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + if (pos == members_.end() ) + { + members_.emplace_back(*it); + } + else + { + pos->value((*it).value()); + } + } + } + + void merge_or_update(iterator hint, const sorted_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, (*it).key(),(*it).value()); + } + } + + void merge_or_update(iterator hint, sorted_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != members_.end() && hint->key() <= (*it).key()) + { + pos = std::lower_bound(hint,members_.end(), (*it).key(), + Comp()); + } + else + { + pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), + Comp()); + } + if (pos == members_.end() ) + { + members_.emplace_back(*it); + hint = members_.begin() + (members_.size() - 1); + } + else + { + pos->value((*it).value()); + hint = pos; + } + } + } + + bool operator==(const sorted_json_object& rhs) const + { + return members_ == rhs.members_; + } + + bool operator<(const sorted_json_object& rhs) const + { + return members_ < rhs.members_; + } + private: + + void flatten_and_destroy() noexcept + { + if (!members_.empty()) + { + json_array temp(get_allocator()); + + for (auto& kv : members_) + { + switch (kv.value().storage_kind()) + { + case json_storage_kind::array_value: + case json_storage_kind::object_value: + if (!kv.value().empty()) + { + temp.emplace_back(std::move(kv.value())); + } + break; + default: + break; + } + } + } + } + }; + + // Preserve order + template class SequenceContainer = std::vector> + class order_preserving_json_object : public allocator_holder + { + public: + using allocator_type = typename Json::allocator_type; + using char_type = typename Json::char_type; + using key_type = KeyT; + //using mapped_type = Json; + using string_view_type = typename Json::string_view_type; + using key_value_type = key_value; + //using implementation_policy = typename Json::implementation_policy; + private: + + using key_value_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using key_value_container_type = SequenceContainer; + typedef typename std::allocator_traits:: template rebind_alloc index_allocator_type; + //using index_container_type = typename implementation_policy::template sequence_container_type; + using index_container_type = SequenceContainer; + + key_value_container_type members_; + index_container_type index_; + + struct Comp + { + const key_value_container_type& members_; + + Comp(const key_value_container_type& members_) + : members_(members_) + { + } + + bool operator() (std::size_t i, string_view_type k) const { return members_.at(i).key() < k; } + bool operator() (string_view_type k, std::size_t i) const { return k < members_.at(i).key(); } + }; + public: + using iterator = typename key_value_container_type::iterator; + using const_iterator = typename key_value_container_type::const_iterator; + + using allocator_holder::get_allocator; + + order_preserving_json_object() + { + } + order_preserving_json_object(const allocator_type& alloc) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + } + + order_preserving_json_object(const order_preserving_json_object& val) + : allocator_holder(val.get_allocator()), + members_(val.members_), + index_(val.index_) + { + } + + order_preserving_json_object(order_preserving_json_object&& val) + : allocator_holder(val.get_allocator()), + members_(std::move(val.members_)), + index_(std::move(val.index_)) + { + } + + order_preserving_json_object(const order_preserving_json_object& val, const allocator_type& alloc) + : allocator_holder(alloc), + members_(val.members_,key_value_allocator_type(alloc)), + index_(val.index_,index_allocator_type(alloc)) + { + } + + order_preserving_json_object(order_preserving_json_object&& val,const allocator_type& alloc) + : allocator_holder(alloc), + members_(std::move(val.members_),key_value_allocator_type(alloc)), + index_(std::move(val.index_),index_allocator_type(alloc)) + { + } + + template + order_preserving_json_object(InputIt first, InputIt last) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value()(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + template + order_preserving_json_object(InputIt first, InputIt last, + const allocator_type& alloc) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + std::size_t count = std::distance(first,last); + members_.reserve(count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(get_key_value()(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + order_preserving_json_object(std::initializer_list,Json>> init, + const allocator_type& alloc = allocator_type()) + : allocator_holder(alloc), + members_(key_value_allocator_type(alloc)), + index_(index_allocator_type(alloc)) + { + members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, item.second); + } + } + + ~order_preserving_json_object() noexcept + { + flatten_and_destroy(); + } + + order_preserving_json_object& operator=(order_preserving_json_object&& val) + { + val.swap(*this); + return *this; + } + + order_preserving_json_object& operator=(const order_preserving_json_object& val) + { + allocator_holder::operator=(val.get_allocator()); + members_ = val.members_; + index_ = val.index_; + return *this; + } + + void swap(order_preserving_json_object& val) noexcept + { + members_.swap(val.members_); + } + + bool empty() const + { + return members_.empty(); + } + + iterator begin() + { + return members_.begin(); + } + + iterator end() + { + return members_.end(); + } + + const_iterator begin() const + { + return members_.begin(); + } + + const_iterator end() const + { + return members_.end(); + } + + std::size_t size() const {return members_.size();} + + std::size_t capacity() const {return members_.capacity();} + + void clear() + { + members_.clear(); + index_.clear(); + } + + void shrink_to_fit() + { + for (std::size_t i = 0; i < members_.size(); ++i) + { + members_[i].shrink_to_fit(); + } + members_.shrink_to_fit(); + index_.shrink_to_fit(); + } + + void reserve(std::size_t n) {members_.reserve(n);} + + Json& at(std::size_t i) + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return members_[i].value(); + } + + const Json& at(std::size_t i) const + { + if (i >= members_.size()) + { + JSONCONS_THROW(json_runtime_error("Invalid array subscript")); + } + return members_[i].value(); + } + + iterator find(const string_view_type& name) noexcept + { + auto p = std::equal_range(index_.begin(),index_.end(), name, + Comp(members_)); + return p.first == p.second ? members_.end() : members_.begin() + *p.first; + } + + const_iterator find(const string_view_type& name) const noexcept + { + auto p = std::equal_range(index_.begin(),index_.end(), name, + Comp(members_)); + return p.first == p.second ? members_.end() : members_.begin() + *p.first; + } + + iterator erase(const_iterator pos) + { + if (pos != members_.end()) + { + std::size_t pos1 = pos - members_.begin(); + std::size_t pos2 = pos1 + 1; + + erase_index_entries(pos1, pos2); + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + return members_.erase(it); + #else + return members_.erase(pos); + #endif + } + else + { + return members_.end(); + } + } + + iterator erase(const_iterator first, const_iterator last) + { + std::size_t pos1 = first == members_.end() ? members_.size() : first - members_.begin(); + std::size_t pos2 = last == members_.end() ? members_.size() : last - members_.begin(); + + if (pos1 < members_.size() && pos2 <= members_.size()) + { + erase_index_entries(pos1,pos2); + + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it1 = members_.begin() + (first - members_.begin()); + iterator it2 = members_.begin() + (last - members_.begin()); + return members_.erase(it1,it2); + #else + return members_.erase(first,last); + #endif + } + else + { + return members_.end(); + } + } + + void erase(const string_view_type& name) + { + auto pos = find(name); + if (pos != members_.end()) + { + std::size_t pos1 = pos - members_.begin(); + std::size_t pos2 = pos1 + 1; + + erase_index_entries(pos1, pos2); + #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR) + iterator it = members_.begin() + (pos - members_.begin()); + members_.erase(it); + #else + members_.erase(pos); + #endif + } + } + + template + void insert(InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + + build_index(); + auto last_unique = std::unique(index_.begin(), index_.end(), + [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); }); + + if (last_unique != index_.end()) + { + index_.erase(last_unique, index_.end()); + std::sort(index_.begin(), index_.end()); + + auto result = index_.rbegin(); + if (*result != members_.size()) + { + members_.erase(members_.begin() + (*result + 1), members_.end()); + } + for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result) + { + if (*result - *it > 1) + { + members_.erase(members_.begin() + (*it + 1), members_.begin() + *result); + } + } + } + build_index(); + } + + template + void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert) + { + std::size_t count = std::distance(first,last); + + members_.reserve(members_.size() + count); + for (auto s = first; s != last; ++s) + { + members_.emplace_back(convert(*s)); + } + + build_index(); + } + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(), name.end()), std::forward(value)); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward(value))); + return std::make_pair(it,false); + } + } + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(),name.end(),get_allocator()), + std::forward(value),get_allocator()); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward(value),get_allocator())); + return std::make_pair(it,false); + } + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + if (hint == members_.end()) + { + auto result = insert_or_assign(key, std::forward(value)); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key,pos); + + if (result.second) + { + auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward(value)); + return it; + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward(value))); + return it; + } + } + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + if (hint == members_.end()) + { + auto result = insert_or_assign(key, std::forward(value)); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key,pos); + + if (result.second) + { + auto it = members_.emplace(hint, + key_type(key.begin(),key.end(),get_allocator()), + std::forward(value),get_allocator()); + return it; + } + else + { + auto it = members_.begin() + result.first; + it->value(Json(std::forward(value),get_allocator())); + return it; + } + } + } + + // merge + + void merge(const order_preserving_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace((*it).key(),(*it).value()); + } + } + + void merge(order_preserving_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = find((*it).key()); + if (pos == members_.end() ) + { + try_emplace((*it).key(),std::move((*it).value())); + } + } + } + + void merge(iterator hint, const order_preserving_json_object& source) + { + std::size_t pos = hint - members_.begin(); + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, (*it).key(),(*it).value()); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + void merge(iterator hint, order_preserving_json_object&& source) + { + std::size_t pos = hint - members_.begin(); + + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + hint = try_emplace(hint, (*it).key(), std::move((*it).value())); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + // merge_or_update + + void merge_or_update(const order_preserving_json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign((*it).key(),(*it).value()); + } + } + + void merge_or_update(order_preserving_json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = find((*it).key()); + if (pos == members_.end() ) + { + insert_or_assign((*it).key(),std::move((*it).value())); + } + else + { + pos->value(std::move((*it).value())); + } + } + } + + void merge_or_update(iterator hint, const order_preserving_json_object& source) + { + std::size_t pos = hint - members_.begin(); + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, (*it).key(),(*it).value()); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + void merge_or_update(iterator hint, order_preserving_json_object&& source) + { + std::size_t pos = hint - members_.begin(); + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + hint = insert_or_assign(hint, (*it).key(), std::move((*it).value())); + std::size_t newpos = hint - members_.begin(); + if (newpos == pos) + { + ++hint; + pos = hint - members_.begin(); + } + else + { + hint = members_.begin() + pos; + } + } + } + + // try_emplace + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& name, Args&&... args) + { + auto result = insert_index_entry(name,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(name.begin(), name.end()), std::forward(args)...); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + return std::make_pair(it,false); + } + } + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& key, Args&&... args) + { + auto result = insert_index_entry(key,members_.size()); + if (result.second) + { + members_.emplace_back(key_type(key.begin(),key.end(), get_allocator()), + std::forward(args)...); + auto it = members_.begin() + result.first; + return std::make_pair(it,true); + } + else + { + auto it = members_.begin() + result.first; + return std::make_pair(it,false); + } + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + if (hint == members_.end()) + { + auto result = try_emplace(key, std::forward(args)...); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key, pos); + + if (result.second) + { + auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward(args)...); + return it; + } + else + { + auto it = members_.begin() + result.first; + return it; + } + } + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + if (hint == members_.end()) + { + auto result = try_emplace(key, std::forward(args)...); + return result.first; + } + else + { + std::size_t pos = hint - members_.begin(); + auto result = insert_index_entry(key, pos); + + if (result.second) + { + auto it = members_.emplace(hint, + key_type(key.begin(),key.end(), get_allocator()), + std::forward(args)...); + return it; + } + else + { + auto it = members_.begin() + result.first; + return it; + } + } + } + + bool operator==(const order_preserving_json_object& rhs) const + { + return members_ == rhs.members_; + } + + bool operator<(const order_preserving_json_object& rhs) const + { + return members_ < rhs.members_; + } + private: + + void flatten_and_destroy() noexcept + { + if (!members_.empty()) + { + json_array temp(get_allocator()); + + for (auto&& kv : members_) + { + if (kv.value().size() > 0) + { + temp.emplace_back(std::move(kv.value())); + } + } + } + } + + std::pair insert_index_entry(const string_view_type& key, std::size_t pos) + { + JSONCONS_ASSERT(pos <= index_.size()); + + auto it = std::lower_bound(index_.begin(),index_.end(), key, + Comp(members_)); + + if (it == index_.end()) + { + std::size_t count = index_.size() - pos; + for (std::size_t i = 0; count > 0 && i < index_.size(); ++i) + { + if (index_[i] >= pos) + { + ++index_[i]; + --count; + } + } + index_.push_back(pos); + return std::make_pair(pos,true); + } + else if (members_.at(*it).key() != key) + { + std::size_t count = index_.size() - pos; + for (std::size_t i = 0; count > 0 && i < index_.size(); ++i) + { + if (index_[i] >= pos) + { + ++index_[i]; + --count; + } + } + auto it2 = index_.insert(it, pos); + return std::make_pair(*it2,true); + } + else + { + return std::make_pair(*it,false); + } + } + + void erase_index_entries(std::size_t pos1, std::size_t pos2) + { + JSONCONS_ASSERT(pos1 <= pos2); + JSONCONS_ASSERT(pos2 <= index_.size()); + + const size_t offset = pos2 - pos1; + const size_t n = index_.size() - offset; + for (std::size_t i = 0; i < index_.size(); ++i) + { + if (index_[i] >= pos1 && index_[i] < pos2) + { + index_.erase(index_.begin()+i); + --i; + } + } + for (std::size_t i = 0; i < index_.size(); ++i) + { + if (index_[i] >= pos2) + { + index_[i] -= offset; + } + } + JSONCONS_ASSERT(index_.size() == n); + } + + void build_index() + { + index_.clear(); + index_.reserve(members_.size()); + for (std::size_t i = 0; i < members_.size(); ++i) + { + index_.push_back(i); + } + std::stable_sort(index_.begin(),index_.end(), + [&](std::size_t a, std::size_t b) -> bool {return members_.at(a).key().compare(members_.at(b).key()) < 0;}); + } + }; + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/json_options.hpp b/include/jsoncons/json_options.hpp new file mode 100644 index 0000000..e73f233 --- /dev/null +++ b/include/jsoncons/json_options.hpp @@ -0,0 +1,862 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_OPTIONS_HPP +#define JSONCONS_JSON_OPTIONS_HPP + +#include +#include // std::numeric_limits +#include +#include +#include + +namespace jsoncons { + +enum class float_chars_format : uint8_t {general,fixed,scientific,hex}; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use float_chars_format") typedef float_chars_format chars_format; +#endif + +enum class indenting : uint8_t {no_indent = 0, indent = 1}; + +enum class line_split_kind : uint8_t {same_line,new_line,multi_line}; + +enum class bigint_chars_format : uint8_t {number, base10, base64, base64url +#if !defined(JSONCONS_NO_DEPRECATED) +,integer = number +#endif +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use bigint_chars_format") typedef bigint_chars_format bignum_chars_format; +JSONCONS_DEPRECATED_MSG("Instead, use bigint_chars_format") typedef bigint_chars_format big_integer_chars_format; +#endif + +enum class byte_string_chars_format : uint8_t {none=0,base16,base64,base64url}; + +enum class spaces_option : uint8_t {no_spaces=0,space_after,space_before,space_before_and_after}; + +template +class basic_json_options; + +template +class basic_json_options_common +{ + friend class basic_json_options; +public: + using char_type = CharT; + using string_type = std::basic_string; +private: +#if !defined(JSONCONS_NO_DEPRECATED) + bool can_read_nan_replacement_; + bool can_read_pos_inf_replacement_; + bool can_read_neg_inf_replacement_; + string_type nan_replacement_; + string_type pos_inf_replacement_; + string_type neg_inf_replacement_; +#endif + + bool enable_nan_to_num_:1; + bool enable_inf_to_num_:1; + bool enable_neginf_to_num_:1; + bool enable_nan_to_str_:1; + bool enable_inf_to_str_:1; + bool enable_neginf_to_str_:1; + bool enable_str_to_nan_:1; + bool enable_str_to_inf_:1; + bool enable_str_to_neginf_:1; + + string_type nan_to_num_; + string_type inf_to_num_; + string_type neginf_to_num_; + string_type nan_to_str_; + string_type inf_to_str_; + string_type neginf_to_str_; + int max_nesting_depth_; + +protected: + basic_json_options_common() + : +#if !defined(JSONCONS_NO_DEPRECATED) + can_read_nan_replacement_(false), + can_read_pos_inf_replacement_(false), + can_read_neg_inf_replacement_(false), +#endif + enable_nan_to_num_(false), + enable_inf_to_num_(false), + enable_neginf_to_num_(false), + enable_nan_to_str_(false), + enable_inf_to_str_(false), + enable_neginf_to_str_(false), + enable_str_to_nan_(false), + enable_str_to_inf_(false), + enable_str_to_neginf_(false), + max_nesting_depth_(1024) + {} + + virtual ~basic_json_options_common() noexcept = default; + + basic_json_options_common(const basic_json_options_common&) = default; + basic_json_options_common& operator=(const basic_json_options_common&) = default; + basic_json_options_common(basic_json_options_common&&) = default; + basic_json_options_common& operator=(basic_json_options_common&&) = default; + +public: + + bool enable_nan_to_num() const + { + return enable_nan_to_num_; + } + + bool enable_inf_to_num() const + { + return enable_inf_to_num_; + } + + bool enable_neginf_to_num() const + { + return enable_neginf_to_num_ || enable_inf_to_num_; + } + + bool enable_nan_to_str() const + { + return enable_nan_to_str_; + } + + bool enable_str_to_nan() const + { + return enable_str_to_nan_; + } + + bool enable_inf_to_str() const + { + return enable_inf_to_str_; + } + + bool enable_str_to_inf() const + { + return enable_str_to_inf_; + } + + bool enable_neginf_to_str() const + { + return enable_neginf_to_str_ || enable_inf_to_str_; + } + + bool enable_str_to_neginf() const + { + return enable_str_to_neginf_ || enable_str_to_inf_; + } + + string_type nan_to_num() const + { + if (enable_nan_to_num_) + { + return nan_to_num_; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (!can_read_nan_replacement_) // not string + { + return nan_replacement_; + } +#endif + else + { + return nan_to_num_; // empty string + } + } + + string_type inf_to_num() const + { + if (enable_inf_to_num_) + { + return inf_to_num_; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (!can_read_pos_inf_replacement_) // not string + { + return pos_inf_replacement_; + } +#endif + else + { + return inf_to_num_; // empty string + } + } + + string_type neginf_to_num() const + { + if (enable_neginf_to_num_) + { + return neginf_to_num_; + } + else if (enable_inf_to_num_) + { + string_type s; + s.push_back('-'); + s.append(inf_to_num_); + return s; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (!can_read_neg_inf_replacement_) // not string + { + return neg_inf_replacement_; + } +#endif + else + { + return neginf_to_num_; // empty string + } + } + + string_type nan_to_str() const + { + if (enable_nan_to_str_) + { + return nan_to_str_; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (can_read_nan_replacement_ && nan_replacement_.size() >= 2) // string + { + return nan_replacement_.substr(1, nan_replacement_.size() - 2); // Remove quotes + } +#endif + else + { + return nan_to_str_; // empty string + } + } + + string_type inf_to_str() const + { + if (enable_inf_to_str_) + { + return inf_to_str_; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (can_read_pos_inf_replacement_ && pos_inf_replacement_.size() >= 2) // string + { + return pos_inf_replacement_.substr(1, pos_inf_replacement_.size() - 2); // Strip quotes + } +#endif + else + { + return inf_to_str_; // empty string + } + } + + string_type neginf_to_str() const + { + if (enable_neginf_to_str_) + { + return neginf_to_str_; + } + else if (enable_inf_to_str_) + { + string_type s; + s.push_back('-'); + s.append(inf_to_str_); + return s; + } +#if !defined(JSONCONS_NO_DEPRECATED) + else if (can_read_neg_inf_replacement_ && neg_inf_replacement_.size() >= 2) // string + { + return neg_inf_replacement_.substr(1, neg_inf_replacement_.size() - 2); // Strip quotes + } +#endif + else + { + return neginf_to_str_; // empty string + } + } + + int max_nesting_depth() const + { + return max_nesting_depth_; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use enable_nan_to_num() or enable_nan_to_str()") + bool can_read_nan_replacement() const { return can_read_nan_replacement_; } + + JSONCONS_DEPRECATED_MSG("Instead, use enable_inf_to_num() or enable_inf_to_str()") + bool can_read_pos_inf_replacement() const { return can_read_pos_inf_replacement_; } + + JSONCONS_DEPRECATED_MSG("Instead, use enable_neginf_to_num() or enable_neginf_to_str()") + bool can_read_neg_inf_replacement() const { return can_read_neg_inf_replacement_; } + + bool can_write_nan_replacement() const { return !nan_replacement_.empty(); } + + bool can_write_pos_inf_replacement() const { return !pos_inf_replacement_.empty(); } + + bool can_write_neg_inf_replacement() const { return !neg_inf_replacement_.empty(); } + + JSONCONS_DEPRECATED_MSG("Instead, use nan_to_num() or nan_to_str()") + const string_type& nan_replacement() const + { + return nan_replacement_; + } + + JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num() or inf_to_str()") + const string_type& pos_inf_replacement() const + { + return pos_inf_replacement_; + } + + JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num() or neginf_to_str()") + const string_type& neg_inf_replacement() const + { + return neg_inf_replacement_; + } +#endif +}; + +template +class basic_json_decode_options : public virtual basic_json_options_common +{ + friend class basic_json_options; + using super_type = basic_json_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; +private: + bool lossless_number_:1; +public: + basic_json_decode_options() + : lossless_number_(false) + { + } + + basic_json_decode_options(const basic_json_decode_options&) = default; + + basic_json_decode_options(basic_json_decode_options&& other) + : super_type(std::forward(other)), + lossless_number_(other.lossless_number_) + { + } + + basic_json_decode_options& operator=(const basic_json_decode_options&) = default; + + bool lossless_number() const + { + return lossless_number_; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use lossless_number()") + bool dec_to_str() const + { + return lossless_number_; + } +#endif +}; + +template +class basic_json_encode_options : public virtual basic_json_options_common +{ + friend class basic_json_options; + using super_type = basic_json_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; + + static constexpr uint8_t indent_size_default = 4; + static constexpr size_t line_length_limit_default = 120; +private: + bool escape_all_non_ascii_:1; + bool escape_solidus_:1; + bool pad_inside_object_braces_:1; + bool pad_inside_array_brackets_:1; + float_chars_format float_format_; + byte_string_chars_format byte_string_format_; + bigint_chars_format bigint_format_; + line_split_kind object_object_line_splits_; + line_split_kind object_array_line_splits_; + line_split_kind array_array_line_splits_; + line_split_kind array_object_line_splits_; + spaces_option spaces_around_colon_; + spaces_option spaces_around_comma_; + int8_t precision_; + uint8_t indent_size_; + std::size_t line_length_limit_; + string_type new_line_chars_; +public: + basic_json_encode_options() + : escape_all_non_ascii_(false), + escape_solidus_(false), + pad_inside_object_braces_(false), + pad_inside_array_brackets_(false), + float_format_(float_chars_format::general), + byte_string_format_(byte_string_chars_format::none), + bigint_format_(bigint_chars_format::base10), + object_object_line_splits_(line_split_kind::multi_line), + object_array_line_splits_(line_split_kind::same_line), + array_array_line_splits_(line_split_kind::new_line), + array_object_line_splits_(line_split_kind::multi_line), + spaces_around_colon_(spaces_option::space_after), + spaces_around_comma_(spaces_option::space_after), + precision_(0), + indent_size_(indent_size_default), + line_length_limit_(line_length_limit_default) + { + new_line_chars_.push_back('\n'); + } + + basic_json_encode_options(const basic_json_encode_options&) = default; + + basic_json_encode_options(basic_json_encode_options&& other) + : super_type(std::forward(other)), + escape_all_non_ascii_(other.escape_all_non_ascii_), + escape_solidus_(other.escape_solidus_), + pad_inside_object_braces_(other.pad_inside_object_braces_), + pad_inside_array_brackets_(other.pad_inside_array_brackets_), + float_format_(other.float_format_), + byte_string_format_(other.byte_string_format_), + bigint_format_(other.bigint_format_), + object_object_line_splits_(other.object_object_line_splits_), + object_array_line_splits_(other.object_array_line_splits_), + array_array_line_splits_(other.array_array_line_splits_), + array_object_line_splits_(other.array_object_line_splits_), + spaces_around_colon_(other.spaces_around_colon_), + spaces_around_comma_(other.spaces_around_comma_), + precision_(other.precision_), + indent_size_(other.indent_size_), + line_length_limit_(other.line_length_limit_), + new_line_chars_(std::move(other.new_line_chars_)) + { + } + + basic_json_encode_options& operator=(const basic_json_encode_options&) = default; + + byte_string_chars_format byte_string_format() const {return byte_string_format_;} + + bigint_chars_format bigint_format() const {return bigint_format_;} + + line_split_kind object_object_line_splits() const {return object_object_line_splits_;} + + line_split_kind array_object_line_splits() const {return array_object_line_splits_;} + + line_split_kind object_array_line_splits() const {return object_array_line_splits_;} + + line_split_kind array_array_line_splits() const {return array_array_line_splits_;} + + uint8_t indent_size() const + { + return indent_size_; + } + + spaces_option spaces_around_colon() const + { + return spaces_around_colon_; + } + + spaces_option spaces_around_comma() const + { + return spaces_around_comma_; + } + + bool pad_inside_object_braces() const + { + return pad_inside_object_braces_; + } + + bool pad_inside_array_brackets() const + { + return pad_inside_array_brackets_; + } + + string_type new_line_chars() const + { + return new_line_chars_; + } + + std::size_t line_length_limit() const + { + return line_length_limit_; + } + + float_chars_format float_format() const + { + return float_format_; + } + + int8_t precision() const + { + return precision_; + } + + bool escape_all_non_ascii() const + { + return escape_all_non_ascii_; + } + + bool escape_solidus() const + { + return escape_solidus_; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use bigint_format()") + bigint_chars_format bignum_format() const {return bigint_format_;} + + JSONCONS_DEPRECATED_MSG("Instead, use indent_size()") + uint8_t indent() const + { + return indent_size(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_object_line_splits()") + line_split_kind object_object_split_lines() const {return object_object_line_splits_;} + + JSONCONS_DEPRECATED_MSG("Instead, use array_object_line_splits()") + line_split_kind array_object_split_lines() const {return array_object_line_splits_;} + + JSONCONS_DEPRECATED_MSG("Instead, use object_array_line_splits()") + line_split_kind object_array_split_lines() const {return object_array_line_splits_;} + + JSONCONS_DEPRECATED_MSG("Instead, use array_array_line_splits()") + line_split_kind array_array_split_lines() const {return array_array_line_splits_;} +#endif +}; + +template +class basic_json_options final: public basic_json_decode_options, + public basic_json_encode_options +{ +public: + using char_type = CharT; + using string_type = std::basic_string; + + using basic_json_options_common::max_nesting_depth; + + using basic_json_decode_options::enable_str_to_nan; + using basic_json_decode_options::enable_str_to_inf; + using basic_json_decode_options::enable_str_to_neginf; + using basic_json_decode_options::nan_to_str; + using basic_json_decode_options::inf_to_str; + using basic_json_decode_options::neginf_to_str; + using basic_json_decode_options::nan_to_num; + using basic_json_decode_options::inf_to_num; + using basic_json_decode_options::neginf_to_num; + + using basic_json_decode_options::lossless_number; + + using basic_json_encode_options::byte_string_format; + using basic_json_encode_options::bigint_format; + using basic_json_encode_options::object_object_line_splits; + using basic_json_encode_options::array_object_line_splits; + using basic_json_encode_options::object_array_line_splits; + using basic_json_encode_options::array_array_line_splits; + using basic_json_encode_options::indent_size; + using basic_json_encode_options::spaces_around_colon; + using basic_json_encode_options::spaces_around_comma; + using basic_json_encode_options::pad_inside_object_braces; + using basic_json_encode_options::pad_inside_array_brackets; + using basic_json_encode_options::new_line_chars; + using basic_json_encode_options::line_length_limit; + using basic_json_encode_options::float_format; + using basic_json_encode_options::precision; + using basic_json_encode_options::escape_all_non_ascii; + using basic_json_encode_options::escape_solidus; +public: + +// Constructors + + basic_json_options() = default; + basic_json_options(const basic_json_options&) = default; + basic_json_options(basic_json_options&&) = default; + basic_json_options& operator=(const basic_json_options&) = default; + basic_json_options& operator=(basic_json_options&&) = default; + + basic_json_options& nan_to_num(const string_type& value) + { + this->enable_nan_to_num_ = true; + this->nan_to_str_.clear(); + this->nan_to_num_ = value; + return *this; + } + + basic_json_options& inf_to_num(const string_type& value) + { + this->enable_inf_to_num_ = true; + this->inf_to_str_.clear(); + this->inf_to_num_ = value; + return *this; + } + + basic_json_options& neginf_to_num(const string_type& value) + { + this->enable_neginf_to_num_ = true; + this->neginf_to_str_.clear(); + this->neginf_to_num_ = value; + return *this; + } + + basic_json_options& nan_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_nan_to_str_ = true; + this->enable_str_to_nan_ = enable_inverse; + this->nan_to_num_.clear(); + this->nan_to_str_ = value; + return *this; + } + + basic_json_options& inf_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_inf_to_str_ = true; + this->enable_inf_to_str_ = enable_inverse; + this->inf_to_num_.clear(); + this->inf_to_str_ = value; + return *this; + } + + basic_json_options& neginf_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_neginf_to_str_ = true; + this->enable_neginf_to_str_ = enable_inverse; + this->neginf_to_num_.clear(); + this->neginf_to_str_ = value; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)") + basic_json_options& replace_inf(bool replace) + { + this->can_read_pos_inf_replacement_ = replace; + this->can_read_neg_inf_replacement_ = replace; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)") + basic_json_options& replace_pos_inf(bool replace) + { + this->can_read_pos_inf_replacement_ = replace; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num(const string_type&) or neginf_to_str(const string_type&)") + basic_json_options& replace_neg_inf(bool replace) + { + this->can_read_neg_inf_replacement_ = replace; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use nan_to_num(const string_type&) or nan_to_str(const string_type&)") + basic_json_options& nan_replacement(const string_type& value) + { + this->nan_replacement_ = value; + + this->can_read_nan_replacement_ = is_string(value); + + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)") + basic_json_options& pos_inf_replacement(const string_type& value) + { + this->pos_inf_replacement_ = value; + this->can_read_pos_inf_replacement_ = is_string(value); + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num(const string_type&) or neginf_to_str(const string_type&)") + basic_json_options& neg_inf_replacement(const string_type& value) + { + this->neg_inf_replacement_ = value; + this->can_read_neg_inf_replacement_ = is_string(value); + return *this; + } + + basic_json_options& byte_string_format(byte_string_chars_format value) {this->byte_string_format_ = value; return *this;} + + basic_json_options& bigint_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;} + + basic_json_options& object_object_line_splits(line_split_kind value) {this->object_object_line_splits_ = value; return *this;} + + basic_json_options& array_object_line_splits(line_split_kind value) {this->array_object_line_splits_ = value; return *this;} + + basic_json_options& object_array_line_splits(line_split_kind value) {this->object_array_line_splits_ = value; return *this;} + + basic_json_options& array_array_line_splits(line_split_kind value) {this->array_array_line_splits_ = value; return *this;} + + basic_json_options& indent_size(uint8_t value) + { + this->indent_size_ = value; + return *this; + } + + basic_json_options& spaces_around_colon(spaces_option value) + { + this->spaces_around_colon_ = value; + return *this; + } + + basic_json_options& spaces_around_comma(spaces_option value) + { + this->spaces_around_comma_ = value; + return *this; + } + + basic_json_options& pad_inside_object_braces(bool value) + { + this->pad_inside_object_braces_ = value; + return *this; + } + + basic_json_options& pad_inside_array_brackets(bool value) + { + this->pad_inside_array_brackets_ = value; + return *this; + } + + basic_json_options& new_line_chars(const string_type& value) + { + this->new_line_chars_ = value; + return *this; + } + + basic_json_options& lossless_number(bool value) + { + this->lossless_number_ = value; + return *this; + } + + basic_json_options& line_length_limit(std::size_t value) + { + this->line_length_limit_ = value; + return *this; + } + + basic_json_options& float_format(float_chars_format value) + { + this->float_format_ = value; + return *this; + } + + basic_json_options& precision(int8_t value) + { + this->precision_ = value; + return *this; + } + + basic_json_options& escape_all_non_ascii(bool value) + { + this->escape_all_non_ascii_ = value; + return *this; + } + + basic_json_options& escape_solidus(bool value) + { + this->escape_solidus_ = value; + return *this; + } + + basic_json_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use bigint_format(bigint_chars_format)") + basic_json_options& big_integer_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;} + + JSONCONS_DEPRECATED_MSG("Instead, use bigint_format(bigint_chars_format)") + basic_json_options& bignum_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;} + + JSONCONS_DEPRECATED_MSG("Instead, use float_format(float_chars_format)") + basic_json_options& floating_point_format(float_chars_format value) + { + this->float_format_ = value; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use lossless_number(bool)") + basic_json_options& dec_to_str(bool value) + { + this->lossless_number_ = value; + return *this; + } + + JSONCONS_DEPRECATED_MSG("Instead, use indent_size(uint8_t_t)") + basic_json_options& indent(uint8_t value) + { + return indent_size(value); + } + + JSONCONS_DEPRECATED_MSG("Instead, use object_object_line_splits(line_split_kind)") + basic_json_options& object_object_split_lines(line_split_kind value) {this->object_object_line_splits_ = value; return *this;} + + JSONCONS_DEPRECATED_MSG("Instead, use array_object_line_splits(line_split_kind)") + basic_json_options& array_object_split_lines(line_split_kind value) {this->array_object_line_splits_ = value; return *this;} + + JSONCONS_DEPRECATED_MSG("Instead, use object_array_line_splits(line_split_kind)") + basic_json_options& object_array_split_lines(line_split_kind value) {this->object_array_line_splits_ = value; return *this;} + + JSONCONS_DEPRECATED_MSG("Instead, use array_array_line_splits(line_split_kind)") + basic_json_options& array_array_split_lines(line_split_kind value) {this->array_array_line_splits_ = value; return *this;} +#endif +private: + enum class input_state {initial,begin_quote,character,end_quote,escape,error}; + bool is_string(const string_type& s) const + { + input_state state = input_state::initial; + for (char_type c : s) + { + switch (c) + { + case '\t': case ' ': case '\n': case'\r': + break; + case '\\': + state = input_state::escape; + break; + case '\"': + switch (state) + { + case input_state::initial: + state = input_state::begin_quote; + break; + case input_state::begin_quote: + state = input_state::end_quote; + break; + case input_state::character: + state = input_state::end_quote; + break; + case input_state::end_quote: + state = input_state::error; + break; + case input_state::escape: + state = input_state::character; + break; + default: + state = input_state::character; + break; + } + break; + default: + break; + } + + } + return state == input_state::end_quote; + } +}; + +using json_options = basic_json_options; +using wjson_options = basic_json_options; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("json_options") typedef json_options output_format; +JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options woutput_format; +JSONCONS_DEPRECATED_MSG("json_options") typedef json_options serialization_options; +JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options wserialization_options; +JSONCONS_DEPRECATED_MSG("json_options") typedef json_options json_serializing_options; +JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options wjson_serializing_options; +#endif + +} +#endif diff --git a/include/jsoncons/json_parser.hpp b/include/jsoncons/json_parser.hpp new file mode 100644 index 0000000..4ba9bf1 --- /dev/null +++ b/include/jsoncons/json_parser.hpp @@ -0,0 +1,2871 @@ +// Copyright 2015 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_PARSER_HPP +#define JSONCONS_JSON_PARSER_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::numeric_limits +#include // std::function +#include +#include +#include +#include +#include +#include + +#define JSONCONS_ILLEGAL_CONTROL_CHARACTER \ + case 0x00:case 0x01:case 0x02:case 0x03:case 0x04:case 0x05:case 0x06:case 0x07:case 0x08:case 0x0b: \ + case 0x0c:case 0x0e:case 0x0f:case 0x10:case 0x11:case 0x12:case 0x13:case 0x14:case 0x15:case 0x16: \ + case 0x17:case 0x18:case 0x19:case 0x1a:case 0x1b:case 0x1c:case 0x1d:case 0x1e:case 0x1f + +namespace jsoncons { + +namespace detail { + +} + +enum class json_parse_state : uint8_t +{ + root, + start, + accept, + slash, + slash_slash, + slash_star, + slash_star_star, + expect_comma_or_end, + object, + expect_member_name_or_end, + expect_member_name, + expect_colon, + expect_value_or_end, + expect_value, + array, + string, + member_name, + escape, + escape_u1, + escape_u2, + escape_u3, + escape_u4, + escape_expect_surrogate_pair1, + escape_expect_surrogate_pair2, + escape_u5, + escape_u6, + escape_u7, + escape_u8, + minus, + zero, + integer, + fraction1, + fraction2, + exp1, + exp2, + exp3, + n, + nu, + nul, + t, + tr, + tru, + f, + fa, + fal, + fals, + cr, + done +}; + +struct default_json_parsing +{ + bool operator()(json_errc ec, const ser_context&) noexcept + { + if (ec == json_errc::illegal_comment) + { + return true; // Recover, allow comments + } + else + { + return false; + } + } +}; + +struct strict_json_parsing +{ + bool operator()(json_errc, const ser_context&) noexcept + { + return false; + } +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use default_json_parsing") typedef default_json_parsing default_parse_error_handler; +JSONCONS_DEPRECATED_MSG("Instead, use strict_json_parsing") typedef strict_json_parsing strict_parse_error_handler; +#endif + +template > +class basic_json_parser : public ser_context +{ +public: + using char_type = CharT; + using string_view_type = typename basic_json_visitor::string_view_type; +private: + struct string_maps_to_double + { + string_view_type s; + + bool operator()(const std::pair& val) const + { + return val.first == s; + } + }; + + using temp_allocator_type = TempAllocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + static constexpr std::size_t initial_string_buffer_capacity_ = 1024; + static constexpr std::size_t default_initial_stack_capacity_ = 100; + + basic_json_decode_options options_; + + std::function err_handler_; + int initial_stack_capacity_; + int nesting_depth_; + uint32_t cp_; + uint32_t cp2_; + std::size_t line_; + std::size_t position_; + std::size_t mark_position_; + std::size_t saved_position_; + const char_type* begin_input_; + const char_type* end_input_; + const char_type* input_ptr_; + json_parse_state state_; + bool more_; + bool done_; + + std::basic_string,char_allocator_type> string_buffer_; + jsoncons::detail::chars_to to_double_; + + std::vector state_stack_; + std::vector> string_double_map_; + + // Noncopyable and nonmoveable + basic_json_parser(const basic_json_parser&) = delete; + basic_json_parser& operator=(const basic_json_parser&) = delete; + +public: + basic_json_parser(const TempAllocator& alloc = TempAllocator()) + : basic_json_parser(basic_json_decode_options(), default_json_parsing(), alloc) + { + } + + basic_json_parser(std::function err_handler, + const TempAllocator& alloc = TempAllocator()) + : basic_json_parser(basic_json_decode_options(), err_handler, alloc) + { + } + + basic_json_parser(const basic_json_decode_options& options, + const TempAllocator& alloc = TempAllocator()) + : basic_json_parser(options, default_json_parsing(), alloc) + { + } + + basic_json_parser(const basic_json_decode_options& options, + std::function err_handler, + const TempAllocator& alloc = TempAllocator()) + : options_(options), + err_handler_(err_handler), + initial_stack_capacity_(default_initial_stack_capacity_), + nesting_depth_(0), + cp_(0), + cp2_(0), + line_(1), + position_(0), + mark_position_(0), + saved_position_(0), + begin_input_(nullptr), + end_input_(nullptr), + input_ptr_(nullptr), + state_(json_parse_state::start), + more_(true), + done_(false), + string_buffer_(alloc), + state_stack_(alloc) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + + state_stack_.reserve(initial_stack_capacity_); + push_state(json_parse_state::root); + + if (options_.enable_str_to_nan()) + { + string_double_map_.emplace_back(options_.nan_to_str(),std::nan("")); + } + if (options_.enable_str_to_inf()) + { + string_double_map_.emplace_back(options_.inf_to_str(),std::numeric_limits::infinity()); + } + if (options_.enable_str_to_neginf()) + { + string_double_map_.emplace_back(options_.neginf_to_str(),-std::numeric_limits::infinity()); + } + } + + bool source_exhausted() const + { + return input_ptr_ == end_input_; + } + + ~basic_json_parser() noexcept + { + } + + json_parse_state parent() const + { + JSONCONS_ASSERT(state_stack_.size() >= 1); + return state_stack_.back(); + } + + bool done() const + { + return done_; + } + + bool enter() const + { + return state_ == json_parse_state::start; + } + + bool accept() const + { + return state_ == json_parse_state::accept || done_; + } + + bool stopped() const + { + return !more_; + } + + json_parse_state state() const + { + return state_; + } + + bool finished() const + { + return !more_ && state_ != json_parse_state::accept; + } + + const char_type* first() const + { + return begin_input_; + } + + const char_type* current() const + { + return input_ptr_; + } + + const char_type* last() const + { + return end_input_; + } + + void skip_space() + { + const char_type* local_input_end = end_input_; + while (input_ptr_ != local_input_end) + { + switch (*input_ptr_) + { + case ' ': + case '\t': + ++input_ptr_; + ++position_; + break; + case '\r': + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + return; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + return; + default: + return; + } + } + } + + void skip_whitespace() + { + const char_type* local_input_end = end_input_; + + while (input_ptr_ != local_input_end) + { + switch (state_) + { + case json_parse_state::cr: + ++line_; + ++position_; + mark_position_ = position_; + switch (*input_ptr_) + { + case '\n': + ++input_ptr_; + ++position_; + state_ = pop_state(); + break; + default: + state_ = pop_state(); + break; + } + break; + + default: + switch (*input_ptr_) + { + case ' ': + case '\t': + case '\n': + case '\r': + skip_space(); + break; + default: + return; + } + break; + } + } + } + + void begin_object(basic_json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + more_ = err_handler_(json_errc::max_nesting_depth_exceeded, *this); + if (!more_) + { + ec = json_errc::max_nesting_depth_exceeded; + return; + } + } + + push_state(json_parse_state::object); + state_ = json_parse_state::expect_member_name_or_end; + more_ = visitor.begin_object(semantic_tag::none, *this, ec); + } + + void end_object(basic_json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(nesting_depth_ < 1)) + { + err_handler_(json_errc::unexpected_rbrace, *this); + ec = json_errc::unexpected_rbrace; + more_ = false; + return; + } + --nesting_depth_; + state_ = pop_state(); + if (state_ == json_parse_state::object) + { + more_ = visitor.end_object(*this, ec); + } + else if (state_ == json_parse_state::array) + { + err_handler_(json_errc::expected_comma_or_rbracket, *this); + ec = json_errc::expected_comma_or_rbracket; + more_ = false; + return; + } + else + { + err_handler_(json_errc::unexpected_rbrace, *this); + ec = json_errc::unexpected_rbrace; + more_ = false; + return; + } + + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + } + + void begin_array(basic_json_visitor& visitor, std::error_code& ec) + { + if (++nesting_depth_ > options_.max_nesting_depth()) + { + more_ = err_handler_(json_errc::max_nesting_depth_exceeded, *this); + if (!more_) + { + ec = json_errc::max_nesting_depth_exceeded; + return; + } + } + + push_state(json_parse_state::array); + state_ = json_parse_state::expect_value_or_end; + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + } + + void end_array(basic_json_visitor& visitor, std::error_code& ec) + { + if (nesting_depth_ < 1) + { + err_handler_(json_errc::unexpected_rbracket, *this); + ec = json_errc::unexpected_rbracket; + more_ = false; + return; + } + --nesting_depth_; + state_ = pop_state(); + if (state_ == json_parse_state::array) + { + more_ = visitor.end_array(*this, ec); + } + else if (state_ == json_parse_state::object) + { + err_handler_(json_errc::expected_comma_or_rbrace, *this); + ec = json_errc::expected_comma_or_rbrace; + more_ = false; + return; + } + else + { + err_handler_(json_errc::unexpected_rbracket, *this); + ec = json_errc::unexpected_rbracket; + more_ = false; + return; + } + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + } + + void reinitialize() + { + reset(); + cp_ = 0; + cp2_ = 0; + saved_position_ = 0; + begin_input_ = nullptr; + end_input_ = nullptr; + input_ptr_ = nullptr; + string_buffer_.clear(); + } + + void reset() + { + state_stack_.clear(); + state_stack_.reserve(initial_stack_capacity_); + push_state(json_parse_state::root); + state_ = json_parse_state::start; + more_ = true; + done_ = false; + line_ = 1; + position_ = 0; + mark_position_ = 0; + nesting_depth_ = 0; + } + + void restart() + { + more_ = true; + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column())); + } + } + + void check_done(std::error_code& ec) + { + for (; input_ptr_ != end_input_; ++input_ptr_) + { + char_type curr_char_ = *input_ptr_; + switch (curr_char_) + { + case '\n': + case '\r': + case '\t': + case ' ': + break; + default: + more_ = err_handler_(json_errc::extra_character, *this); + if (!more_) + { + ec = json_errc::extra_character; + return; + } + break; + } + } + } + + void update(const string_view_type sv) + { + update(sv.data(),sv.length()); + } + + void update(const char_type* data, std::size_t length) + { + begin_input_ = data; + end_input_ = data + length; + input_ptr_ = begin_input_; + } + + void parse_some(basic_json_visitor& visitor) + { + std::error_code ec; + parse_some(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column())); + } + } + + void parse_some(basic_json_visitor& visitor, std::error_code& ec) + { + parse_some_(visitor, ec); + } + + void finish_parse(basic_json_visitor& visitor) + { + std::error_code ec; + finish_parse(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column())); + } + } + + void finish_parse(basic_json_visitor& visitor, std::error_code& ec) + { + while (!finished()) + { + parse_some(visitor, ec); + } + } + + void parse_some_(basic_json_visitor& visitor, std::error_code& ec) + { + if (state_ == json_parse_state::accept) + { + visitor.flush(); + done_ = true; + state_ = json_parse_state::done; + more_ = false; + return; + } + const char_type* local_input_end = end_input_; + + if (input_ptr_ == local_input_end && more_) + { + switch (state_) + { + case json_parse_state::zero: + case json_parse_state::integer: + end_integer_value(visitor, ec); + if (ec) return; + break; + case json_parse_state::fraction2: + end_fraction_value(visitor, ec); + if (ec) return; + break; + case json_parse_state::exp3: + end_fraction_value(visitor, ec); + if (ec) return; + break; + case json_parse_state::accept: + visitor.flush(); + done_ = true; + state_ = json_parse_state::done; + more_ = false; + break; + case json_parse_state::start: + case json_parse_state::done: + more_ = false; + break; + case json_parse_state::cr: + state_ = pop_state(); + break; + default: + err_handler_(json_errc::unexpected_eof, *this); + ec = json_errc::unexpected_eof; + more_ = false; + return; + } + } + + while ((input_ptr_ < local_input_end) && more_) + { + switch (state_) + { + case json_parse_state::accept: + visitor.flush(); + done_ = true; + state_ = json_parse_state::done; + more_ = false; + break; + case json_parse_state::cr: + ++line_; + mark_position_ = position_; + switch (*input_ptr_) + { + case '\n': + ++input_ptr_; + ++position_; + state_ = pop_state(); + break; + default: + state_ = pop_state(); + break; + } + break; + case json_parse_state::start: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + break; + case '\r': + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + break; + case '{': + begin_object(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '[': + begin_array(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '\"': + state_ = json_parse_state::string; + ++input_ptr_; + ++position_; + string_buffer_.clear(); + parse_string(visitor, ec); + if (ec) return; + break; + case '-': + string_buffer_.clear(); + string_buffer_.push_back('-'); + ++input_ptr_; + ++position_; + state_ = json_parse_state::minus; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '0': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + state_ = json_parse_state::zero; + ++input_ptr_; + ++position_; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + state_ = json_parse_state::integer; + parse_number(visitor, ec); + if (ec) {return;} + break; + case 'n': + parse_null(visitor, ec); + if (ec) {return;} + break; + case 't': + parse_true(visitor, ec); + if (ec) {return;} + break; + case 'f': + parse_false(visitor, ec); + if (ec) {return;} + break; + case '}': + err_handler_(json_errc::unexpected_rbrace, *this); + ec = json_errc::unexpected_rbrace; + more_ = false; + return; + case ']': + err_handler_(json_errc::unexpected_rbracket, *this); + ec = json_errc::unexpected_rbracket; + more_ = false; + return; + default: + err_handler_(json_errc::syntax_error, *this); + ec = json_errc::syntax_error; + more_ = false; + return; + } + } + break; + + case json_parse_state::expect_comma_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + break; + case '}': + end_object(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case ']': + end_array(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case ',': + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + default: + if (parent() == json_parse_state::array) + { + more_ = err_handler_(json_errc::expected_comma_or_rbracket, *this); + if (!more_) + { + ec = json_errc::expected_comma_or_rbracket; + return; + } + } + else if (parent() == json_parse_state::object) + { + more_ = err_handler_(json_errc::expected_comma_or_rbrace, *this); + if (!more_) + { + ec = json_errc::expected_comma_or_rbrace; + return; + } + } + ++input_ptr_; + ++position_; + break; + } + } + break; + case json_parse_state::expect_member_name_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + break; + case '}': + end_object(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '\"': + ++input_ptr_; + ++position_; + push_state(json_parse_state::member_name); + state_ = json_parse_state::string; + string_buffer_.clear(); + parse_string(visitor, ec); + if (ec) return; + break; + case '\'': + more_ = err_handler_(json_errc::single_quote, *this); + if (!more_) + { + ec = json_errc::single_quote; + return; + } + ++input_ptr_; + ++position_; + break; + default: + more_ = err_handler_(json_errc::expected_key, *this); + if (!more_) + { + ec = json_errc::expected_key; + return; + } + ++input_ptr_; + ++position_; + break; + } + } + break; + case json_parse_state::expect_member_name: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + break; + case '\"': + ++input_ptr_; + ++position_; + push_state(json_parse_state::member_name); + state_ = json_parse_state::string; + string_buffer_.clear(); + parse_string(visitor, ec); + if (ec) return; + break; + case '}': + more_ = err_handler_(json_errc::extra_comma, *this); + if (!more_) + { + ec = json_errc::extra_comma; + return; + } + end_object(visitor, ec); // Recover + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '\'': + more_ = err_handler_(json_errc::single_quote, *this); + if (!more_) + { + ec = json_errc::single_quote; + return; + } + ++input_ptr_; + ++position_; + break; + default: + more_ = err_handler_(json_errc::expected_key, *this); + if (!more_) + { + ec = json_errc::expected_key; + return; + } + ++input_ptr_; + ++position_; + break; + } + } + break; + case json_parse_state::expect_colon: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + push_state(state_); + state_ = json_parse_state::cr; + ++input_ptr_; + ++position_; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + push_state(state_); + state_ = json_parse_state::slash; + ++input_ptr_; + ++position_; + break; + case ':': + state_ = json_parse_state::expect_value; + ++input_ptr_; + ++position_; + break; + default: + more_ = err_handler_(json_errc::expected_colon, *this); + if (!more_) + { + ec = json_errc::expected_colon; + return; + } + ++input_ptr_; + ++position_; + break; + } + } + break; + + case json_parse_state::expect_value: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::slash; + break; + case '{': + begin_object(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '[': + begin_array(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '\"': + ++input_ptr_; + ++position_; + state_ = json_parse_state::string; + string_buffer_.clear(); + parse_string(visitor, ec); + if (ec) return; + break; + case '-': + string_buffer_.clear(); + string_buffer_.push_back('-'); + ++input_ptr_; + ++position_; + state_ = json_parse_state::minus; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '0': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + state_ = json_parse_state::zero; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + state_ = json_parse_state::integer; + parse_number(visitor, ec); + if (ec) {return;} + break; + case 'n': + parse_null(visitor, ec); + if (ec) {return;} + break; + case 't': + parse_true(visitor, ec); + if (ec) {return;} + break; + case 'f': + parse_false(visitor, ec); + if (ec) {return;} + break; + case ']': + if (parent() == json_parse_state::array) + { + more_ = err_handler_(json_errc::extra_comma, *this); + if (!more_) + { + ec = json_errc::extra_comma; + return; + } + end_array(visitor, ec); // Recover + if (ec) return; + } + else + { + more_ = err_handler_(json_errc::expected_value, *this); + if (!more_) + { + ec = json_errc::expected_value; + return; + } + } + ++input_ptr_; + ++position_; + break; + case '\'': + more_ = err_handler_(json_errc::single_quote, *this); + if (!more_) + { + ec = json_errc::single_quote; + return; + } + ++input_ptr_; + ++position_; + break; + default: + more_ = err_handler_(json_errc::expected_value, *this); + if (!more_) + { + ec = json_errc::expected_value; + return; + } + ++input_ptr_; + ++position_; + break; + } + } + break; + case json_parse_state::expect_value_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++position_; + break; + case '\r': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case ' ':case '\t': + skip_space(); + break; + case '/': + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + break; + case '{': + begin_object(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '[': + begin_array(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case ']': + end_array(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + break; + case '\"': + ++input_ptr_; + ++position_; + state_ = json_parse_state::string; + string_buffer_.clear(); + parse_string(visitor, ec); + if (ec) return; + break; + case '-': + string_buffer_.clear(); + string_buffer_.push_back('-'); + ++input_ptr_; + ++position_; + state_ = json_parse_state::minus; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '0': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + state_ = json_parse_state::zero; + parse_number(visitor, ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.clear(); + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + state_ = json_parse_state::integer; + parse_number(visitor, ec); + if (ec) {return;} + break; + case 'n': + parse_null(visitor, ec); + if (ec) {return;} + break; + case 't': + parse_true(visitor, ec); + if (ec) {return;} + break; + case 'f': + parse_false(visitor, ec); + if (ec) {return;} + break; + case '\'': + more_ = err_handler_(json_errc::single_quote, *this); + if (!more_) + { + ec = json_errc::single_quote; + return; + } + ++input_ptr_; + ++position_; + break; + default: + more_ = err_handler_(json_errc::expected_value, *this); + if (!more_) + { + ec = json_errc::expected_value; + return; + } + ++input_ptr_; + ++position_; + break; + } + } + break; + case json_parse_state::string: + case json_parse_state::escape: + case json_parse_state::escape_u1: + case json_parse_state::escape_u2: + case json_parse_state::escape_u3: + case json_parse_state::escape_u4: + case json_parse_state::escape_expect_surrogate_pair1: + case json_parse_state::escape_expect_surrogate_pair2: + case json_parse_state::escape_u5: + case json_parse_state::escape_u6: + case json_parse_state::escape_u7: + case json_parse_state::escape_u8: + parse_string(visitor, ec); + if (ec) return; + break; + case json_parse_state::minus: + case json_parse_state::zero: + case json_parse_state::integer: + case json_parse_state::fraction1: + case json_parse_state::fraction2: + case json_parse_state::exp1: + case json_parse_state::exp2: + case json_parse_state::exp3: + parse_number(visitor, ec); + if (ec) return; + break; + case json_parse_state::t: + switch (*input_ptr_) + { + case 'r': + ++input_ptr_; + ++position_; + state_ = json_parse_state::tr; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + break; + case json_parse_state::tr: + switch (*input_ptr_) + { + case 'u': + state_ = json_parse_state::tru; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::tru: + switch (*input_ptr_) + { + case 'e': + more_ = visitor.bool_value(true, semantic_tag::none, *this, ec); + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::f: + switch (*input_ptr_) + { + case 'a': + ++input_ptr_; + ++position_; + state_ = json_parse_state::fa; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + break; + case json_parse_state::fa: + switch (*input_ptr_) + { + case 'l': + state_ = json_parse_state::fal; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::fal: + switch (*input_ptr_) + { + case 's': + state_ = json_parse_state::fals; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::fals: + switch (*input_ptr_) + { + case 'e': + more_ = visitor.bool_value(false, semantic_tag::none, *this, ec); + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::n: + switch (*input_ptr_) + { + case 'u': + ++input_ptr_; + ++position_; + state_ = json_parse_state::nu; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + break; + case json_parse_state::nu: + switch (*input_ptr_) + { + case 'l': + state_ = json_parse_state::nul; + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::nul: + switch (*input_ptr_) + { + case 'l': + more_ = visitor.null_value(semantic_tag::none, *this, ec); + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + break; + default: + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + ++input_ptr_; + ++position_; + break; + case json_parse_state::slash: + { + switch (*input_ptr_) + { + case '*': + state_ = json_parse_state::slash_star; + more_ = err_handler_(json_errc::illegal_comment, *this); + if (!more_) + { + ec = json_errc::illegal_comment; + return; + } + break; + case '/': + state_ = json_parse_state::slash_slash; + more_ = err_handler_(json_errc::illegal_comment, *this); + if (!more_) + { + ec = json_errc::illegal_comment; + return; + } + break; + default: + more_ = err_handler_(json_errc::syntax_error, *this); + if (!more_) + { + ec = json_errc::syntax_error; + return; + } + break; + } + ++input_ptr_; + ++position_; + break; + } + case json_parse_state::slash_star: + { + switch (*input_ptr_) + { + case '\r': + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + break; + case '*': + ++input_ptr_; + ++position_; + state_ = json_parse_state::slash_star_star; + break; + default: + ++input_ptr_; + ++position_; + break; + } + break; + } + case json_parse_state::slash_slash: + { + switch (*input_ptr_) + { + case '\r': + state_ = pop_state(); + break; + case '\n': + state_ = pop_state(); + break; + default: + ++input_ptr_; + ++position_; + } + break; + } + case json_parse_state::slash_star_star: + { + switch (*input_ptr_) + { + case '/': + state_ = pop_state(); + break; + default: + state_ = json_parse_state::slash_star; + break; + } + ++input_ptr_; + ++position_; + break; + } + default: + JSONCONS_ASSERT(false); + break; + } + } + } + + void parse_true(basic_json_visitor& visitor, std::error_code& ec) + { + saved_position_ = position_; + if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4)) + { + if (*(input_ptr_+1) == 'r' && *(input_ptr_+2) == 'u' && *(input_ptr_+3) == 'e') + { + more_ = visitor.bool_value(true, semantic_tag::none, *this, ec); + input_ptr_ += 4; + position_ += 4; + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + } + else + { + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + } + else + { + ++input_ptr_; + ++position_; + state_ = json_parse_state::t; + } + } + + void parse_null(basic_json_visitor& visitor, std::error_code& ec) + { + saved_position_ = position_; + if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4)) + { + if (*(input_ptr_+1) == 'u' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 'l') + { + more_ = visitor.null_value(semantic_tag::none, *this, ec); + input_ptr_ += 4; + position_ += 4; + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + } + else + { + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + } + else + { + ++input_ptr_; + ++position_; + state_ = json_parse_state::n; + } + } + + void parse_false(basic_json_visitor& visitor, std::error_code& ec) + { + saved_position_ = position_; + if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 5)) + { + if (*(input_ptr_+1) == 'a' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 's' && *(input_ptr_+4) == 'e') + { + more_ = visitor.bool_value(false, semantic_tag::none, *this, ec); + input_ptr_ += 5; + position_ += 5; + if (parent() == json_parse_state::root) + { + state_ = json_parse_state::accept; + } + else + { + state_ = json_parse_state::expect_comma_or_end; + } + } + else + { + err_handler_(json_errc::invalid_value, *this); + ec = json_errc::invalid_value; + more_ = false; + return; + } + } + else + { + ++input_ptr_; + ++position_; + state_ = json_parse_state::f; + } + } + + void parse_number(basic_json_visitor& visitor, std::error_code& ec) + { + saved_position_ = position_ - 1; + const char_type* local_input_end = end_input_; + + switch (state_) + { + case json_parse_state::minus: + goto minus_sign; + case json_parse_state::zero: + goto zero; + case json_parse_state::integer: + goto integer; + case json_parse_state::fraction1: + goto fraction1; + case json_parse_state::fraction2: + goto fraction2; + case json_parse_state::exp1: + goto exp1; + case json_parse_state::exp2: + goto exp2; + case json_parse_state::exp3: + goto exp3; + default: + JSONCONS_UNREACHABLE(); + } +minus_sign: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::minus; + return; + } + switch (*input_ptr_) + { + case '0': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto zero; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto integer; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::expected_value; + more_ = false; + return; + } +zero: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::zero; + return; + } + switch (*input_ptr_) + { + case '\r': + end_integer_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + return; + case '\n': + end_integer_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + return; + case ' ':case '\t': + end_integer_value(visitor, ec); + if (ec) return; + skip_space(); + return; + case '/': + end_integer_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::slash; + return; + case '}': + end_integer_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ']': + end_integer_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case '.': + string_buffer_.push_back(to_double_.get_decimal_point()); + ++input_ptr_; + ++position_; + goto fraction1; + case 'e':case 'E': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp1; + case ',': + end_integer_value(visitor, ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++position_; + return; + case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + err_handler_(json_errc::leading_zero, *this); + ec = json_errc::leading_zero; + more_ = false; + state_ = json_parse_state::zero; + return; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + state_ = json_parse_state::zero; + return; + } +integer: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::integer; + return; + } + switch (*input_ptr_) + { + case '\r': + end_integer_value(visitor, ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + return; + case '\n': + end_integer_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + return; + case ' ':case '\t': + end_integer_value(visitor, ec); + if (ec) return; + skip_space(); + return; + case '/': + end_integer_value(visitor, ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::slash; + return; + case '}': + end_integer_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ']': + end_integer_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto integer; + case '.': + string_buffer_.push_back(to_double_.get_decimal_point()); + ++input_ptr_; + ++position_; + goto fraction1; + case 'e':case 'E': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp1; + case ',': + end_integer_value(visitor, ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++position_; + return; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + state_ = json_parse_state::integer; + return; + } +fraction1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::fraction1; + return; + } + switch (*input_ptr_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto fraction2; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + state_ = json_parse_state::fraction1; + return; + } +fraction2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::fraction2; + return; + } + switch (*input_ptr_) + { + case '\r': + end_fraction_value(visitor, ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::cr; + return; + case '\n': + end_fraction_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + return; + case ' ':case '\t': + end_fraction_value(visitor, ec); + if (ec) return; + skip_space(); + return; + case '/': + end_fraction_value(visitor, ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::slash; + return; + case '}': + end_fraction_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ']': + end_fraction_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ',': + end_fraction_value(visitor, ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++position_; + return; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto fraction2; + case 'e':case 'E': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp1; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + state_ = json_parse_state::fraction2; + return; + } +exp1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::exp1; + return; + } + switch (*input_ptr_) + { + case '+': + ++input_ptr_; + ++position_; + goto exp2; + case '-': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp2; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp3; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::expected_value; + more_ = false; + state_ = json_parse_state::exp1; + return; + } +exp2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::exp2; + return; + } + switch (*input_ptr_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp3; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::expected_value; + more_ = false; + state_ = json_parse_state::exp2; + return; + } + +exp3: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::exp3; + return; + } + switch (*input_ptr_) + { + case '\r': + end_fraction_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++position_; + push_state(state_); + state_ = json_parse_state::cr; + return; + case '\n': + end_fraction_value(visitor, ec); + if (ec) return; + ++input_ptr_; + ++line_; + ++position_; + mark_position_ = position_; + return; + case ' ':case '\t': + end_fraction_value(visitor, ec); + if (ec) return; + skip_space(); + return; + case '/': + end_fraction_value(visitor, ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++position_; + state_ = json_parse_state::slash; + return; + case '}': + end_fraction_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ']': + end_fraction_value(visitor, ec); + if (ec) return; + state_ = json_parse_state::expect_comma_or_end; + return; + case ',': + end_fraction_value(visitor, ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++position_; + return; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + string_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++position_; + goto exp3; + default: + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + state_ = json_parse_state::exp3; + return; + } + + JSONCONS_UNREACHABLE(); + } + + void parse_string(basic_json_visitor& visitor, std::error_code& ec) + { + saved_position_ = position_ - 1; + const char_type* local_input_end = end_input_; + const char_type* sb = input_ptr_; + + switch (state_) + { + case json_parse_state::string: + goto string_u1; + case json_parse_state::escape: + goto escape; + case json_parse_state::escape_u1: + goto escape_u1; + case json_parse_state::escape_u2: + goto escape_u2; + case json_parse_state::escape_u3: + goto escape_u3; + case json_parse_state::escape_u4: + goto escape_u4; + case json_parse_state::escape_expect_surrogate_pair1: + goto escape_expect_surrogate_pair1; + case json_parse_state::escape_expect_surrogate_pair2: + goto escape_expect_surrogate_pair2; + case json_parse_state::escape_u5: + goto escape_u5; + case json_parse_state::escape_u6: + goto escape_u6; + case json_parse_state::escape_u7: + goto escape_u7; + case json_parse_state::escape_u8: + goto escape_u8; + default: + JSONCONS_UNREACHABLE(); + } + +string_u1: + while (input_ptr_ < local_input_end) + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + { + position_ += (input_ptr_ - sb + 1); + more_ = err_handler_(json_errc::illegal_control_character, *this); + if (!more_) + { + ec = json_errc::illegal_control_character; + state_ = json_parse_state::string; + return; + } + // recovery - skip + string_buffer_.append(sb,input_ptr_-sb); + ++input_ptr_; + state_ = json_parse_state::string; + return; + } + case '\r': + { + position_ += (input_ptr_ - sb + 1); + more_ = err_handler_(json_errc::illegal_character_in_string, *this); + if (!more_) + { + ec = json_errc::illegal_character_in_string; + state_ = json_parse_state::string; + return; + } + // recovery - keep + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + push_state(state_); + state_ = json_parse_state::cr; + return; + } + case '\n': + { + ++line_; + ++position_; + mark_position_ = position_; + more_ = err_handler_(json_errc::illegal_character_in_string, *this); + if (!more_) + { + ec = json_errc::illegal_character_in_string; + state_ = json_parse_state::string; + return; + } + // recovery - keep + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + return; + } + case '\t': + { + position_ += (input_ptr_ - sb + 1); + more_ = err_handler_(json_errc::illegal_character_in_string, *this); + if (!more_) + { + ec = json_errc::illegal_character_in_string; + state_ = json_parse_state::string; + return; + } + // recovery - keep + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + state_ = json_parse_state::string; + return; + } + case '\\': + { + string_buffer_.append(sb,input_ptr_-sb); + position_ += (input_ptr_ - sb + 1); + ++input_ptr_; + goto escape; + } + case '\"': + { + if (string_buffer_.length() == 0) + { + end_string_value(sb,input_ptr_-sb, visitor, ec); + if (ec) {return;} + } + else + { + string_buffer_.append(sb,input_ptr_-sb); + end_string_value(string_buffer_.data(),string_buffer_.length(), visitor, ec); + if (ec) {return;} + } + position_ += (input_ptr_ - sb + 1); + ++input_ptr_; + return; + } + default: + break; + } + ++input_ptr_; + } + + // Buffer exhausted + { + string_buffer_.append(sb,input_ptr_-sb); + position_ += (input_ptr_ - sb + 1); + state_ = json_parse_state::string; + return; + } + +escape: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape; + return; + } + switch (*input_ptr_) + { + case '\"': + string_buffer_.push_back('\"'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case '\\': + string_buffer_.push_back('\\'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case '/': + string_buffer_.push_back('/'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 'b': + string_buffer_.push_back('\b'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 'f': + string_buffer_.push_back('\f'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 'n': + string_buffer_.push_back('\n'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 'r': + string_buffer_.push_back('\r'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 't': + string_buffer_.push_back('\t'); + sb = ++input_ptr_; + ++position_; + goto string_u1; + case 'u': + cp_ = 0; + ++input_ptr_; + ++position_; + goto escape_u1; + default: + err_handler_(json_errc::illegal_escaped_character, *this); + ec = json_errc::illegal_escaped_character; + more_ = false; + state_ = json_parse_state::escape; + return; + } + +escape_u1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u1; + return; + } + { + cp_ = append_to_codepoint(0, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u1; + return; + } + ++input_ptr_; + ++position_; + goto escape_u2; + } + +escape_u2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u2; + return; + } + { + cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u2; + return; + } + ++input_ptr_; + ++position_; + goto escape_u3; + } + +escape_u3: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u3; + return; + } + { + cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u3; + return; + } + ++input_ptr_; + ++position_; + goto escape_u4; + } + +escape_u4: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u4; + return; + } + { + cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u4; + return; + } + if (unicode_traits::is_high_surrogate(cp_)) + { + ++input_ptr_; + ++position_; + goto escape_expect_surrogate_pair1; + } + else + { + unicode_traits::convert(&cp_, 1, string_buffer_); + sb = ++input_ptr_; + ++position_; + state_ = json_parse_state::string; + return; + } + } + +escape_expect_surrogate_pair1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_expect_surrogate_pair1; + return; + } + { + switch (*input_ptr_) + { + case '\\': + cp2_ = 0; + ++input_ptr_; + ++position_; + goto escape_expect_surrogate_pair2; + default: + err_handler_(json_errc::expected_codepoint_surrogate_pair, *this); + ec = json_errc::expected_codepoint_surrogate_pair; + more_ = false; + state_ = json_parse_state::escape_expect_surrogate_pair1; + return; + } + } + +escape_expect_surrogate_pair2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_expect_surrogate_pair2; + return; + } + { + switch (*input_ptr_) + { + case 'u': + ++input_ptr_; + ++position_; + goto escape_u5; + default: + err_handler_(json_errc::expected_codepoint_surrogate_pair, *this); + ec = json_errc::expected_codepoint_surrogate_pair; + more_ = false; + state_ = json_parse_state::escape_expect_surrogate_pair2; + return; + } + } + +escape_u5: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u5; + return; + } + { + cp2_ = append_to_codepoint(0, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u5; + return; + } + } + ++input_ptr_; + ++position_; + goto escape_u6; + +escape_u6: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u6; + return; + } + { + cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u6; + return; + } + ++input_ptr_; + ++position_; + goto escape_u7; + } + +escape_u7: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u7; + return; + } + { + cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u7; + return; + } + ++input_ptr_; + ++position_; + goto escape_u8; + } + +escape_u8: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = json_parse_state::escape_u8; + return; + } + { + cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + if (ec) + { + state_ = json_parse_state::escape_u8; + return; + } + uint32_t cp = 0x10000 + ((cp_ & 0x3FF) << 10) + (cp2_ & 0x3FF); + unicode_traits::convert(&cp, 1, string_buffer_); + sb = ++input_ptr_; + ++position_; + goto string_u1; + } + + JSONCONS_UNREACHABLE(); + } + + void translate_conv_errc(unicode_traits::conv_errc result, std::error_code& ec) + { + switch (result) + { + case unicode_traits::conv_errc(): + break; + case unicode_traits::conv_errc::over_long_utf8_sequence: + more_ = err_handler_(json_errc::over_long_utf8_sequence, *this); + if (!more_) + { + ec = json_errc::over_long_utf8_sequence; + return; + } + break; + case unicode_traits::conv_errc::unpaired_high_surrogate: + more_ = err_handler_(json_errc::unpaired_high_surrogate, *this); + if (!more_) + { + ec = json_errc::unpaired_high_surrogate; + return; + } + break; + case unicode_traits::conv_errc::expected_continuation_byte: + more_ = err_handler_(json_errc::expected_continuation_byte, *this); + if (!more_) + { + ec = json_errc::expected_continuation_byte; + return; + } + break; + case unicode_traits::conv_errc::illegal_surrogate_value: + more_ = err_handler_(json_errc::illegal_surrogate_value, *this); + if (!more_) + { + ec = json_errc::illegal_surrogate_value; + return; + } + break; + default: + more_ = err_handler_(json_errc::illegal_codepoint, *this); + if (!more_) + { + ec = json_errc::illegal_codepoint; + return; + } + break; + } + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use finish_parse(basic_json_visitor&)") + void end_parse(basic_json_visitor& visitor) + { + std::error_code ec; + finish_parse(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column())); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use finish_parse(basic_json_visitor&, std::error_code&)") + void end_parse(basic_json_visitor& visitor, std::error_code& ec) + { + while (!finished()) + { + parse_some(visitor, ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use update(const char_type*, std::size_t)") + void set_source(const char_type* data, std::size_t length) + { + begin_input_ = data; + end_input_ = data + length; + input_ptr_ = begin_input_; + } +#endif + + std::size_t line() const override + { + return line_; + } + + std::size_t column() const override + { + return (position_ - mark_position_) + 1; + } + + std::size_t position() const override + { + return saved_position_; + } + + std::size_t offset() const + { + return input_ptr_ - begin_input_; + } +private: + + void end_integer_value(basic_json_visitor& visitor, std::error_code& ec) + { + if (string_buffer_[0] == '-') + { + end_negative_value(visitor, ec); + } + else + { + end_positive_value(visitor, ec); + } + } + + void end_negative_value(basic_json_visitor& visitor, std::error_code& ec) + { + int64_t val; + auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val); + if (result) + { + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + } + else // Must be overflow + { + more_ = visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec); + } + after_value(ec); + } + + void end_positive_value(basic_json_visitor& visitor, std::error_code& ec) + { + uint64_t val; + auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val); + if (result) + { + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + } + else // Must be overflow + { + more_ = visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec); + } + after_value(ec); + } + + void end_fraction_value(basic_json_visitor& visitor, std::error_code& ec) + { + JSONCONS_TRY + { + if (options_.lossless_number()) + { + more_ = visitor.string_value(string_buffer_, semantic_tag::bigdec, *this, ec); + } + else + { + double d = to_double_(string_buffer_.c_str(), string_buffer_.length()); + more_ = visitor.double_value(d, semantic_tag::none, *this, ec); + } + } + JSONCONS_CATCH(...) + { + more_ = err_handler_(json_errc::invalid_number, *this); + if (!more_) + { + ec = json_errc::invalid_number; + return; + } + more_ = visitor.null_value(semantic_tag::none, *this, ec); // recovery + } + + after_value(ec); + } + + void end_string_value(const char_type* s, std::size_t length, basic_json_visitor& visitor, std::error_code& ec) + { + string_view_type sv(s, length); + auto result = unicode_traits::validate(s, length); + if (result.ec != unicode_traits::conv_errc()) + { + translate_conv_errc(result.ec,ec); + position_ += (result.ptr - s); + return; + } + switch (parent()) + { + case json_parse_state::member_name: + more_ = visitor.key(sv, *this, ec); + pop_state(); + state_ = json_parse_state::expect_colon; + break; + case json_parse_state::object: + case json_parse_state::array: + { + auto it = std::find_if(string_double_map_.begin(), string_double_map_.end(), string_maps_to_double{ sv }); + if (it != string_double_map_.end()) + { + more_ = visitor.double_value(it->second, semantic_tag::none, *this, ec); + } + else + { + more_ = visitor.string_value(sv, semantic_tag::none, *this, ec); + } + state_ = json_parse_state::expect_comma_or_end; + break; + } + case json_parse_state::root: + { + auto it = std::find_if(string_double_map_.begin(),string_double_map_.end(),string_maps_to_double{sv}); + if (it != string_double_map_.end()) + { + more_ = visitor.double_value(it->second, semantic_tag::none, *this, ec); + } + else + { + more_ = visitor.string_value(sv, semantic_tag::none, *this, ec); + } + state_ = json_parse_state::accept; + break; + } + default: + more_ = err_handler_(json_errc::syntax_error, *this); + if (!more_) + { + ec = json_errc::syntax_error; + return; + } + break; + } + } + + void begin_member_or_element(std::error_code& ec) + { + switch (parent()) + { + case json_parse_state::object: + state_ = json_parse_state::expect_member_name; + break; + case json_parse_state::array: + state_ = json_parse_state::expect_value; + break; + case json_parse_state::root: + break; + default: + more_ = err_handler_(json_errc::syntax_error, *this); + if (!more_) + { + ec = json_errc::syntax_error; + return; + } + break; + } + } + + void after_value(std::error_code& ec) + { + switch (parent()) + { + case json_parse_state::array: + case json_parse_state::object: + state_ = json_parse_state::expect_comma_or_end; + break; + case json_parse_state::root: + state_ = json_parse_state::accept; + break; + default: + more_ = err_handler_(json_errc::syntax_error, *this); + if (!more_) + { + ec = json_errc::syntax_error; + return; + } + break; + } + } + + void push_state(json_parse_state state) + { + state_stack_.push_back(state); + } + + json_parse_state pop_state() + { + JSONCONS_ASSERT(!state_stack_.empty()) + json_parse_state state = state_stack_.back(); + state_stack_.pop_back(); + return state; + } + + uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec) + { + cp *= 16; + if (c >= '0' && c <= '9') + { + cp += c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + cp += c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + cp += c - 'A' + 10; + } + else + { + more_ = err_handler_(json_errc::invalid_unicode_escape_sequence, *this); + if (!more_) + { + ec = json_errc::invalid_unicode_escape_sequence; + return cp; + } + } + return cp; + } +}; + +using json_parser = basic_json_parser; +using wjson_parser = basic_json_parser; + +} + +#endif + diff --git a/include/jsoncons/json_reader.hpp b/include/jsoncons/json_reader.hpp new file mode 100644 index 0000000..eeb351f --- /dev/null +++ b/include/jsoncons/json_reader.hpp @@ -0,0 +1,731 @@ +// Copyright 2015 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_READER_HPP +#define JSONCONS_JSON_READER_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include + +namespace jsoncons { + + // utf8_other_json_input_adapter + + template + class json_utf8_to_other_visitor_adaptor : public json_visitor + { + public: + using json_visitor::string_view_type; + private: + basic_default_json_visitor default_visitor_; + basic_json_visitor& other_visitor_; + //std::function err_handler_; + + // noncopyable and nonmoveable + json_utf8_to_other_visitor_adaptor(const json_utf8_to_other_visitor_adaptor&) = delete; + json_utf8_to_other_visitor_adaptor& operator=(const json_utf8_to_other_visitor_adaptor&) = delete; + + public: + json_utf8_to_other_visitor_adaptor() + : other_visitor_(default_visitor_) + { + } + + json_utf8_to_other_visitor_adaptor(basic_json_visitor& other_visitor/*, + std::function err_handler*/) + : other_visitor_(other_visitor)/*, + err_handler_(err_handler)*/ + { + } + + private: + + void visit_flush() override + { + other_visitor_.flush(); + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return other_visitor_.begin_object(tag, context, ec); + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + return other_visitor_.end_object(context, ec); + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return other_visitor_.begin_array(tag, context, ec); + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + return other_visitor_.end_array(context, ec); + } + + bool visit_key(const string_view_type& name, const ser_context& context, std::error_code& ec) override + { + std::basic_string target; + auto result = unicode_traits::convert( + name.data(), name.size(), target, + unicode_traits::conv_flags::strict); + if (result.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(result.ec,context.line(),context.column())); + } + return other_visitor_.key(target, context, ec); + } + + bool visit_string(const string_view_type& value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + std::basic_string target; + auto result = unicode_traits::convert( + value.data(), value.size(), target, + unicode_traits::conv_flags::strict); + if (result.ec != unicode_traits::conv_errc()) + { + ec = result.ec; + return false; + } + return other_visitor_.string_value(target, tag, context, ec); + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return other_visitor_.int64_value(value, tag, context, ec); + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return other_visitor_.uint64_value(value, tag, context, ec); + } + + bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return other_visitor_.half_value(value, tag, context, ec); + } + + bool visit_double(double value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return other_visitor_.double_value(value, tag, context, ec); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return other_visitor_.bool_value(value, tag, context, ec); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return other_visitor_.null_value(tag, context, ec); + } + }; + + template,class Allocator=std::allocator> + class basic_json_reader + { + public: + using char_type = CharT; + using source_type = Source; + using string_view_type = jsoncons::basic_string_view; + private: + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + static constexpr size_t default_max_buffer_size = 16384; + + json_source_adaptor source_; + basic_default_json_visitor default_visitor_; + basic_json_visitor& visitor_; + basic_json_parser parser_; + + // Noncopyable and nonmoveable + basic_json_reader(const basic_json_reader&) = delete; + basic_json_reader& operator=(const basic_json_reader&) = delete; + + public: + template + explicit basic_json_reader(Sourceable&& source, const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + default_visitor_, + basic_json_decode_options(), + default_json_parsing(), + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + const basic_json_decode_options& options, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + default_visitor_, + options, + default_json_parsing(), + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + std::function err_handler, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + default_visitor_, + basic_json_decode_options(), + err_handler, + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + default_visitor_, + options, + err_handler, + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + visitor, + basic_json_decode_options(), + default_json_parsing(), + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_json_decode_options& options, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + visitor, + options, + default_json_parsing(), + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + std::function err_handler, + const Allocator& alloc = Allocator()) + : basic_json_reader(std::forward(source), + visitor, + basic_json_decode_options(), + err_handler, + alloc) + { + } + + template + basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator()) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options,err_handler,alloc) + { + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth() on options") + int max_nesting_depth() const + { + return parser_.max_nesting_depth(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth(int) on options") + void max_nesting_depth(int depth) + { + parser_.max_nesting_depth(depth); + } +#endif + void read_next() + { + std::error_code ec; + read_next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_next(std::error_code& ec) + { + if (source_.is_error()) + { + ec = json_errc::source_error; + return; + } + parser_.reset(); + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + bool eof = parser_.source_exhausted(); + parser_.parse_some(visitor_, ec); + if (ec) return; + if (eof) + { + if (parser_.enter()) + { + break; + } + else if (!parser_.accept()) + { + ec = json_errc::unexpected_eof; + return; + } + } + } + + while (!source_.eof()) + { + parser_.skip_whitespace(); + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + else + { + break; + } + } + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } + + void check_done(std::error_code& ec) + { + if (source_.is_error()) + { + ec = json_errc::source_error; + return; + } + if (source_.eof()) + { + parser_.check_done(ec); + if (ec) return; + } + else + { + do + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + if (!parser_.source_exhausted()) + { + parser_.check_done(ec); + if (ec) return; + } + } + while (!eof()); + } + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + void read() + { + read_next(); + check_done(); + } + + void read(std::error_code& ec) + { + read_next(ec); + if (!ec) + { + check_done(ec); + } + } + }; + + template,class Allocator=std::allocator> + class legacy_basic_json_reader + { + public: + using char_type = CharT; + using source_type = Source; + using string_view_type = jsoncons::basic_string_view; + private: + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + static constexpr size_t default_max_buffer_size = 16384; + + json_source_adaptor source_; + basic_default_json_visitor default_visitor_; + basic_json_visitor& visitor_; + basic_json_parser parser_; + + // Noncopyable and nonmoveable + legacy_basic_json_reader(const legacy_basic_json_reader&) = delete; + legacy_basic_json_reader& operator=(const legacy_basic_json_reader&) = delete; + + public: + template + explicit legacy_basic_json_reader(Sourceable&& source, const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + default_visitor_, + basic_json_decode_options(), + default_json_parsing(), + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + const basic_json_decode_options& options, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + default_visitor_, + options, + default_json_parsing(), + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + std::function err_handler, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + default_visitor_, + basic_json_decode_options(), + err_handler, + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + default_visitor_, + options, + err_handler, + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + visitor, + basic_json_decode_options(), + default_json_parsing(), + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_json_decode_options& options, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + visitor, + options, + default_json_parsing(), + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + std::function err_handler, + const Allocator& alloc = Allocator()) + : legacy_basic_json_reader(std::forward(source), + visitor, + basic_json_decode_options(), + err_handler, + alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options,err_handler,alloc) + { + } + + template + legacy_basic_json_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + visitor_(visitor), + parser_(options,err_handler,alloc) + { + jsoncons::basic_string_view sv(std::forward(source)); + + auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column())); + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth() on options") + int max_nesting_depth() const + { + return parser_.max_nesting_depth(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth(int) on options") + void max_nesting_depth(int depth) + { + parser_.max_nesting_depth(depth); + } +#endif + void read_next() + { + std::error_code ec; + read_next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_next(std::error_code& ec) + { + if (source_.is_error()) + { + ec = json_errc::source_error; + return; + } + parser_.reset(); + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + bool eof = parser_.source_exhausted(); + parser_.parse_some(visitor_, ec); + if (ec) return; + if (eof) + { + if (parser_.enter()) + { + break; + } + else if (!parser_.accept()) + { + ec = json_errc::unexpected_eof; + return; + } + } + } + + while (!source_.eof()) + { + parser_.skip_whitespace(); + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + else + { + break; + } + } + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } + + void check_done(std::error_code& ec) + { + if (source_.is_error()) + { + ec = json_errc::source_error; + return; + } + if (source_.eof()) + { + parser_.check_done(ec); + if (ec) return; + } + else + { + do + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + if (!parser_.source_exhausted()) + { + parser_.check_done(ec); + if (ec) return; + } + } + while (!eof()); + } + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + void read() + { + read_next(); + check_done(); + } + + void read(std::error_code& ec) + { + read_next(ec); + if (!ec) + { + check_done(ec); + } + } + }; + +#if !defined(JSONCONS_NO_DEPRECATED) + using json_reader = legacy_basic_json_reader; + using wjson_reader = legacy_basic_json_reader; +#endif + using json_string_reader = basic_json_reader>; + using wjson_string_reader = basic_json_reader>; + using json_stream_reader = basic_json_reader>; + using wjson_stream_reader = basic_json_reader>; +} + +#endif + diff --git a/include/jsoncons/json_traits_macros.hpp b/include/jsoncons/json_traits_macros.hpp new file mode 100644 index 0000000..321d889 --- /dev/null +++ b/include/jsoncons/json_traits_macros.hpp @@ -0,0 +1,1072 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_TRAITS_MACROS_HPP +#define JSONCONS_JSON_TRAITS_MACROS_HPP + +#include // std::swap +#include // std::iterator_traits, std::input_iterator_tag +#include // JSONCONS_EXPAND, JSONCONS_QUOTE +#include +#include +#include // std::numeric_limits +#include +#include // std::enable_if +#include +#include + +namespace jsoncons +{ + #define JSONCONS_RDONLY(X) + + #define JSONCONS_RDWR(X) X + + struct always_true + { + template< class T> + constexpr bool operator()(const T&) const noexcept + { + return true; + } + }; + + struct identity + { + template< class T> + constexpr T&& operator()(T&& val) const noexcept + { + return std::forward(val); + } + }; + + template + struct json_traits_macro_names + {}; + + template + struct json_traits_helper + { + using string_view_type = typename Json::string_view_type; + + template + static void set_udt_member(const Json&, const string_view_type&, const OutputType&) + { + } + template + static void set_udt_member(const Json& j, const string_view_type& key, OutputType& val) + { + val = j.at(key).template as(); + } + + template + static void set_udt_member(const Json&, const string_view_type&, From, const OutputType&) + { + } + template + static void set_udt_member(const Json& j, const string_view_type& key, From from, OutputType& val) + { + val = from(j.at(key).template as()); + } + template + static void set_optional_json_member(const string_view_type& key, const std::shared_ptr& val, Json& j) + { + if (val) j.try_emplace(key, val); + } + template + static void set_optional_json_member(const string_view_type& key, const std::unique_ptr& val, Json& j) + { + if (val) j.try_emplace(key, val); + } + template + static void set_optional_json_member(const string_view_type& key, const jsoncons::optional& val, Json& j) + { + if (val) j.try_emplace(key, val); + } + template + static void set_optional_json_member(const string_view_type& key, const U& val, Json& j) + { + j.try_emplace(key, val); + } + }; +} + +#if defined(_MSC_VER) +#pragma warning( disable : 4127) +#endif + +#define JSONCONS_CONCAT_RAW(a, b) a ## b +#define JSONCONS_CONCAT(a, b) JSONCONS_CONCAT_RAW(a, b) + +// Inspired by https://github.com/Loki-Astari/ThorsSerializer/blob/master/src/Serialize/Traits.h + +#define JSONCONS_NARGS(...) JSONCONS_NARG_(__VA_ARGS__, 70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) +#define JSONCONS_NARG_(...) JSONCONS_EXPAND( JSONCONS_ARG_N(__VA_ARGS__) ) +#define JSONCONS_ARG_N(e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16,e17,e18,e19,e20,e21,e22,e23,e24,e25,e26,e27,e28,e29,e30,e31,e32,e33,e34,e35,e36,e37,e38,e39,e40,e41,e42,e43,e44,e45,e46,e47,e48,e49,e50,e51,e52,e53,e54,e55,e56,e57,e58,e59,e60,e61,e62,e63,e64,e65,e66,e67,e68,e69,e70,N,...)N + +#define JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, Count) Call(P1, P2, P3, P4, Count) + +#define JSONCONS_VARIADIC_REP_N(Call, P1, P2, P3, ...) JSONCONS_VARIADIC_REP_OF_N(Call, P1,P2, P3, JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) +#define JSONCONS_VARIADIC_REP_OF_N(Call, P1, P2, P3, Count, ...) JSONCONS_VARIADIC_REP_OF_N_(Call, P1, P2, P3, Count, __VA_ARGS__) +#define JSONCONS_VARIADIC_REP_OF_N_(Call, P1, P2, P3, Count, ...) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_ ## Count(Call, P1, P2, P3, __VA_ARGS__)) + +#define JSONCONS_VARIADIC_REP_OF_70(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 70) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_69(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_69(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 69) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_68(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_68(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 68) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_67(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_67(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 67) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_66(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_66(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 66) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_65(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_65(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 65) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_64(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_64(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 64) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_63(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_63(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 63) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_62(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_62(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 62) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_61(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_61(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 61) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_60(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_60(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 60) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_59(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_59(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 59) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_58(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_58(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 58) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_57(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_57(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 57) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_56(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_56(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 56) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_55(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_55(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 55) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_54(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_54(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 54) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_53(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_53(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 53) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_52(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_52(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 52) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_51(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_51(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 51) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_50(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_50(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 50) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_49(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_49(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 49) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_48(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_48(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 48) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_47(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_47(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 47) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_46(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_46(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 46) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_45(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_45(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 45) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_44(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_44(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 44) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_43(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_43(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 43) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_42(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_42(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 42) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_41(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_41(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 41) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_40(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_40(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 40) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_39(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_39(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 39) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_38(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_38(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 38) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_37(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_37(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 37) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_36(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_36(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 36) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_35(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_35(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 35) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_34(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_34(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 34) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_33(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_33(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 33) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_32(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_32(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 32) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_31(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_31(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 31) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_30(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_30(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 30) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_29(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_29(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 29) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_28(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_28(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 28) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_27(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_27(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 27) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_26(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_26(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 26) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_25(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_25(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 25) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_24(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_24(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 24) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_23(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_23(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 23) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_22(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_22(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 22) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_21(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_21(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 21) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_20(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_20(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 20) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_19(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_19(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 19) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_18(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_18(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 18) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_17(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_17(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 17) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_16(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_16(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 16) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_15(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_15(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 15) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_14(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_14(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 14) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_13(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_13(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 13) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_12(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_12(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 12) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_11(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_11(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 11) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_10(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_10(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 10) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_9(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_9(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 9) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_8(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_8(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 8) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_7(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_7(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 7) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_6(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_6(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 6) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_5(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_5(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 5) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_4(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_4(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 4) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_3(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_3(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 3) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_2(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_2(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 2) JSONCONS_EXPAND(JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, P4) JSONCONS_EXPAND(Call ## _LAST(P1, P2, P3, P4, 1)) + +#define JSONCONS_TYPE_TRAITS_FRIEND \ + template \ + friend struct jsoncons::json_type_traits; + +#define JSONCONS_EXPAND_CALL2(Call, Expr, Id) JSONCONS_EXPAND(Call(Expr, Id)) + +#define JSONCONS_REP_OF_N(Call, Expr, Pre, App, Count) JSONCONS_REP_OF_ ## Count(Call, Expr, Pre, App) + +#define JSONCONS_REP_OF_50(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 50) JSONCONS_REP_OF_49(Call, Expr, , App) +#define JSONCONS_REP_OF_49(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 49) JSONCONS_REP_OF_48(Call, Expr, , App) +#define JSONCONS_REP_OF_48(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 48) JSONCONS_REP_OF_47(Call, Expr, , App) +#define JSONCONS_REP_OF_47(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 47) JSONCONS_REP_OF_46(Call, Expr, , App) +#define JSONCONS_REP_OF_46(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 46) JSONCONS_REP_OF_45(Call, Expr, , App) +#define JSONCONS_REP_OF_45(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 45) JSONCONS_REP_OF_44(Call, Expr, , App) +#define JSONCONS_REP_OF_44(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 44) JSONCONS_REP_OF_43(Call, Expr, , App) +#define JSONCONS_REP_OF_43(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 43) JSONCONS_REP_OF_42(Call, Expr, , App) +#define JSONCONS_REP_OF_42(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 42) JSONCONS_REP_OF_41(Call, Expr, , App) +#define JSONCONS_REP_OF_41(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 41) JSONCONS_REP_OF_40(Call, Expr, , App) +#define JSONCONS_REP_OF_40(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 40) JSONCONS_REP_OF_39(Call, Expr, , App) +#define JSONCONS_REP_OF_39(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 39) JSONCONS_REP_OF_38(Call, Expr, , App) +#define JSONCONS_REP_OF_38(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 38) JSONCONS_REP_OF_37(Call, Expr, , App) +#define JSONCONS_REP_OF_37(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 37) JSONCONS_REP_OF_36(Call, Expr, , App) +#define JSONCONS_REP_OF_36(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 36) JSONCONS_REP_OF_35(Call, Expr, , App) +#define JSONCONS_REP_OF_35(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 35) JSONCONS_REP_OF_34(Call, Expr, , App) +#define JSONCONS_REP_OF_34(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 34) JSONCONS_REP_OF_33(Call, Expr, , App) +#define JSONCONS_REP_OF_33(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 33) JSONCONS_REP_OF_32(Call, Expr, , App) +#define JSONCONS_REP_OF_32(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 32) JSONCONS_REP_OF_31(Call, Expr, , App) +#define JSONCONS_REP_OF_31(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 31) JSONCONS_REP_OF_30(Call, Expr, , App) +#define JSONCONS_REP_OF_30(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 30) JSONCONS_REP_OF_29(Call, Expr, , App) +#define JSONCONS_REP_OF_29(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 29) JSONCONS_REP_OF_28(Call, Expr, , App) +#define JSONCONS_REP_OF_28(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 28) JSONCONS_REP_OF_27(Call, Expr, , App) +#define JSONCONS_REP_OF_27(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 27) JSONCONS_REP_OF_26(Call, Expr, , App) +#define JSONCONS_REP_OF_26(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 26) JSONCONS_REP_OF_25(Call, Expr, , App) +#define JSONCONS_REP_OF_25(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 25) JSONCONS_REP_OF_24(Call, Expr, , App) +#define JSONCONS_REP_OF_24(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 24) JSONCONS_REP_OF_23(Call, Expr, , App) +#define JSONCONS_REP_OF_23(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 23) JSONCONS_REP_OF_22(Call, Expr, , App) +#define JSONCONS_REP_OF_22(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 22) JSONCONS_REP_OF_21(Call, Expr, , App) +#define JSONCONS_REP_OF_21(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 21) JSONCONS_REP_OF_20(Call, Expr, , App) +#define JSONCONS_REP_OF_20(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 20) JSONCONS_REP_OF_19(Call, Expr, , App) +#define JSONCONS_REP_OF_19(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 19) JSONCONS_REP_OF_18(Call, Expr, , App) +#define JSONCONS_REP_OF_18(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 18) JSONCONS_REP_OF_17(Call, Expr, , App) +#define JSONCONS_REP_OF_17(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 17) JSONCONS_REP_OF_16(Call, Expr, , App) +#define JSONCONS_REP_OF_16(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 16) JSONCONS_REP_OF_15(Call, Expr, , App) +#define JSONCONS_REP_OF_15(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 15) JSONCONS_REP_OF_14(Call, Expr, , App) +#define JSONCONS_REP_OF_14(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 14) JSONCONS_REP_OF_13(Call, Expr, , App) +#define JSONCONS_REP_OF_13(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 13) JSONCONS_REP_OF_12(Call, Expr, , App) +#define JSONCONS_REP_OF_12(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 12) JSONCONS_REP_OF_11(Call, Expr, , App) +#define JSONCONS_REP_OF_11(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 11) JSONCONS_REP_OF_10(Call, Expr, , App) +#define JSONCONS_REP_OF_10(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 10) JSONCONS_REP_OF_9(Call, Expr, , App) +#define JSONCONS_REP_OF_9(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 9) JSONCONS_REP_OF_8(Call, Expr, , App) +#define JSONCONS_REP_OF_8(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 8) JSONCONS_REP_OF_7(Call, Expr, , App) +#define JSONCONS_REP_OF_7(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 7) JSONCONS_REP_OF_6(Call, Expr, , App) +#define JSONCONS_REP_OF_6(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 6) JSONCONS_REP_OF_5(Call, Expr, , App) +#define JSONCONS_REP_OF_5(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 5) JSONCONS_REP_OF_4(Call, Expr, , App) +#define JSONCONS_REP_OF_4(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 4) JSONCONS_REP_OF_3(Call, Expr, , App) +#define JSONCONS_REP_OF_3(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 3) JSONCONS_REP_OF_2(Call, Expr, , App) +#define JSONCONS_REP_OF_2(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 2) JSONCONS_REP_OF_1(Call, Expr, , App) +#define JSONCONS_REP_OF_1(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call ## _LAST, Expr, 1) App +#define JSONCONS_REP_OF_0(Call, Expr, Pre, App) + +#define JSONCONS_GENERATE_TPL_PARAMS(Call, Count) JSONCONS_REP_OF_N(Call, , , ,Count) +#define JSONCONS_GENERATE_TPL_ARGS(Call, Count) JSONCONS_REP_OF_N(Call, ,<,>,Count) +#define JSONCONS_GENERATE_TPL_PARAM(Expr, Id) typename T ## Id, +#define JSONCONS_GENERATE_TPL_PARAM_LAST(Expr, Id) typename T ## Id +#define JSONCONS_GENERATE_MORE_TPL_PARAM(Expr, Id) , typename T ## Id +#define JSONCONS_GENERATE_MORE_TPL_PARAM_LAST(Expr, Id) , typename T ## Id +#define JSONCONS_GENERATE_TPL_ARG(Expr, Id) T ## Id, +#define JSONCONS_GENERATE_TPL_ARG_LAST(Ex, Id) T ## Id + +#define JSONCONS_GENERATE_NAME_STR(Prefix, P2, P3, Member, Count) JSONCONS_GENERATE_NAME_STR_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_GENERATE_NAME_STR_LAST(Prefix, P2, P3, Member, Count) \ + static inline const char* Member ## _str(char) {return JSONCONS_QUOTE(,Member);} \ + static inline const wchar_t* Member ## _str(wchar_t) {return JSONCONS_QUOTE(L,Member);} \ + /**/ + +#define JSONCONS_N_MEMBER_IS(Prefix, P2, P3, Member, Count) JSONCONS_N_MEMBER_IS_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_N_MEMBER_IS_LAST(Prefix, P2, P3, Member, Count) if ((num_params-Count) < num_mandatory_params1 && !ajson.contains(json_traits_macro_names::Member##_str(char_type{}))) return false; + +#define JSONCONS_N_MEMBER_AS(Prefix,P2,P3, Member, Count) JSONCONS_N_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) +#define JSONCONS_N_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) \ + if ((num_params-Count) < num_mandatory_params2 || ajson.contains(json_traits_macro_names::Member##_str(char_type{}))) \ + {json_traits_helper::set_udt_member(ajson,json_traits_macro_names::Member##_str(char_type{}),aval.Member);} + +#define JSONCONS_ALL_MEMBER_AS(Prefix, P2,P3,Member, Count) JSONCONS_ALL_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) +#define JSONCONS_ALL_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) \ + json_traits_helper::set_udt_member(ajson,json_traits_macro_names::Member##_str(char_type{}),aval.Member); + +#define JSONCONS_TO_JSON(Prefix, P2, P3, Member, Count) JSONCONS_TO_JSON_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_TO_JSON_LAST(Prefix, P2, P3, Member, Count) if ((num_params-Count) < num_mandatory_params2) \ + {ajson.try_emplace(json_traits_macro_names::Member##_str(char_type{}), aval.Member);} \ + else {json_traits_helper::set_optional_json_member(json_traits_macro_names::Member##_str(char_type{}), aval.Member, ajson);} + +#define JSONCONS_ALL_TO_JSON(Prefix, P2, P3, Member, Count) JSONCONS_ALL_TO_JSON_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_ALL_TO_JSON_LAST(Prefix, P2, P3, Member, Count) \ + ajson.try_emplace(json_traits_macro_names::Member##_str(char_type{}), aval.Member); + +#define JSONCONS_MEMBER_TRAITS_BASE(AsT,ToJ,NumTemplateParams,ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_traits_macro_names \ + { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_N_MEMBER_IS, ,,, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + value_type aval{}; \ + JSONCONS_VARIADIC_REP_N(AsT, ,,, __VA_ARGS__) \ + return aval; \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(ToJ, ,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_N_MEMBER_TRAITS(ValueType,NumMandatoryParams,...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_N_MEMBER_AS, JSONCONS_TO_JSON,0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_MEMBER_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_N_MEMBER_AS, JSONCONS_TO_JSON,NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_MEMBER_TRAITS(ValueType, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_MEMBER_AS,JSONCONS_ALL_TO_JSON,0,ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_MEMBER_TRAITS(NumTemplateParams, ValueType, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_MEMBER_AS,JSONCONS_ALL_TO_JSON,NumTemplateParams,ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_MEMBER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_MEMBER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_MEMBER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_MEMBER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_MEMBER_NAME_IS_2(Member, Name) !ajson.contains(Name)) return false; +#define JSONCONS_MEMBER_NAME_IS_3(Member, Name, Mode) JSONCONS_MEMBER_NAME_IS_2(Member, Name) +#define JSONCONS_MEMBER_NAME_IS_4(Member, Name, Mode, Match) JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, , ) +#define JSONCONS_MEMBER_NAME_IS_5(Member, Name, Mode, Match, Into) JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Member))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_N_MEMBER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_N_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_MEMBER_NAME_AS_2(Member, Name) \ + if (ajson.contains(Name)) {json_traits_helper::set_udt_member(ajson,Name,aval.Member);} +#define JSONCONS_N_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_N_MEMBER_NAME_AS_2(Member, Name)) +#define JSONCONS_N_MEMBER_NAME_AS_4(Member, Name, Mode, Match) \ + Mode(if (ajson.contains(Name)) {json_traits_helper::set_udt_member(ajson,Name,aval.Member);}) +#define JSONCONS_N_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) \ + Mode(if (ajson.contains(Name)) {json_traits_helper::template set_udt_member())->Member))>::type>(ajson,Name,aval.Member);}) +#define JSONCONS_N_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) \ + Mode(if (ajson.contains(Name)) {json_traits_helper::template set_udt_member())->Member))>::type>(ajson,Name,From,aval.Member);}) + +#define JSONCONS_ALL_MEMBER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_ALL_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_MEMBER_NAME_AS_2(Member, Name) \ + json_traits_helper::set_udt_member(ajson,Name,aval.Member); +#define JSONCONS_ALL_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_ALL_MEMBER_NAME_AS_2(Member, Name)) +#define JSONCONS_ALL_MEMBER_NAME_AS_4(Member, Name, Mode, Match) \ + Mode(json_traits_helper::set_udt_member(ajson,Name,aval.Member);) +#define JSONCONS_ALL_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) \ + Mode(json_traits_helper::template set_udt_member())->Member))>::type>(ajson,Name,aval.Member);) +#define JSONCONS_ALL_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) \ + Mode(json_traits_helper::template set_udt_member())->Member))>::type>(ajson,Name,From,aval.Member);) + +#define JSONCONS_N_MEMBER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_N_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_MEMBER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_2(Member, Name) \ + {ajson.try_emplace(Name, aval.Member);} \ +else \ + {json_traits_helper::set_optional_json_member(Name, aval.Member, ajson);} +#define JSONCONS_N_MEMBER_NAME_TO_JSON_3(Member, Name, Mode) JSONCONS_N_MEMBER_NAME_TO_JSON_2(Member, Name) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_4(Member, Name, Mode, Match) JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match,,) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_5(Member, Name, Mode, Match, Into) JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, From) \ + {ajson.try_emplace(Name, Into(aval.Member));} \ +else \ + {json_traits_helper::set_optional_json_member(Name, Into(aval.Member), ajson);} + +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_ALL_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_MEMBER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_2(Member, Name) ajson.try_emplace(Name, aval.Member); +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_3(Member, Name, Mode) JSONCONS_ALL_MEMBER_NAME_TO_JSON_2(Member, Name) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_4(Member, Name, Mode, Match) JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match,,) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_5(Member, Name, Mode, Match, Into) JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, From) ajson.try_emplace(Name, Into(aval.Member)); + +#define JSONCONS_MEMBER_NAME_TRAITS_BASE(AsT,ToJ, NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_MEMBER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + value_type aval{}; \ + JSONCONS_VARIADIC_REP_N(AsT,,,, __VA_ARGS__) \ + return aval; \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(ToJ,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + + +#define JSONCONS_N_MEMBER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_AS, JSONCONS_N_MEMBER_NAME_TO_JSON, 0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_MEMBER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_AS, JSONCONS_N_MEMBER_NAME_TO_JSON, NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_MEMBER_NAME_TRAITS(ValueType, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_AS, JSONCONS_ALL_MEMBER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_AS, JSONCONS_ALL_MEMBER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_CTOR_GETTER_IS(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_IS_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_IS_LAST(Prefix, P2, P3, Getter, Count) if ((num_params-Count) < num_mandatory_params1 && !ajson.contains(json_traits_macro_names::Getter##_str(char_type{}))) return false; + +#define JSONCONS_CTOR_GETTER_AS(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_AS_LAST(Prefix, P2, P3, Getter, Count), +#define JSONCONS_CTOR_GETTER_AS_LAST(Prefix, P2, P3, Getter, Count) ((num_params-Count) < num_mandatory_params2) ? (ajson.at(json_traits_macro_names::Getter##_str(char_type{}))).template as())->Getter())>::type>() : (ajson.contains(json_traits_macro_names::Getter##_str(char_type{})) ? (ajson.at(json_traits_macro_names::Getter##_str(char_type{}))).template as())->Getter())>::type>() : typename std::decay())->Getter())>::type()) + +#define JSONCONS_CTOR_GETTER_TO_JSON(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_TO_JSON_LAST(Prefix, P2, P3, Getter, Count) + +#define JSONCONS_CTOR_GETTER_TO_JSON_LAST(Prefix, P2, P3, Getter, Count) \ +if ((num_params-Count) < num_mandatory_params2) { \ + ajson.try_emplace(json_traits_macro_names::Getter##_str(char_type{}), aval.Getter() ); \ + } \ +else { \ + json_traits_helper::set_optional_json_member(json_traits_macro_names::Getter##_str(char_type{}), aval.Getter(), ajson); \ +} + +#define JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_traits_macro_names \ + { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_IS, ,,, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + return value_type ( JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_AS, ,,, __VA_ARGS__) ); \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_TO_JSON, ,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_ALL_CTOR_GETTER_TRAITS(ValueType, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ValueType, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_CTOR_GETTER_TRAITS(ValueType,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_CTOR_GETTER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_CTOR_GETTER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_IS_2(Getter, Name) !ajson.contains(Name)) return false; +#define JSONCONS_CTOR_GETTER_NAME_IS_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_IS_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_IS_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, , ) +#define JSONCONS_CTOR_GETTER_NAME_IS_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Getter()))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_CTOR_GETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_CTOR_GETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_AS_2(Getter, Name) JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name) JSONCONS_COMMA +#define JSONCONS_CTOR_GETTER_NAME_AS_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name)) Mode(JSONCONS_COMMA) +#define JSONCONS_CTOR_GETTER_NAME_AS_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match,,) +#define JSONCONS_CTOR_GETTER_NAME_AS_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match, Into, From) JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter,Name,Mode,Match,Into,From) Mode(JSONCONS_COMMA) +#define JSONCONS_COMMA , + +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_CTOR_GETTER_NAME_AS_LAST_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name) (ajson.contains(Name)) ? (ajson.at(Name)).template as())->Getter())>::type>() : typename std::decay())->Getter())>::type() +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name)) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match,,) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match, Into, From) Mode(ajson.contains(Name) ? From(ajson.at(Name).template as())->Getter()))>::type>()) : From(typename std::decay())->Getter()))>::type())) + +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_CTOR_GETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) \ +{ \ + ajson.try_emplace(Name, aval.Getter() ); \ +} \ +else { \ + json_traits_helper::set_optional_json_member(Name, aval.Getter(), ajson); \ +} +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_TO_JSON_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_6(Getter, Name, Mode, Match, Into, From) \ +{ \ + ajson.try_emplace(Name, Into(aval.Getter()) ); \ +} \ +else { \ + json_traits_helper::set_optional_json_member(Name, Into(aval.Getter()), ajson); \ +} + +#define JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + return value_type ( JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_NAME_AS,,,, __VA_ARGS__) ); \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_NAME_TO_JSON,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS(ValueType, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_CTOR_GETTER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \ +JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ENUM_PAIR(Prefix, P2, P3, Member, Count) JSONCONS_ENUM_PAIR_LAST(Prefix, P2, P3, Member, Count), +#define JSONCONS_ENUM_PAIR_LAST(Prefix, P2, P3, Member, Count) {value_type::Member, json_traits_macro_names::Member##_str(char_type{})} + +#define JSONCONS_ENUM_TRAITS_BASE(EnumType, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_traits_macro_names \ + { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template \ + struct json_type_traits \ + { \ + static_assert(std::is_enum::value, # EnumType " must be an enum"); \ + using value_type = EnumType; \ + using char_type = typename Json::char_type; \ + using string_type = std::basic_string; \ + using string_view_type = jsoncons::basic_string_view; \ + using allocator_type = typename Json::allocator_type; \ + using mapped_type = std::pair; \ + \ + static std::pair get_values() \ + { \ + static const mapped_type v[] = { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_ENUM_PAIR, ,,, __VA_ARGS__)\ + };\ + return std::make_pair(v,v+JSONCONS_NARGS(__VA_ARGS__)); \ + } \ + \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_string()) return false; \ + auto first = get_values().first; \ + auto last = get_values().second; \ + const string_view_type s = ajson.template as(); \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return true; \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + return it != last; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # EnumType)); \ + const string_view_type s = ajson.template as(); \ + auto first = get_values().first; \ + auto last = get_values().second; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return value_type(); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + if (it == last) \ + { \ + if (s.empty()) \ + { \ + return value_type(); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \ + } \ + } \ + return it->first; \ + } \ + static Json to_json(value_type aval, allocator_type alloc=allocator_type()) \ + { \ + static constexpr char_type empty_string[] = {0}; \ + auto first = get_values().first; \ + auto last = get_values().second; \ + auto it = std::find_if(first, last, \ + [aval](const mapped_type& item) -> bool \ + { return item.first == aval; }); \ + if (it == last) \ + { \ + if (aval == value_type()) \ + { \ + return Json(empty_string); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \ + } \ + } \ + return Json(it->second,alloc); \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_ENUM_TRAITS(EnumType, ...) \ + JSONCONS_ENUM_TRAITS_BASE(EnumType,__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_NAME_ENUM_PAIR(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_NAME_ENUM_PAIR_ Seq), +#define JSONCONS_NAME_ENUM_PAIR_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_NAME_ENUM_PAIR_ Seq) +#define JSONCONS_NAME_ENUM_PAIR_(Member, Name) {value_type::Member, Name} + +#define JSONCONS_ENUM_NAME_TRAITS(EnumType, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_type_traits \ + { \ + static_assert(std::is_enum::value, # EnumType " must be an enum"); \ + using value_type = EnumType; \ + using char_type = typename Json::char_type; \ + using string_type = std::basic_string; \ + using string_view_type = jsoncons::basic_string_view; \ + using allocator_type = typename Json::allocator_type; \ + using mapped_type = std::pair; \ + \ + static std::pair get_values() \ + { \ + static const mapped_type v[] = { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_NAME_ENUM_PAIR,,,, __VA_ARGS__)\ + };\ + return std::make_pair(v,v+JSONCONS_NARGS(__VA_ARGS__)); \ + } \ + \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_string()) return false; \ + auto first = get_values().first; \ + auto last = get_values().second; \ + const string_view_type s = ajson.template as(); \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return true; \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + return it != last; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # EnumType)); \ + const string_view_type s = ajson.template as(); \ + auto first = get_values().first; \ + auto last = get_values().second; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return value_type(); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + if (it == last) \ + { \ + if (s.empty()) \ + { \ + return value_type(); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \ + } \ + } \ + return it->first; \ + } \ + static Json to_json(value_type aval, allocator_type alloc=allocator_type()) \ + { \ + static constexpr char_type empty_string[] = {0}; \ + auto first = get_values().first; \ + auto last = get_values().second; \ + auto it = std::find_if(first, last, \ + [aval](const mapped_type& item) -> bool \ + { return item.first == aval; }); \ + if (it == last) \ + { \ + if (aval == value_type()) \ + { \ + return Json(empty_string); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \ + } \ + } \ + return Json(it->second,alloc); \ + } \ + }; \ + template <> struct is_json_type_traits_declared : public std::true_type {}; \ +} \ + /**/ + +#define JSONCONS_GETTER_SETTER_AS(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_GETTER_SETTER_AS_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_GETTER_SETTER_AS_(Prefix, Getter, Setter, Property, Count) if ((num_params-Count) < num_mandatory_params2 || ajson.contains(json_traits_macro_names::Property##_str(char_type{}))) {aval.Setter(ajson.at(json_traits_macro_names::Property##_str(char_type{})).template as::type>());} + +#define JSONCONS_ALL_GETTER_SETTER_AS(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_AS_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, Getter, Setter, Property, Count) aval.Setter(ajson.at(json_traits_macro_names::Property##_str(char_type{})).template as::type>()); + +#define JSONCONS_GETTER_SETTER_TO_JSON(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_GETTER_SETTER_TO_JSON_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, Getter, Setter, Property, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ + {ajson.try_emplace(json_traits_macro_names::Property##_str(char_type{}), aval.Getter());} \ +else \ + {json_traits_helper::set_optional_json_member(json_traits_macro_names::Property##_str(char_type{}), aval.Getter(), ajson);} + +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, Getter, Setter, Property, Count) ajson.try_emplace(json_traits_macro_names::Property##_str(char_type{}), aval.Getter() ); + +#define JSONCONS_GETTER_SETTER_TRAITS_BASE(AsT,ToJ,NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_traits_macro_names \ + { \ + JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_N_MEMBER_IS, ,GetPrefix,SetPrefix, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + value_type aval{}; \ + JSONCONS_VARIADIC_REP_N(AsT, ,GetPrefix,SetPrefix, __VA_ARGS__) \ + return aval; \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(ToJ, ,GetPrefix,SetPrefix, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_N_GETTER_SETTER_TRAITS(ValueType,GetPrefix,SetPrefix,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,0, ValueType,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_GETTER_SETTER_TRAITS(NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_GETTER_SETTER_TRAITS(ValueType,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_AS, JSONCONS_ALL_GETTER_SETTER_TO_JSON,0,ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS(NumTemplateParams, ValueType,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_AS, JSONCONS_ALL_GETTER_SETTER_TO_JSON,NumTemplateParams,ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_GETTER_SETTER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_GETTER_SETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_GETTER_SETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_GETTER_SETTER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_GETTER_SETTER_NAME_IS_3(Getter, Setter, Name) !ajson.contains(Name)) return false; +#define JSONCONS_GETTER_SETTER_NAME_IS_5(Getter, Setter, Name, Mode, Match) JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match,, ) +#define JSONCONS_GETTER_SETTER_NAME_IS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Getter()))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_N_GETTER_SETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) if (ajson.contains(Name)) aval.Setter(ajson.at(Name).template as::type>()); +#define JSONCONS_N_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_N_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name)) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, From) Mode(if (ajson.contains(Name)) aval.Setter(From(ajson.at(Name).template as::type>()));) + +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_3(Getter, Setter, Name) ajson.try_emplace(Name, aval.Getter() ); +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, From) ajson.try_emplace(Name, Into(aval.Getter()) ); + +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_ALL_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) aval.Setter(ajson.at(Name).template as::type>()); +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_ALL_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name)) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_5(Getter, Setter, Name, Mode, Match) JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, From) Mode(aval.Setter(From(ajson.at(Name).template as::type>()));) + +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_3(Getter, Setter, Name) \ + ajson.try_emplace(Name, aval.Getter()); \ +else \ + {json_traits_helper::set_optional_json_member(Name, aval.Getter(), ajson);} +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_5(Getter, Setter, Name, Mode, Match) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, From) \ + ajson.try_emplace(Name, Into(aval.Getter())); \ +else \ + {json_traits_helper::set_optional_json_member(Name, Into(aval.Getter()), ajson);} + +#define JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(AsT,ToJ, NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons \ +{ \ + template \ + struct json_type_traits \ + { \ + using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using allocator_type = typename Json::allocator_type; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_GETTER_SETTER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + static value_type as(const Json& ajson) \ + { \ + if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \ + value_type aval{}; \ + JSONCONS_VARIADIC_REP_N(AsT,,,, __VA_ARGS__) \ + return aval; \ + } \ + static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \ + { \ + Json ajson(json_object_arg, semantic_tag::none, alloc); \ + JSONCONS_VARIADIC_REP_N(ToJ,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ +} \ + /**/ + +#define JSONCONS_N_GETTER_SETTER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_AS,JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, 0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_AS,JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS(ValueType, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_AS,JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_AS,JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_POLYMORPHIC_IS(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return true; +#define JSONCONS_POLYMORPHIC_IS_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return true; + +#define JSONCONS_POLYMORPHIC_AS(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return std::make_shared(ajson.template as()); +#define JSONCONS_POLYMORPHIC_AS_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return std::make_shared(ajson.template as()); + +#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return jsoncons::make_unique(ajson.template as()); +#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return jsoncons::make_unique(ajson.template as()); + +#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return std::make_shared(ajson.template as()); +#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return std::make_shared(ajson.template as()); + +#define JSONCONS_POLYMORPHIC_TO_JSON(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast(ptr.get())) {return Json(*p);} +#define JSONCONS_POLYMORPHIC_TO_JSON_LAST(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast(ptr.get())) {return Json(*p);} + +#define JSONCONS_POLYMORPHIC_TRAITS(BaseClass, ...) \ +namespace jsoncons { \ + template \ + struct json_type_traits> { \ + static bool is(const Json& ajson) noexcept { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\ + return false; \ + } \ +\ + static std::shared_ptr as(const Json& ajson) { \ + if (!ajson.is_object()) return std::shared_ptr(); \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_AS_SHARED_PTR, BaseClass,,, __VA_ARGS__)\ + return std::shared_ptr(); \ + } \ +\ + static Json to_json(const std::shared_ptr& ptr) { \ + if (ptr.get() == nullptr) {return Json::null();} \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\ + return Json::null(); \ + } \ + }; \ + template \ + struct json_type_traits> { \ + static bool is(const Json& ajson) noexcept { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\ + return false; \ + } \ + static std::unique_ptr as(const Json& ajson) { \ + if (!ajson.is_object()) return std::unique_ptr(); \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR, BaseClass,,, __VA_ARGS__)\ + return std::unique_ptr(); \ + } \ + static Json to_json(const std::unique_ptr& ptr) { \ + if (ptr.get() == nullptr) {return Json::null();} \ + JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\ + return Json::null(); \ + } \ + }; \ +} \ + /**/ + +#endif diff --git a/include/jsoncons/json_traits_macros_deprecated.hpp b/include/jsoncons/json_traits_macros_deprecated.hpp new file mode 100644 index 0000000..0d44e38 --- /dev/null +++ b/include/jsoncons/json_traits_macros_deprecated.hpp @@ -0,0 +1,144 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_TRAITS_MACROS_DEPRECATED_HPP +#define JSONCONS_JSON_TRAITS_MACROS_DEPRECATED_HPP + +#include + +#if !defined(JSONCONS_NO_DEPRECATED) + +#define JSONCONS_MEMBER_TRAITS_DECL(ValueType, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_AS,JSONCONS_TO_JSON,0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_TPL_MEMBER_TRAITS_DECL(NumTemplateParams, ValueType, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_AS,JSONCONS_TO_JSON,NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_MEMBER_NAMED_TRAITS_DECL(ValueType, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_NAME_AS, JSONCONS_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_TPL_MEMBER_NAMED_TRAITS_DECL(NumTemplateParams, ValueType, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_NAME_AS, JSONCONS_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_GETTER_SETTER_TRAITS_DECL(ValueType,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,0, ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_TPL_GETTER_SETTER_TRAITS_DECL(NumTemplateParams, ValueType,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,NumTemplateParams, ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_GETTER_SETTER_NAMED_TRAITS_DECL(ValueType, ...) \ +JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_GETTER_SETTER_NAME_AS,JSONCONS_GETTER_SETTER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_TPL_GETTER_SETTER_NAMED_TRAITS_DECL(NumTemplateParams, ValueType, ...) \ +JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_GETTER_SETTER_NAME_AS,JSONCONS_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \ + /**/ + +#define JSONCONS_ALL_GETTER_CTOR_TRAITS JSONCONS_ALL_CTOR_GETTER_TRAITS +#define JSONCONS_N_GETTER_CTOR_TRAITS JSONCONS_N_CTOR_GETTER_TRAITS +#define JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS JSONCONS_TPL_ALL_CTOR_GETTER_TRAITS +#define JSONCONS_TPL_N_GETTER_CTOR_TRAITS JSONCONS_TPL_N_CTOR_GETTER_TRAITS + +#define JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS +#define JSONCONS_N_GETTER_CTOR_NAME_TRAITS JSONCONS_N_CTOR_GETTER_NAME_TRAITS +#define JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS JSONCONS_TPL_ALL_CTOR_GETTER_NAME_TRAITS +#define JSONCONS_TPL_N_GETTER_CTOR_NAME_TRAITS JSONCONS_TPL_N_CTOR_GETTER_NAME_TRAITS + +#define JSONCONS_PROPERTY_TRAITS_DECL JSONCONS_GETTER_SETTER_TRAITS_DECL +#define JSONCONS_TPL_PROPERTY_TRAITS_DECL JSONCONS_TPL_GETTER_SETTER_TRAITS_DECL +#define JSONCONS_TYPE_TRAITS_DECL JSONCONS_MEMBER_TRAITS_DECL +#define JSONCONS_MEMBER_TRAITS_NAMED_DECL JSONCONS_MEMBER_NAMED_TRAITS_DECL +#define JSONCONS_TEMPLATE_MEMBER_TRAITS_NAMED_DECL JSONCONS_TPL_MEMBER_NAMED_TRAITS_DECL +#define JSONCONS_TEMPLATE_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_TPL_GETTER_SETTER_NAMED_TRAITS_DECL +#define JSONCONS_TEMPLATE_MEMBER_TRAITS_DECL JSONCONS_TPL_MEMBER_TRAITS_DECL + +#define JSONCONS_N_MEMBER_NAMED_TRAITS JSONCONS_N_MEMBER_NAME_TRAITS +#define JSONCONS_TPL_N_MEMBER_NAMED_TRAITS JSONCONS_TPL_N_MEMBER_NAME_TRAITS +#define JSONCONS_ALL_MEMBER_NAMED_TRAITS JSONCONS_ALL_MEMBER_NAME_TRAITS +#define JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS + +#define JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_N_GETTER_CTOR_NAMED_TRAITS JSONCONS_N_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS JSONCONS_TPL_N_GETTER_CTOR_NAME_TRAITS + +#define JSONCONS_ENUM_NAMED_TRAITS JSONCONS_ENUM_NAME_TRAITS + +#define JSONCONS_N_GETTER_SETTER_NAMED_TRAITS JSONCONS_N_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS JSONCONS_TPL_N_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS + +#define JSONCONS_N_MEMBER_TRAITS_DECL JSONCONS_N_MEMBER_TRAITS +#define JSONCONS_TPL_N_MEMBER_TRAITS_DECL JSONCONS_TPL_N_MEMBER_TRAITS +#define JSONCONS_ALL_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS +#define JSONCONS_TPL_ALL_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS +#define JSONCONS_N_MEMBER_NAMED_TRAITS_DECL JSONCONS_N_MEMBER_NAMED_TRAITS +#define JSONCONS_TPL_N_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_N_MEMBER_NAMED_TRAITS +#define JSONCONS_ALL_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_MEMBER_NAMED_TRAITS +#define JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS +#define JSONCONS_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_N_GETTER_CTOR_TRAITS_DECL JSONCONS_N_GETTER_CTOR_TRAITS +#define JSONCONS_N_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_N_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS +#define JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS +#define JSONCONS_N_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_N_GETTER_CTOR_NAMED_TRAITS +#define JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS +#define JSONCONS_ENUM_TRAITS_DECL JSONCONS_ENUM_TRAITS +#define JSONCONS_ENUM_NAMED_TRAITS_DECL JSONCONS_ENUM_NAMED_TRAITS +#define JSONCONS_N_GETTER_SETTER_TRAITS_DECL JSONCONS_N_GETTER_SETTER_TRAITS +#define JSONCONS_TPL_N_GETTER_SETTER_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_TRAITS +#define JSONCONS_ALL_GETTER_SETTER_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS +#define JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS +#define JSONCONS_N_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_N_GETTER_SETTER_NAMED_TRAITS +#define JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS +#define JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS +#define JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS +#define JSONCONS_POLYMORPHIC_TRAITS_DECL JSONCONS_POLYMORPHIC_TRAITS +#define JSONCONS_NONDEFAULT_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS +#define JSONCONS_TEMPLATE_STRICT_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS + +#define JSONCONS_STRICT_MEMBER_TRAITS_NAMED_DECL JSONCONS_ALL_MEMBER_NAME_TRAITS +#define JSONCONS_STRICT_TEMPLATE_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS +#define JSONCONS_STRICT_TEMPLATE_MEMBER_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS +#define JSONCONS_ENUM_TRAITS_NAMED_DECL JSONCONS_ENUM_NAME_TRAITS +#define JSONCONS_GETTER_CTOR_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_TEMPLATE_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_TEMPLATE_GETTER_CTOR_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_STRICT_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_STRICT_TEMPLATE_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_STRICT_TPL_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS +#define JSONCONS_STRICT_TPL_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS +#define JSONCONS_STRICT_TPL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS + +#define JSONCONS_STRICT_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS +#define JSONCONS_TPL_STRICT_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS +#define JSONCONS_STRICT_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_MEMBER_NAME_TRAITS +#define JSONCONS_TPL_STRICT_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_STRICT_MEMBER_NAME_TRAITS +#define JSONCONS_STRICT_PROPERTY_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS +#define JSONCONS_TPL_STRICT_PROPERTY_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS +#define JSONCONS_STRICT_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_TPL_STRICT_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS +#define JSONCONS_GETTER_CTOR_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_TPL_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS +#define JSONCONS_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_TPL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS +#define JSONCONS_N_PROPERTY_TRAITS_DECL JSONCONS_N_GETTER_SETTER_TRAITS +#define JSONCONS_ALL_PROPERTY_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS +#define JSONCONS_TPL_N_PROPERTY_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_TRAITS +#define JSONCONS_TPL_ALL_PROPERTY_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS + +#endif + +#endif diff --git a/include/jsoncons/json_type.hpp b/include/jsoncons/json_type.hpp new file mode 100644 index 0000000..f642d6b --- /dev/null +++ b/include/jsoncons/json_type.hpp @@ -0,0 +1,206 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_TYPE_HPP +#define JSONCONS_JSON_TYPE_HPP + +#include +#include + +namespace jsoncons { + + enum class json_type : uint8_t + { + null_value, + bool_value, + int64_value, + uint64_value, + half_value, + double_value, + string_value, + byte_string_value, + array_value, + object_value + }; + + template + std::basic_ostream& operator<<(std::basic_ostream& os, json_type type) + { + static constexpr const CharT* null_value = JSONCONS_CSTRING_CONSTANT(CharT, "null"); + static constexpr const CharT* bool_value = JSONCONS_CSTRING_CONSTANT(CharT, "bool"); + static constexpr const CharT* int64_value = JSONCONS_CSTRING_CONSTANT(CharT, "int64"); + static constexpr const CharT* uint64_value = JSONCONS_CSTRING_CONSTANT(CharT, "uint64"); + static constexpr const CharT* half_value = JSONCONS_CSTRING_CONSTANT(CharT, "half"); + static constexpr const CharT* double_value = JSONCONS_CSTRING_CONSTANT(CharT, "double"); + static constexpr const CharT* string_value = JSONCONS_CSTRING_CONSTANT(CharT, "string"); + static constexpr const CharT* byte_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string"); + static constexpr const CharT* array_value = JSONCONS_CSTRING_CONSTANT(CharT, "array"); + static constexpr const CharT* object_value = JSONCONS_CSTRING_CONSTANT(CharT, "object"); + + switch (type) + { + case json_type::null_value: + { + os << null_value; + break; + } + case json_type::bool_value: + { + os << bool_value; + break; + } + case json_type::int64_value: + { + os << int64_value; + break; + } + case json_type::uint64_value: + { + os << uint64_value; + break; + } + case json_type::half_value: + { + os << half_value; + break; + } + case json_type::double_value: + { + os << double_value; + break; + } + case json_type::string_value: + { + os << string_value; + break; + } + case json_type::byte_string_value: + { + os << byte_string_value; + break; + } + case json_type::array_value: + { + os << array_value; + break; + } + case json_type::object_value: + { + os << object_value; + break; + } + } + return os; + } + + enum class json_storage_kind : uint8_t + { + null_value = 0x00, + bool_value = 0x01, + int64_value = 0x02, + uint64_value = 0x03, + half_value = 0x04, + double_value = 0x05, + short_string_value = 0x06, + long_string_value = 0x07, + byte_string_value = 0x08, + array_value = 0x09, + empty_object_value = 0x0a, + object_value = 0x0b, + json_const_pointer = 0x0c + }; + + template + std::basic_ostream& operator<<(std::basic_ostream& os, json_storage_kind storage) + { + static constexpr const CharT* null_value = JSONCONS_CSTRING_CONSTANT(CharT, "null"); + static constexpr const CharT* bool_value = JSONCONS_CSTRING_CONSTANT(CharT, "bool"); + static constexpr const CharT* int64_value = JSONCONS_CSTRING_CONSTANT(CharT, "int64"); + static constexpr const CharT* uint64_value = JSONCONS_CSTRING_CONSTANT(CharT, "uint64"); + static constexpr const CharT* half_value = JSONCONS_CSTRING_CONSTANT(CharT, "half"); + static constexpr const CharT* double_value = JSONCONS_CSTRING_CONSTANT(CharT, "double"); + static constexpr const CharT* short_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "short_string"); + static constexpr const CharT* long_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "string"); + static constexpr const CharT* byte_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string"); + static constexpr const CharT* array_value = JSONCONS_CSTRING_CONSTANT(CharT, "array"); + static constexpr const CharT* empty_object_value = JSONCONS_CSTRING_CONSTANT(CharT, "empty_object"); + static constexpr const CharT* object_value = JSONCONS_CSTRING_CONSTANT(CharT, "object"); + static constexpr const CharT* json_const_pointer = JSONCONS_CSTRING_CONSTANT(CharT, "json_const_pointer"); + + switch (storage) + { + case json_storage_kind::null_value: + { + os << null_value; + break; + } + case json_storage_kind::bool_value: + { + os << bool_value; + break; + } + case json_storage_kind::int64_value: + { + os << int64_value; + break; + } + case json_storage_kind::uint64_value: + { + os << uint64_value; + break; + } + case json_storage_kind::half_value: + { + os << half_value; + break; + } + case json_storage_kind::double_value: + { + os << double_value; + break; + } + case json_storage_kind::short_string_value: + { + os << short_string_value; + break; + } + case json_storage_kind::long_string_value: + { + os << long_string_value; + break; + } + case json_storage_kind::byte_string_value: + { + os << byte_string_value; + break; + } + case json_storage_kind::array_value: + { + os << array_value; + break; + } + case json_storage_kind::empty_object_value: + { + os << empty_object_value; + break; + } + case json_storage_kind::object_value: + { + os << object_value; + break; + } + case json_storage_kind::json_const_pointer: + { + os << json_const_pointer; + break; + } + } + return os; + } + +} // jsoncons + +#endif diff --git a/include/jsoncons/json_type_traits.hpp b/include/jsoncons/json_type_traits.hpp new file mode 100644 index 0000000..7ee26b2 --- /dev/null +++ b/include/jsoncons/json_type_traits.hpp @@ -0,0 +1,1829 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_TYPE_TRAITS_HPP +#define JSONCONS_JSON_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::swap +#include // std::numeric_limits +#include // std::enable_if +#include // std::iterator_traits, std::input_iterator_tag +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::bitset +#include +#include + +#if defined(JSONCONS_HAS_STD_VARIANT) + #include +#endif + +namespace jsoncons { + + template + struct is_json_type_traits_declared : public std::false_type + {}; + + #if !defined(JSONCONS_NO_DEPRECATED) + template + using is_json_type_traits_impl = is_json_type_traits_declared; + #endif + + // json_type_traits + + template + struct unimplemented : std::false_type + {}; + + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + static constexpr bool is_compatible = false; + + static constexpr bool is(const Json&) noexcept + { + return false; + } + + static T as(const Json&) + { + static_assert(unimplemented::value, "as not implemented"); + } + + static Json to_json(const T&, const allocator_type& = allocator_type()) + { + static_assert(unimplemented::value, "to_json not implemented"); + } + }; + +namespace detail { + +template +using +traits_can_convert_t = decltype(json_type_traits::can_convert(Json())); + +template +using +has_can_convert = type_traits::is_detected; + + template + struct invoke_can_convert + { + template + static + typename std::enable_if::value,bool>::type + can_convert(const Json& j) noexcept + { + return json_type_traits::can_convert(j); + } + template + static + typename std::enable_if::value,bool>::type + can_convert(const Json& j) noexcept + { + return json_type_traits::is(j); + } + }; + + // is_json_type_traits_unspecialized + template + struct is_json_type_traits_unspecialized : std::false_type {}; + + // is_json_type_traits_unspecialized + template + struct is_json_type_traits_unspecialized::is_compatible>::value>::type + > : std::true_type {}; + + // is_compatible_array_type + template + struct is_compatible_array_type : std::false_type {}; + + template + struct is_compatible_array_type::value && + type_traits::is_list_like::value && + !is_json_type_traits_unspecialized::value_type>::value + >::type> : std::true_type {}; + +} // namespace detail + + // is_json_type_traits_specialized + template + struct is_json_type_traits_specialized : std::false_type {}; + + template + struct is_json_type_traits_specialized::value + >::type> : std::true_type {}; + + template + struct json_type_traits::type*> + { + using char_type = typename Json::char_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + static const char_type* as(const Json& j) + { + return j.as_cstring(); + } + template + static Json to_json(const char_type* s, Args&&... args) + { + return Json(s, semantic_tag::none, std::forward(args)...); + } + }; + + template + struct json_type_traits::type*> + { + using char_type = typename Json::char_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + template + static Json to_json(const char_type* s, Args&&... args) + { + return Json(s, semantic_tag::none, std::forward(args)...); + } + }; + + // integer + + template + struct json_type_traits::value + >::type> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.template is_integer(); + } + static T as(const Json& j) + { + return j.template as_integer(); + } + + static Json to_json(T val, allocator_type alloc = allocator_type()) + { + return Json(val, semantic_tag::none, alloc); + } + }; + + template + struct json_type_traits::value + >::type> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_double(); + } + static T as(const Json& j) + { + return static_cast(j.as_double()); + } + static Json to_json(T val, allocator_type = allocator_type()) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_type_traits + { + using json_object = typename Json::object; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_object(); + } + static Json to_json(const json_object& o, const allocator_type& = allocator_type()) + { + return Json(o,semantic_tag::none); + } + }; + + template + struct json_type_traits + { + using json_array = typename Json::array; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_array(); + } + static Json to_json(const json_array& a, const allocator_type& = allocator_type()) + { + return Json(a, semantic_tag::none); + } + }; + + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json&) noexcept + { + return true; + } + static Json as(Json j) + { + return j; + } + static Json to_json(const Json& val, allocator_type = allocator_type()) + { + return val; + } + }; + + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_null(); + } + static typename jsoncons::null_type as(const Json& j) + { + if (!j.is_null()) + { + JSONCONS_THROW(conv_error(conv_errc::not_jsoncons_null_type)); + } + return jsoncons::null_type(); + } + static Json to_json(jsoncons::null_type, allocator_type = allocator_type()) + { + return Json::null(); + } + }; + + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + static Json to_json(bool val, allocator_type = allocator_type()) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_type_traits::const_reference>::value, + std::vector::const_reference, + void>::type>::value>::type> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + static Json to_json(bool val, allocator_type = allocator_type()) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_type_traits::reference> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + static Json to_json(bool val, allocator_type = allocator_type()) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_type_traits::value && + type_traits::is_basic_string::value && + std::is_same::value>::type> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + + static T as(const Json& j) + { + return T(j.as_string()); + } + + static Json to_json(const T& val) + { + return Json(val, semantic_tag::none); + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + return Json(val, semantic_tag::none, alloc); + } + }; + + template + struct json_type_traits::value && + type_traits::is_basic_string::value && + !std::is_same::value>::type> + { + using char_type = typename Json::char_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + + static T as(const Json& j) + { + auto s = j.as_string(); + T val; + unicode_traits::convert(s.data(), s.size(), val); + return val; + } + + static Json to_json(const T& val) + { + std::basic_string s; + unicode_traits::convert(val.data(), val.size(), s); + + return Json(s, semantic_tag::none); + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + std::basic_string s; + unicode_traits::convert(val.data(), val.size(), s); + return Json(s, semantic_tag::none, alloc); + } + }; + + template + struct json_type_traits::value && + type_traits::is_basic_string_view::value && + std::is_same::value>::type> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_string_view(); + } + + static T as(const Json& j) + { + return T(j.as_string_view().data(),j.as_string_view().size()); + } + + static Json to_json(const T& val) + { + return Json(val, semantic_tag::none); + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + return Json(val, semantic_tag::none, alloc); + } + }; + + // array back insertable + + template + struct json_type_traits::value && + jsoncons::detail::is_compatible_array_type::value && + type_traits::is_back_insertable::value + >::type> + { + typedef typename std::iterator_traits::value_type value_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + // array back insertable non-byte container + + template + static typename std::enable_if::value,Container>::type + as(const Json& j) + { + if (j.is_array()) + { + T result; + visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); + for (const auto& item : j.array_range()) + { + result.push_back(item.template as()); + } + + return result; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_vector)); + } + } + + // array back insertable byte container + + template + static typename std::enable_if::value,Container>::type + as(const Json& j) + { + converter convert; + std::error_code ec; + if (j.is_array()) + { + T result; + visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); + for (const auto& item : j.array_range()) + { + result.push_back(item.template as()); + } + + return result; + } + else if (j.is_byte_string_view()) + { + auto v = convert.from(j.as_byte_string_view(),j.tag(), ec); + if (ec) + { + JSONCONS_THROW(conv_error(ec)); + } + return v; + } + else if (j.is_string()) + { + auto v = convert.from(j.as_string_view(),j.tag(), ec); + if (ec) + { + JSONCONS_THROW(conv_error(ec)); + } + return v; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_vector)); + } + } + + template + static typename std::enable_if::value,Json>::type + to_json(const T& val) + { + Json j(json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + template + static typename std::enable_if::value,Json>::type + to_json(const T& val, const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + template + static typename std::enable_if::value,Json>::type + to_json(const T& val) + { + Json j(byte_string_arg, val); + return j; + } + + template + static typename std::enable_if::value,Json>::type + to_json(const T& val, const allocator_type& alloc) + { + Json j(byte_string_arg, val, semantic_tag::none, alloc); + return j; + } + + static void visit_reserve_(std::true_type, T& v, std::size_t size) + { + v.reserve(size); + } + + static void visit_reserve_(std::false_type, T&, std::size_t) + { + } + }; + + // array, not back insertable but insertable + + template + struct json_type_traits::value && + jsoncons::detail::is_compatible_array_type::value && + !type_traits::is_back_insertable::value && + type_traits::is_insertable::value>::type> + { + typedef typename std::iterator_traits::value_type value_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static T as(const Json& j) + { + if (j.is_array()) + { + T result; + for (const auto& item : j.array_range()) + { + result.insert(item.template as()); + } + + return result; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_vector)); + } + } + + static Json to_json(const T& val) + { + Json j(json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // array not back insertable or insertable, but front insertable + + template + struct json_type_traits::value && + jsoncons::detail::is_compatible_array_type::value && + !type_traits::is_back_insertable::value && + !type_traits::is_insertable::value && + type_traits::is_front_insertable::value>::type> + { + typedef typename std::iterator_traits::value_type value_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static T as(const Json& j) + { + if (j.is_array()) + { + T result; + + auto it = j.array_range().rbegin(); + auto end = j.array_range().rend(); + for (; it != end; ++it) + { + result.push_front((*it).template as()); + } + + return result; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_vector)); + } + } + + static Json to_json(const T& val) + { + Json j(json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // std::array + + template + struct json_type_traits> + { + using allocator_type = typename Json::allocator_type; + + using value_type = E; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array() && j.size() == N; + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static std::array as(const Json& j) + { + std::array buff; + if (j.size() != N) + { + JSONCONS_THROW(conv_error(conv_errc::not_array)); + } + for (std::size_t i = 0; i < N; i++) + { + buff[i] = j[i].template as(); + } + return buff; + } + + static Json to_json(const std::array& val) + { + Json j(json_array_arg); + j.reserve(N); + for (auto it = val.begin(); it != val.end(); ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const std::array& val, + const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + j.reserve(N); + for (auto it = val.begin(); it != val.end(); ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // map like + template + struct json_type_traits::value && + type_traits::is_map_like::value && + type_traits::is_constructible_from_const_pointer_and_size::value && + is_json_type_traits_specialized::value>::type + > + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + bool result = j.is_object(); + for (auto member : j.object_range()) + { + if (!member.value().template is()) + { + result = false; + } + } + return result; + } + + static T as(const Json& j) + { + if (!j.is_object()) + { + JSONCONS_THROW(conv_error(conv_errc::not_map)); + } + T result; + for (const auto& item : j.object_range()) + { + result.emplace(key_type(item.key().data(),item.key().size()), item.value().template as()); + } + + return result; + } + + static Json to_json(const T& val) + { + Json j(json_object_arg, val.begin(), val.end()); + return j; + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + Json j(json_object_arg, val.begin(), val.end(), alloc); + return j; + } + }; + + template + struct json_type_traits::value && + type_traits::is_map_like::value && + !type_traits::is_constructible_from_const_pointer_and_size::value && + is_json_type_traits_specialized::value && + is_json_type_traits_specialized::value>::type + > + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& val) noexcept + { + if (!val.is_object()) + return false; + for (const auto& item : val.object_range()) + { + Json j(item.key()); + if (!j.template is()) + { + return false; + } + if (!item.value().template is()) + { + return false; + } + } + return true; + } + + static T as(const Json& val) + { + T result; + for (const auto& item : val.object_range()) + { + Json j(item.key()); + auto key = json_type_traits::as(j); + result.emplace(std::move(key), item.value().template as()); + } + + return result; + } + + static Json to_json(const T& val) + { + Json j(json_object_arg); + j.reserve(val.size()); + for (const auto& item : val) + { + auto temp = json_type_traits::to_json(item.first); + typename Json::key_type key; + temp.dump(key); + j.try_emplace(std::move(key), item.second); + } + return j; + } + + static Json to_json(const T& val, const allocator_type& alloc) + { + Json j(json_object_arg, semantic_tag::none, alloc); + j.reserve(val.size()); + for (const auto& item : val) + { + auto temp = json_type_traits::to_json(item.first, alloc); + typename Json::key_type key(alloc); + temp.dump(key); + j.try_emplace(std::move(key), item.second, alloc); + } + return j; + } + }; + + namespace tuple_detail + { + template + struct json_tuple_helper + { + using element_type = typename std::tuple_element::type; + using next = json_tuple_helper; + + static bool is(const Json& j) noexcept + { + if (j[Size-Pos].template is()) + { + return next::is(j); + } + else + { + return false; + } + } + + static void as(Tuple& tuple, const Json& j) + { + std::get(tuple) = j[Size-Pos].template as(); + next::as(tuple, j); + } + + static void to_json(const Tuple& tuple, Json& j) + { + j.push_back(json_type_traits::to_json(std::get(tuple))); + next::to_json(tuple, j); + } + }; + + template + struct json_tuple_helper<0, Size, Json, Tuple> + { + static bool is(const Json&) noexcept + { + return true; + } + + static void as(Tuple&, const Json&) + { + } + + static void to_json(const Tuple&, Json&) + { + } + }; + } // namespace detail + + template + struct json_type_traits> + { + private: + using helper = tuple_detail::json_tuple_helper>; + + public: + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return helper::is(j); + } + + static std::tuple as(const Json& j) + { + std::tuple buff; + helper::as(buff, j); + return buff; + } + + static Json to_json(const std::tuple& val) + { + Json j(json_array_arg); + j.reserve(sizeof...(E)); + helper::to_json(val, j); + return j; + } + + static Json to_json(const std::tuple& val, + const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + j.reserve(sizeof...(E)); + helper::to_json(val, j); + return j; + } + }; + + template + struct json_type_traits> + { + public: + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_array() && j.size() == 2; + } + + static std::pair as(const Json& j) + { + return std::make_pair(j[0].template as(),j[1].template as()); + } + + static Json to_json(const std::pair& val) + { + Json j(json_array_arg); + j.reserve(2); + j.push_back(val.first); + j.push_back(val.second); + return j; + } + + static Json to_json(const std::pair& val, const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + j.reserve(2); + j.push_back(val.first); + j.push_back(val.second); + return j; + } + }; + + template + struct json_type_traits::value>::type> + { + public: + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_byte_string(); + } + + static T as(const Json& j) + { + return j.template as_byte_string(); + } + + static Json to_json(const T& val, + const allocator_type& alloc = allocator_type()) + { + return Json(byte_string_arg, val, semantic_tag::none, alloc); + } + }; + + template + struct json_type_traits, + typename std::enable_if>::value && + !std::is_polymorphic::value + >::type> + { + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + static std::shared_ptr as(const Json& j) + { + return j.is_null() ? std::shared_ptr(nullptr) : std::make_shared(j.template as()); + } + + static Json to_json(const std::shared_ptr& ptr) + { + if (ptr.get() != nullptr) + { + Json j(*ptr); + return j; + } + else + { + return Json::null(); + } + } + }; + + template + struct json_type_traits, + typename std::enable_if>::value && + !std::is_polymorphic::value + >::type> + { + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + static std::unique_ptr as(const Json& j) + { + return j.is_null() ? std::unique_ptr(nullptr) : jsoncons::make_unique(j.template as()); + } + + static Json to_json(const std::unique_ptr& ptr) + { + if (ptr.get() != nullptr) + { + Json j(*ptr); + return j; + } + else + { + return Json::null(); + } + } + }; + + template + struct json_type_traits, + typename std::enable_if>::value>::type> + { + public: + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + static jsoncons::optional as(const Json& j) + { + return j.is_null() ? jsoncons::optional() : jsoncons::optional(j.template as()); + } + + static Json to_json(const jsoncons::optional& val) + { + return val.has_value() ? Json(*val) : Json::null(); + } + }; + + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + public: + static bool is(const Json& j) noexcept + { + return j.is_byte_string_view(); + } + + static byte_string_view as(const Json& j) + { + return j.as_byte_string_view(); + } + + static Json to_json(const byte_string_view& val, const allocator_type& alloc = allocator_type()) + { + return Json(byte_string_arg, val, semantic_tag::none, alloc); + } + }; + + // basic_bigint + + template + struct json_type_traits> + { + public: + using char_type = typename Json::char_type; + + static bool is(const Json& j) noexcept + { + switch (j.type()) + { + case json_type::string_value: + return jsoncons::detail::is_base10(j.as_string_view().data(), j.as_string_view().length()); + case json_type::int64_value: + case json_type::uint64_value: + return true; + default: + return false; + } + } + + static basic_bigint as(const Json& j) + { + switch (j.type()) + { + case json_type::string_value: + if (!jsoncons::detail::is_base10(j.as_string_view().data(), j.as_string_view().length())) + { + JSONCONS_THROW(conv_error(conv_errc::not_bigint)); + } + return basic_bigint::from_string(j.as_string_view().data(), j.as_string_view().length()); + case json_type::half_value: + case json_type::double_value: + return basic_bigint(j.template as()); + case json_type::int64_value: + return basic_bigint(j.template as()); + case json_type::uint64_value: + return basic_bigint(j.template as()); + default: + JSONCONS_THROW(conv_error(conv_errc::not_bigint)); + } + } + + static Json to_json(const basic_bigint& val) + { + std::basic_string s; + val.write_string(s); + return Json(s,semantic_tag::bigint); + } + }; + + // std::valarray + + template + struct json_type_traits> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static std::valarray as(const Json& j) + { + if (j.is_array()) + { + std::valarray v(j.size()); + for (std::size_t i = 0; i < j.size(); ++i) + { + v[i] = j[i].template as(); + } + return v; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_array)); + } + } + + static Json to_json(const std::valarray& val) + { + Json j(json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const std::valarray& val, const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + +#if defined(JSONCONS_HAS_STD_VARIANT) + +namespace variant_detail +{ + template + typename std::enable_if, bool>::type + is_variant(const Json& /*j*/) + { + return false; + } + + template + typename std::enable_if, bool>::type + is_variant(const Json& j) + { + if (j.template is()) + { + return true; + } + else + { + return is_variant(j); + } + } + + template + typename std::enable_if, Variant>::type + as_variant(const Json& /*j*/) + { + JSONCONS_THROW(conv_error(conv_errc::not_variant)); + } + + template + typename std::enable_if, Variant>::type + as_variant(const Json& j) + { + if (j.template is()) + { + Variant var(j.template as()); + return var; + } + else + { + return as_variant(j); + } + } + + template + struct variant_to_json_visitor + { + Json& j_; + + variant_to_json_visitor(Json& j) : j_(j) {} + + template + void operator()(const T& value) const + { + j_ = value; + } + }; + +} // namespace variant_detail + + template + struct json_type_traits> + { + public: + using variant_type = typename std::variant; + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return variant_detail::is_variant<0,Json,variant_type, VariantTypes...>(j); + } + + static std::variant as(const Json& j) + { + return variant_detail::as_variant<0,Json,variant_type, VariantTypes...>(j); + } + + static Json to_json(const std::variant& var) + { + Json j(json_array_arg); + variant_detail::variant_to_json_visitor visitor(j); + std::visit(visitor, var); + return j; + } + + static Json to_json(const std::variant& var, + const allocator_type& alloc) + { + Json j(json_array_arg, alloc); + variant_detail::variant_to_json_visitor visitor(j); + std::visit(visitor, var); + return j; + } + }; +#endif + + // std::chrono::duration + template + struct json_type_traits> + { + using duration_type = std::chrono::duration; + + using allocator_type = typename Json::allocator_type; + + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; + + static bool is(const Json& j) noexcept + { + return (j.tag() == semantic_tag::epoch_second || j.tag() == semantic_tag::epoch_milli || j.tag() == semantic_tag::epoch_nano); + } + + static duration_type as(const Json& j) + { + return from_json_(j); + } + + static Json to_json(const duration_type& val, allocator_type = allocator_type()) + { + return to_json_(val); + } + + template + static + typename std::enable_if>::value, duration_type>::type + from_json_(const Json& j) + { + if (j.is_int64() || j.is_uint64() || j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(count); + case semantic_tag::epoch_milli: + return duration_type(count == 0 ? 0 : count/millis_in_second); + case semantic_tag::epoch_nano: + return duration_type(count == 0 ? 0 : count/nanos_in_second); + default: + return duration_type(count); + } + } + else if (j.is_string()) + { + switch (j.tag()) + { + case semantic_tag::epoch_second: + { + auto count = j.template as(); + return duration_type(count); + } + case semantic_tag::epoch_milli: + { + auto sv = j.as_string_view(); + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + n = n / millis_in_second; + } + return duration_type(static_cast(n)); + } + case semantic_tag::epoch_nano: + { + auto sv = j.as_string_view(); + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + n = n / nanos_in_second; + } + return duration_type(static_cast(n)); + } + default: + { + auto count = j.template as(); + return duration_type(count); + } + } + } + else + { + return duration_type(); + } + } + + template + static + typename std::enable_if::value, duration_type>::type + from_json_(const Json& j) + { + if (j.is_int64() || j.is_uint64()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(count*millis_in_second); + case semantic_tag::epoch_milli: + return duration_type(count); + case semantic_tag::epoch_nano: + return duration_type(count == 0 ? 0 : count/nanos_in_milli); + default: + return duration_type(count); + } + } + else if (j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(static_cast(count * millis_in_second)); + case semantic_tag::epoch_milli: + return duration_type(static_cast(count)); + case semantic_tag::epoch_nano: + return duration_type(count == 0 ? 0 : static_cast(count / nanos_in_milli)); + default: + return duration_type(static_cast(count)); + } + } + else if (j.is_string()) + { + switch (j.tag()) + { + case semantic_tag::epoch_second: + { + auto count = j.template as(); + return duration_type(count*millis_in_second); + } + case semantic_tag::epoch_milli: + { + auto sv = j.as_string_view(); + Rep n{0}; + auto result = jsoncons::detail::to_integer_decimal(sv.data(), sv.size(), n); + if (!result) + { + return duration_type(); + } + return duration_type(n); + } + case semantic_tag::epoch_nano: + { + auto sv = j.as_string_view(); + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + n = n / nanos_in_milli; + } + return duration_type(static_cast(n)); + } + default: + { + auto count = j.template as(); + return duration_type(count); + } + } + } + else + { + return duration_type(); + } + } + + template + static + typename std::enable_if::value, duration_type>::type + from_json_(const Json& j) + { + if (j.is_int64() || j.is_uint64() || j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(count*nanos_in_second); + case semantic_tag::epoch_milli: + return duration_type(count*nanos_in_milli); + case semantic_tag::epoch_nano: + return duration_type(count); + default: + return duration_type(count); + } + } + else if (j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(static_cast(count * nanos_in_second)); + case semantic_tag::epoch_milli: + return duration_type(static_cast(count * nanos_in_milli)); + case semantic_tag::epoch_nano: + return duration_type(static_cast(count)); + default: + return duration_type(static_cast(count)); + } + } + else if (j.is_string()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return duration_type(count*nanos_in_second); + case semantic_tag::epoch_milli: + return duration_type(count*nanos_in_milli); + case semantic_tag::epoch_nano: + return duration_type(count); + default: + return duration_type(count); + } + } + else + { + return duration_type(); + } + } + + template + static + typename std::enable_if>::value,Json>::type + to_json_(const duration_type& val) + { + return Json(val.count(), semantic_tag::epoch_second); + } + + template + static + typename std::enable_if::value,Json>::type + to_json_(const duration_type& val) + { + return Json(val.count(), semantic_tag::epoch_milli); + } + + template + static + typename std::enable_if::value,Json>::type + to_json_(const duration_type& val) + { + return Json(val.count(), semantic_tag::epoch_nano); + } + }; + + // std::nullptr_t + template + struct json_type_traits + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + return j.is_null(); + } + + static std::nullptr_t as(const Json& j) + { + if (!j.is_null()) + { + JSONCONS_THROW(conv_error(conv_errc::not_nullptr)); + } + return nullptr; + } + + static Json to_json(const std::nullptr_t&, allocator_type = allocator_type()) + { + return Json::null(); + } + }; + + // std::bitset + + struct null_back_insertable_byte_container + { + using value_type = uint8_t; + + void push_back(value_type) + { + } + }; + + template + struct json_type_traits> + { + using allocator_type = typename Json::allocator_type; + + static bool is(const Json& j) noexcept + { + if (j.is_byte_string()) + { + return true; + } + else if (j.is_string()) + { + jsoncons::string_view sv = j.as_string_view(); + null_back_insertable_byte_container cont; + auto result = decode_base16(sv.begin(), sv.end(), cont); + return result.ec == conv_errc::success ? true : false; + } + return false; + } + + static std::bitset as(const Json& j) + { + if (j.template is()) + { + auto bits = j.template as(); + std::bitset bs = static_cast(bits); + return bs; + } + else if (j.is_byte_string() || j.is_string()) + { + std::bitset bs; + std::vector bits; + if (j.is_byte_string()) + { + bits = j.template as>(); + } + else + { + jsoncons::string_view sv = j.as_string_view(); + auto result = decode_base16(sv.begin(), sv.end(), bits); + if (result.ec != conv_errc::success) + { + JSONCONS_THROW(conv_error(conv_errc::not_bitset)); + } + } + std::uint8_t byte = 0; + std::uint8_t mask = 0; + + std::size_t pos = 0; + for (std::size_t i = 0; i < N; ++i) + { + if (mask == 0) + { + if (pos >= bits.size()) + { + JSONCONS_THROW(conv_error(conv_errc::not_bitset)); + } + byte = bits.at(pos++); + mask = 0x80; + } + + if (byte & mask) + { + bs[i] = 1; + } + + mask = static_cast(mask >> 1); + } + return bs; + } + else + { + JSONCONS_THROW(conv_error(conv_errc::not_bitset)); + } + } + + static Json to_json(const std::bitset& val, + const allocator_type& alloc = allocator_type()) + { + std::vector bits; + + uint8_t byte = 0; + uint8_t mask = 0x80; + + for (std::size_t i = 0; i < N; ++i) + { + if (val[i]) + { + byte |= mask; + } + + mask = static_cast(mask >> 1); + + if (mask == 0) + { + bits.push_back(byte); + byte = 0; + mask = 0x80; + } + } + + // Encode remainder + if (mask != 0x80) + { + bits.push_back(byte); + } + + Json j(byte_string_arg, bits, semantic_tag::base16, alloc); + return j; + } + }; + +} // jsoncons + +#endif diff --git a/include/jsoncons/json_visitor.hpp b/include/jsoncons/json_visitor.hpp new file mode 100644 index 0000000..8be5900 --- /dev/null +++ b/include/jsoncons/json_visitor.hpp @@ -0,0 +1,1560 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_VISITOR_HPP +#define JSONCONS_JSON_VISITOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + template + class basic_json_visitor + { + public: + using char_type = CharT; + using char_traits_type = std::char_traits; + + using string_view_type = jsoncons::basic_string_view; + + basic_json_visitor(basic_json_visitor&&) = default; + + basic_json_visitor& operator=(basic_json_visitor&&) = default; + + basic_json_visitor() = default; + + virtual ~basic_json_visitor() noexcept = default; + + void flush() + { + visit_flush(); + } + + bool begin_object(semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_object(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_object(std::size_t length, + semantic_tag tag=semantic_tag::none, + const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_begin_object(length, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_object(const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_end_object(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_array(semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_array(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_array(std::size_t length, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_array(length, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_array(const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_end_array(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool key(const string_view_type& name, const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_key(name, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool null_value(semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_null(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool bool_value(bool value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_bool(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool string_value(const string_view_type& value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_string(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool byte_string_value(const Source& b, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context(), + typename std::enable_if::value,int>::type = 0) + { + std::error_code ec; + bool more = visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool byte_string_value(const Source& b, + uint64_t ext_tag, + const ser_context& context=ser_context(), + typename std::enable_if::value,int>::type = 0) + { + std::error_code ec; + bool more = visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), ext_tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool uint64_value(uint64_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_uint64(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool int64_value(int64_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_int64(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool half_value(uint16_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_half(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool double_value(double value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_double(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_object(semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(tag, context, ec); + } + + bool begin_object(std::size_t length, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(length, tag, context, ec); + } + + bool end_object(const ser_context& context, std::error_code& ec) + { + return visit_end_object(context, ec); + } + + bool begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) + { + return visit_begin_array(tag, context, ec); + } + + bool begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) + { + return visit_begin_array(length, tag, context, ec); + } + + bool end_array(const ser_context& context, std::error_code& ec) + { + return visit_end_array(context, ec); + } + + bool key(const string_view_type& name, const ser_context& context, std::error_code& ec) + { + return visit_key(name, context, ec); + } + + bool null_value(semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_null(tag, context, ec); + } + + bool bool_value(bool value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_bool(value, tag, context, ec); + } + + bool string_value(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_string(value, tag, context, ec); + } + + template + bool byte_string_value(const Source& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec, + typename std::enable_if::value,int>::type = 0) + { + return visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), tag, context, ec); + } + + template + bool byte_string_value(const Source& b, + uint64_t ext_tag, + const ser_context& context, + std::error_code& ec, + typename std::enable_if::value,int>::type = 0) + { + return visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), ext_tag, context, ec); + } + + bool uint64_value(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_uint64(value, tag, context, ec); + } + + bool int64_value(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_int64(value, tag, context, ec); + } + + bool half_value(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_half(value, tag, context, ec); + } + + bool double_value(double value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_double(value, tag, context, ec); + } + + template + bool typed_array(const jsoncons::span& data, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_typed_array(data, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_typed_array(data, tag, context, ec); + } + + bool typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag = semantic_tag::none, + const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_typed_array(half_arg, s, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_typed_array(half_arg, s, tag, context, ec); + } + + bool begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag = semantic_tag::multi_dim_row_major, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_multi_dim(shape, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_multi_dim(shape, tag, context, ec); + } + + bool end_multi_dim(const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_end_multi_dim(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_multi_dim(const ser_context& context, + std::error_code& ec) + { + return visit_end_multi_dim(context, ec); + } + + #if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const Source&,semantic_tag=semantic_tag::none, const ser_context&=ser_context()") + bool byte_string_value(const uint8_t* p, std::size_t size, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + return byte_string_value(byte_string(p, size), tag, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const Source&, semantic_tag, const ser_context&, std::error_code&") + bool byte_string_value(const uint8_t* p, std::size_t size, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return byte_string_value(byte_string(p, size), tag, context, ec); + } + + JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&=ser_context())") + bool name(const string_view_type& name, const ser_context& context=ser_context()) + { + return key(name, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&, std::error_code&)") + bool name(const string_view_type& name, const ser_context& context, std::error_code& ec) + { + return key(name, context, ec); + } + + JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const byte_string_view&, semantic_tag=semantic_tag::none, const ser_context&=ser_context()") + bool byte_string_value(const byte_string_view& b, + byte_string_chars_format encoding_hint, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + switch (encoding_hint) + { + case byte_string_chars_format::base16: + tag = semantic_tag::base16; + break; + case byte_string_chars_format::base64: + tag = semantic_tag::base64; + break; + case byte_string_chars_format::base64url: + tag = semantic_tag::base64url; + break; + default: + break; + } + return byte_string_value(b, tag, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const byte_string_view&, semantic_tag=semantic_tag::none, const ser_context&=ser_context()") + bool byte_string_value(const uint8_t* p, std::size_t size, + byte_string_chars_format encoding_hint, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + switch (encoding_hint) + { + case byte_string_chars_format::base16: + tag = semantic_tag::base16; + break; + case byte_string_chars_format::base64: + tag = semantic_tag::base64; + break; + case byte_string_chars_format::base64url: + tag = semantic_tag::base64url; + break; + default: + break; + } + return byte_string_value(byte_string(p, size), tag, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigint") + bool big_integer_value(const string_view_type& value, const ser_context& context=ser_context()) + { + return string_value(value, semantic_tag::bigint, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigdec") + bool big_decimal_value(const string_view_type& value, const ser_context& context=ser_context()) + { + return string_value(value, semantic_tag::bigdec, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::datetime") + bool date_time_value(const string_view_type& value, const ser_context& context=ser_context()) + { + return string_value(value, semantic_tag::datetime, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use int64_value with semantic_tag::epoch_second") + bool timestamp_value(int64_t val, const ser_context& context=ser_context()) + { + return int64_value(val, semantic_tag::epoch_second, context); + } + + JSONCONS_DEPRECATED_MSG("Remove calls to this method, it doesn't do anything") + bool begin_document() + { + return true; + } + + JSONCONS_DEPRECATED_MSG("Instead, use flush() when serializing") + bool end_document() + { + flush(); + return true; + } + + JSONCONS_DEPRECATED_MSG("Remove calls to this method, it doesn't do anything") + void begin_json() + { + } + + JSONCONS_DEPRECATED_MSG("Instead, use flush() when serializing") + void end_json() + { + end_document(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&=ser_context())") + void name(const char_type* p, std::size_t length, const ser_context& context) + { + name(string_view_type(p, length), context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())") + void integer_value(int64_t value) + { + int64_value(value); + } + + JSONCONS_DEPRECATED_MSG("Instead, use int64_value(int64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())") + void integer_value(int64_t value, const ser_context& context) + { + int64_value(value,context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())") + void uinteger_value(uint64_t value) + { + uint64_value(value); + } + + JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())") + void uinteger_value(uint64_t value, const ser_context& context) + { + uint64_value(value,context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigint") + bool bignum_value(const string_view_type& value, const ser_context& context=ser_context()) + { + return string_value(value, semantic_tag::bigint, context); + } + + JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigdec") + bool decimal_value(const string_view_type& value, const ser_context& context=ser_context()) + { + return string_value(value, semantic_tag::bigdec, context); + } + + #endif + private: + + virtual void visit_flush() = 0; + + virtual bool visit_begin_object(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_object(std::size_t /*length*/, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(tag, context, ec); + } + + virtual bool visit_end_object(const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_array(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_array(std::size_t /*length*/, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_array(tag, context, ec); + } + + virtual bool visit_end_array(const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code&) = 0; + + virtual bool visit_null(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_bool(bool value, + semantic_tag tag, + const ser_context& context, + std::error_code&) = 0; + + virtual bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_byte_string(const byte_string_view& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_byte_string(const byte_string_view& value, + uint64_t /* ext_tag */, + const ser_context& context, + std::error_code& ec) + { + return visit_byte_string(value, semantic_tag::none, context, ec); + } + + virtual bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_double(binary::decode_half(value), + tag, + context, + ec); + } + + virtual bool visit_double(double value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = half_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = visit_begin_array(2, tag, context, ec); + if (more) + { + more = visit_begin_array(shape.size(), tag, context, ec); + for (auto it = shape.begin(); more && it != shape.end(); ++it) + { + visit_uint64(*it, semantic_tag::none, context, ec); + } + if (more) + { + more = visit_end_array(context, ec); + } + } + return more; + } + + virtual bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) + { + return visit_end_array(context, ec); + } + }; + + template + class basic_default_json_visitor : public basic_json_visitor + { + bool parse_more_; + std::error_code ec_; + public: + using typename basic_json_visitor::string_view_type; + + basic_default_json_visitor(bool accept_more = true, + std::error_code ec = std::error_code()) + : parse_more_(accept_more), ec_(ec) + { + } + private: + void visit_flush() override + { + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_key(const string_view_type&, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_string(const string_view_type&, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_byte_string(const byte_string_view&, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_uint64(uint64_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_int64(int64_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_half(uint16_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_double(double, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_bool(bool, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + }; + + template + class basic_json_tee_visitor : public basic_json_visitor + { + public: + using typename basic_json_visitor::char_type; + using typename basic_json_visitor::string_view_type; + private: + basic_json_visitor& destination0_; + basic_json_visitor& destination1_; + + // noncopyable and nonmoveable + basic_json_tee_visitor(const basic_json_tee_visitor&) = delete; + basic_json_tee_visitor& operator=(const basic_json_tee_visitor&) = delete; + public: + basic_json_tee_visitor(basic_json_visitor& destination0, + basic_json_visitor& destination1) + : destination0_(destination0), destination1_(destination1) + { + } + + basic_json_visitor& destination1() + { + return destination0_; + } + + basic_json_visitor& destination2() + { + return destination1_; + } + + private: + void visit_flush() override + { + destination0_.flush(); + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.begin_object(tag, context, ec); + bool more1 = destination1_.begin_object(tag, context, ec); + + return more0 && more1; + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.begin_object(length, tag, context, ec); + bool more1 = destination1_.begin_object(length, tag, context, ec); + + return more0 && more1; + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.end_object(context, ec); + bool more1 = destination1_.end_object(context, ec); + + return more0 && more1; + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.begin_array(tag, context, ec); + bool more1 = destination1_.begin_array(tag, context, ec); + + return more0 && more1; + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.begin_array(length, tag, context, ec); + bool more1 = destination1_.begin_array(length, tag, context, ec); + + return more0 && more1; + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.end_array(context, ec); + bool more1 = destination1_.end_array(context, ec); + + return more0 && more1; + } + + bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.key(name, context, ec); + bool more1 = destination1_.key(name, context, ec); + + return more0 && more1; + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.string_value(value, tag, context, ec); + bool more1 = destination1_.string_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.byte_string_value(b, tag, context, ec); + bool more1 = destination1_.byte_string_value(b, tag, context, ec); + + return more0 && more1; + } + + bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.uint64_value(value, tag, context, ec); + bool more1 = destination1_.uint64_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.int64_value(value, tag, context, ec); + bool more1 = destination1_.int64_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.half_value(value, tag, context, ec); + bool more1 = destination1_.half_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.double_value(value, tag, context, ec); + bool more1 = destination1_.double_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.bool_value(value, tag, context, ec); + bool more1 = destination1_.bool_value(value, tag, context, ec); + + return more0 && more1; + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool more0 = destination0_.null_value(tag, context, ec); + bool more1 = destination1_.null_value(tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(half_arg, s, tag, context, ec); + bool more1 = destination1_.typed_array(half_arg, s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.typed_array(s, tag, context, ec); + bool more1 = destination1_.typed_array(s, tag, context, ec); + + return more0 && more1; + } + + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.begin_multi_dim(shape, tag, context, ec); + bool more1 = destination1_.begin_multi_dim(shape, tag, context, ec); + + return more0 && more1; + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + bool more0 = destination0_.end_multi_dim(context, ec); + bool more1 = destination1_.end_multi_dim(context, ec); + + return more0 && more1; + } + + }; + + template + class basic_json_diagnostics_visitor : public basic_default_json_visitor + { + public: + using stream_type = std::basic_ostream; + using string_type = std::basic_string; + + private: + using supertype = basic_default_json_visitor; + using string_view_type = typename supertype::string_view_type; + + struct enabler {}; + + static constexpr CharT visit_begin_array_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','a','r','r','a','y', 0}; + static constexpr CharT visit_end_array_name[] = {'v','i','s','i','t','_','e','n','d','_','a','r','r','a','y', 0}; + static constexpr CharT visit_begin_object_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','o','b','j','e','c','t', 0}; + static constexpr CharT visit_end_object_name[] = {'v','i','s','i','t','_','e','n','d','_','o','b','j','e','c','t', 0}; + static constexpr CharT visit_key_name[] = {'v','i','s','i','t','_','k','e','y', 0}; + static constexpr CharT visit_string_name[] = {'v','i','s','i','t','_','s','t','r','i','n','g', 0}; + static constexpr CharT visit_byte_string_name[] = {'v','i','s','i','t','_','b','y','t','e','_','s','t','r','i','n','g', 0}; + static constexpr CharT visit_null_name[] = {'v','i','s','i','t','_','n','u','l','l', 0}; + static constexpr CharT visit_bool_name[] = {'v','i','s','i','t','_','b','o','o','l', 0}; + static constexpr CharT visit_uint64_name[] = {'v','i','s','i','t','_','u','i','n','t','6','4', 0}; + static constexpr CharT visit_int64_name[] = {'v','i','s','i','t','_','i','n','t','6','4', 0}; + static constexpr CharT visit_half_name[] = {'v','i','s','i','t','_','h','a','l','f', 0}; + static constexpr CharT visit_double_name[] = {'v','i','s','i','t','_','d','o','u','b','l','e', 0}; + + static constexpr CharT separator_ = ':'; + + stream_type& output_; + string_type indentation_; + long level_; + + public: + // If CharT is char, then enable the default constructor which binds to + // std::cout. + template + basic_json_diagnostics_visitor( + typename std::enable_if::value, U>::type = enabler{}) + : basic_json_diagnostics_visitor(std::cout) + { + } + + // If CharT is wchar_t, then enable the default constructor which binds + // to std::wcout. + template + basic_json_diagnostics_visitor( + typename std::enable_if::value, U>::type = enabler{}) + : basic_json_diagnostics_visitor(std::wcout) + { + } + + explicit basic_json_diagnostics_visitor( + stream_type& output, + string_type indentation = string_type()) + : output_(output), + indentation_(std::move(indentation)), + level_(0) + { + } + + private: + void indent() + { + for (long i=0; i constexpr C basic_json_diagnostics_visitor::visit_begin_array_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_end_array_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_begin_object_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_end_object_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_key_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_string_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_byte_string_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_null_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_bool_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_uint64_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_int64_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_half_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_double_name[]; + + using json_visitor = basic_json_visitor; + using wjson_visitor = basic_json_visitor; + + using json_tee_visitor = basic_json_tee_visitor; + using wjson_tee_visitor = basic_json_tee_visitor; + + using default_json_visitor = basic_default_json_visitor; + using wdefault_json_visitor = basic_default_json_visitor; + + using json_diagnostics_visitor = basic_json_diagnostics_visitor; + using wjson_diagnostics_visitor = basic_json_diagnostics_visitor; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +using basic_json_content_handler = basic_json_visitor; + +JSONCONS_DEPRECATED_MSG("Instead, use json_visitor") typedef json_visitor json_content_handler; +JSONCONS_DEPRECATED_MSG("Instead, use wjson_visitor") typedef wjson_visitor wjson_content_handler; + +template +using basic_default_json_content_handler = basic_default_json_visitor; + +JSONCONS_DEPRECATED_MSG("Instead, use default_json_visitor") typedef default_json_visitor default_json_content_handler; +JSONCONS_DEPRECATED_MSG("Instead, use default_wjson_visitor") typedef wdefault_json_visitor default_wjson_content_handler; +#endif + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/json_visitor2.hpp b/include/jsoncons/json_visitor2.hpp new file mode 100644 index 0000000..26096aa --- /dev/null +++ b/include/jsoncons/json_visitor2.hpp @@ -0,0 +1,2079 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_VISITOR2_HPP +#define JSONCONS_JSON_VISITOR2_HPP + +#include +#include + +namespace jsoncons { + + template > + class basic_json_visitor2_to_visitor_adaptor; + + template + class basic_json_visitor2 + { + template + friend class basic_json_visitor2_to_visitor_adaptor; + public: + using char_type = CharT; + using char_traits_type = std::char_traits; + + using string_view_type = jsoncons::basic_string_view; + + basic_json_visitor2(basic_json_visitor2&&) = default; + + basic_json_visitor2& operator=(basic_json_visitor2&&) = default; + + basic_json_visitor2() = default; + + virtual ~basic_json_visitor2() noexcept = default; + + void flush() + { + visit_flush(); + } + + bool begin_object(semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_object(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_object(std::size_t length, + semantic_tag tag=semantic_tag::none, + const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_begin_object(length, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_object(const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_end_object(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_array(semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_array(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_array(std::size_t length, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_array(length, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_array(const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_end_array(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool key(const string_view_type& name, const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_string(name, semantic_tag::none, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool null_value(semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_null(tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool bool_value(bool value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_bool(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool string_value(const string_view_type& value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_string(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool byte_string_value(const Source& b, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context(), + typename std::enable_if::value,int>::type = 0) + { + std::error_code ec; + bool more = visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool byte_string_value(const Source& b, + uint64_t ext_tag, + const ser_context& context=ser_context(), + typename std::enable_if::value,int>::type = 0) + { + std::error_code ec; + bool more = visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), ext_tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool uint64_value(uint64_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_uint64(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool int64_value(int64_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_int64(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool half_value(uint16_t value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_half(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool double_value(double value, + semantic_tag tag = semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_double(value, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_object(semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(tag, context, ec); + } + + bool begin_object(std::size_t length, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(length, tag, context, ec); + } + + bool end_object(const ser_context& context, std::error_code& ec) + { + return visit_end_object(context, ec); + } + + bool begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) + { + return visit_begin_array(tag, context, ec); + } + + bool begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) + { + return visit_begin_array(length, tag, context, ec); + } + + bool end_array(const ser_context& context, std::error_code& ec) + { + return visit_end_array(context, ec); + } + + bool key(const string_view_type& name, const ser_context& context, std::error_code& ec) + { + return visit_string(name, semantic_tag::none, context, ec); + } + + bool null_value(semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_null(tag, context, ec); + } + + bool bool_value(bool value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_bool(value, tag, context, ec); + } + + bool string_value(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_string(value, tag, context, ec); + } + + template + bool byte_string_value(const Source& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec, + typename std::enable_if::value,int>::type = 0) + { + return visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), tag, context, ec); + } + + template + bool byte_string_value(const Source& b, + uint64_t ext_tag, + const ser_context& context, + std::error_code& ec, + typename std::enable_if::value,int>::type = 0) + { + return visit_byte_string(byte_string_view(reinterpret_cast(b.data()),b.size()), ext_tag, context, ec); + } + + bool uint64_value(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_uint64(value, tag, context, ec); + } + + bool int64_value(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_int64(value, tag, context, ec); + } + + bool half_value(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_half(value, tag, context, ec); + } + + bool double_value(double value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_double(value, tag, context, ec); + } + + template + bool typed_array(const jsoncons::span& data, + semantic_tag tag=semantic_tag::none, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_typed_array(data, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + template + bool typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_typed_array(data, tag, context, ec); + } + + bool typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag = semantic_tag::none, + const ser_context& context = ser_context()) + { + std::error_code ec; + bool more = visit_typed_array(half_arg, s, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_typed_array(half_arg, s, tag, context, ec); + } + + bool begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag = semantic_tag::multi_dim_row_major, + const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_begin_multi_dim(shape, tag, context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_multi_dim(shape, tag, context, ec); + } + + bool end_multi_dim(const ser_context& context=ser_context()) + { + std::error_code ec; + bool more = visit_end_multi_dim(context, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, context.line(), context.column())); + } + return more; + } + + bool end_multi_dim(const ser_context& context, + std::error_code& ec) + { + return visit_end_multi_dim(context, ec); + } + + private: + + virtual void visit_flush() = 0; + + virtual bool visit_begin_object(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_object(std::size_t /*length*/, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_object(tag, context, ec); + } + + virtual bool visit_end_object(const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_array(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_begin_array(std::size_t /*length*/, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_begin_array(tag, context, ec); + } + + virtual bool visit_end_array(const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_null(semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_bool(bool value, + semantic_tag tag, + const ser_context& context, + std::error_code&) = 0; + + virtual bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_byte_string(const byte_string_view& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_byte_string(const byte_string_view& value, + uint64_t /*ext_tag*/, + const ser_context& context, + std::error_code& ec) + { + return visit_byte_string(value, semantic_tag::none, context, ec); + } + + virtual bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + return visit_double(binary::decode_half(value), + tag, + context, + ec); + } + + virtual bool visit_double(double value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) = 0; + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = uint64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag, context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = half_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = begin_array(s.size(), tag,context, ec); + for (auto p = s.begin(); more && p != s.end(); ++p) + { + more = double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = end_array(context, ec); + } + return more; + } + + virtual bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) + { + bool more = visit_begin_array(2, tag, context, ec); + if (more) + { + more = visit_begin_array(shape.size(), tag, context, ec); + for (auto it = shape.begin(); more && it != shape.end(); ++it) + { + visit_uint64(*it, semantic_tag::none, context, ec); + } + if (more) + { + more = visit_end_array(context, ec); + } + } + return more; + } + + virtual bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) + { + return visit_end_array(context, ec); + } + }; + + template + class basic_json_visitor2_to_visitor_adaptor : public basic_json_visitor2 + { + public: + using typename basic_json_visitor2::char_type; + using typename basic_json_visitor2::string_view_type; + private: + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + using string_type = std::basic_string,char_allocator_type>; + + enum class container_t {root, array, object}; + enum class target_t {destination, buffer}; + + struct level + { + private: + target_t state_; + container_t type_; + int even_odd_; + std::size_t count_; + public: + + level(target_t state, container_t type) noexcept + : state_(state), type_(type), even_odd_(type == container_t::object ? 0 : 1), count_(0) + { + } + + void advance() + { + if (!is_key()) + { + ++count_; + } + if (is_object()) + { + even_odd_ = !even_odd_; + } + } + + bool is_key() const + { + return even_odd_ == 0; + } + + bool is_object() const + { + return type_ == container_t::object; + } + + target_t target() const + { + return state_; + } + + std::size_t count() const + { + return count_; + } + }; + using level_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + basic_default_json_visitor default_visitor_; + basic_json_visitor* destination_; + string_type key_; + string_type key_buffer_; + std::vector level_stack_; + + const std::basic_string null_constant = {'n','u','l','l'}; + const std::basic_string true_constant = { 't','r','u','e' }; + const std::basic_string false_constant = { 'f', 'a', 'l', 's', 'e' }; + + // noncopyable and nonmoveable + basic_json_visitor2_to_visitor_adaptor(const basic_json_visitor2_to_visitor_adaptor&) = delete; + basic_json_visitor2_to_visitor_adaptor& operator=(const basic_json_visitor2_to_visitor_adaptor&) = delete; + public: + explicit basic_json_visitor2_to_visitor_adaptor(const Allocator& alloc = Allocator()) + : default_visitor_(), destination_(std::addressof(default_visitor_)), + key_(alloc), key_buffer_(alloc), level_stack_(alloc) + { + level_stack_.emplace_back(target_t::destination,container_t::root); // root + } + + explicit basic_json_visitor2_to_visitor_adaptor(basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) + : destination_(std::addressof(visitor)), + key_(alloc), key_buffer_(alloc), level_stack_(alloc) + { + level_stack_.emplace_back(target_t::destination,container_t::root); // root + } + + void reset() + { + key_.clear(); + key_buffer_.clear(); + level_stack_.clear(); + level_stack_.emplace_back(target_t::destination,container_t::root); // root + } + + basic_json_visitor& destination() + { + return *destination_; + } + + void destination(basic_json_visitor& dest) + { + destination_ = std::addressof(dest); + } + + private: + void visit_flush() override + { + destination_->flush(); + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + if (level_stack_.back().is_key()) + { + if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::object); + key_buffer_.push_back('{'); + return true; + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + level_stack_.emplace_back(target_t::buffer, container_t::object); + key_buffer_.push_back('{'); + return true; + default: + level_stack_.emplace_back(target_t::destination, container_t::object); + return destination_->begin_object(tag, context, ec); + } + } + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + if (level_stack_.back().is_key()) + { + if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::object); + key_buffer_.push_back('{'); + return true; + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::object); + key_buffer_.push_back('{'); + return true; + default: + level_stack_.emplace_back(target_t::destination, container_t::object); + return destination_->begin_object(length, tag, context, ec); + } + } + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + bool retval = true; + switch (level_stack_.back().target()) + { + case target_t::buffer: + key_buffer_.push_back('}'); + JSONCONS_ASSERT(level_stack_.size() > 1); + level_stack_.pop_back(); + + if (level_stack_.back().target() == target_t::destination) + { + retval = destination_->key(key_buffer_,context, ec); + key_buffer_.clear(); + } + else if (level_stack_.back().is_key()) + { + key_buffer_.push_back(':'); + } + level_stack_.back().advance(); + break; + default: + JSONCONS_ASSERT(level_stack_.size() > 1); + level_stack_.pop_back(); + level_stack_.back().advance(); + retval = destination_->end_object(context, ec); + break; + } + return retval; + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + if (level_stack_.back().is_key()) + { + if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::array); + key_buffer_.push_back('['); + return true; + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::array); + key_buffer_.push_back('['); + return true; + default: + level_stack_.emplace_back(target_t::destination, container_t::array); + return destination_->begin_array(tag, context, ec); + } + } + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + if (level_stack_.back().is_key()) + { + if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::array); + key_buffer_.push_back('['); + return true; + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + level_stack_.emplace_back(target_t::buffer, container_t::array); + key_buffer_.push_back('['); + return true; + default: + level_stack_.emplace_back(target_t::destination, container_t::array); + return destination_->begin_array(length, tag, context, ec); + } + } + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + bool retval = true; + switch (level_stack_.back().target()) + { + case target_t::buffer: + key_buffer_.push_back(']'); + JSONCONS_ASSERT(level_stack_.size() > 1); + level_stack_.pop_back(); + if (level_stack_.back().target() == target_t::destination) + { + retval = destination_->key(key_buffer_, context, ec); + key_buffer_.clear(); + } + else if (level_stack_.back().is_key()) + { + key_buffer_.push_back(':'); + } + level_stack_.back().advance(); + break; + default: + JSONCONS_ASSERT(level_stack_.size() > 1); + level_stack_.pop_back(); + level_stack_.back().advance(); + retval = destination_->end_array(context, ec); + break; + } + return retval; + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), value.begin(), value.end()); + key_buffer_.push_back('\"'); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(value, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), value.begin(), value.end()); + key_buffer_.push_back('\"'); + retval = true; + break; + default: + retval = destination_->string_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_byte_string(const byte_string_view& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + switch (tag) + { + case semantic_tag::base64: + encode_base64(value.begin(), value.end(), key_); + break; + case semantic_tag::base16: + encode_base16(value.begin(), value.end(),key_); + break; + default: + encode_base64url(value.begin(), value.end(),key_); + break; + } + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back('\"'); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back('\"'); + retval = true; + break; + default: + retval = destination_->byte_string_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_byte_string(const byte_string_view& value, + uint64_t ext_tag, + const ser_context& context, + std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + encode_base64url(value.begin(), value.end(),key_); + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back('\"'); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.push_back('\"'); + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back('\"'); + retval = true; + break; + default: + retval = destination_->byte_string_value(value, ext_tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + jsoncons::detail::from_integer(value,key_); + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->uint64_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + jsoncons::detail::from_integer(value,key_); + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->int64_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + jsoncons::string_sink sink(key_); + jsoncons::detail::write_double f{float_chars_format::general,0}; + double x = binary::decode_half(value); + f(x, sink); + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->half_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_.clear(); + string_sink sink(key_); + jsoncons::detail::write_double f{float_chars_format::general,0}; + f(value, sink); + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->double_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_ = value ? true_constant : false_constant; + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->bool_value(value, tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + bool retval = true; + + if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) + { + key_ = null_constant; + } + + if (level_stack_.back().is_key()) + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + key_buffer_.push_back(':'); + retval = true; + break; + default: + retval = destination_->key(key_, context, ec); + break; + } + } + else + { + switch (level_stack_.back().target()) + { + case target_t::buffer: + if (!level_stack_.back().is_object() && level_stack_.back().count() > 0) + { + key_buffer_.push_back(','); + } + key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end()); + retval = true; + break; + default: + retval = destination_->null_value(tag, context, ec); + break; + } + } + + level_stack_.back().advance(); + return retval; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(half_arg_t, + const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(half_arg,s,tag,context,ec); + } + else + { + return destination_->typed_array(half_arg, s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + bool is_key = level_stack_.back().is_key(); + level_stack_.back().advance(); + + if (is_key || level_stack_.back().target() == target_t::buffer) + { + return basic_json_visitor2::visit_typed_array(s,tag,context,ec); + } + else + { + return destination_->typed_array(s, tag, context, ec); + } + } + }; + + template + class basic_default_json_visitor2 : public basic_json_visitor2 + { + bool parse_more_; + std::error_code ec_; + public: + using typename basic_json_visitor2::string_view_type; + + basic_default_json_visitor2(bool accept_more = true, + std::error_code ec = std::error_code()) + : parse_more_(accept_more), ec_(ec) + { + } + private: + void visit_flush() override + { + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_begin_object(std::size_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_begin_array(std::size_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_string(const string_view_type&, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_byte_string(const byte_string_view&, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_uint64(uint64_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_int64(int64_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_half(uint16_t, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_double(double, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + + bool visit_bool(bool, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (ec_) + { + ec = ec_; + } + return parse_more_; + } + }; + + // basic_json_visitor_to_visitor2_adaptor + + template + class basic_json_visitor_to_visitor2_adaptor : public basic_json_visitor + { + public: + using typename basic_json_visitor::char_type; + using typename basic_json_visitor::string_view_type; + private: + basic_json_visitor2& destination_; + + // noncopyable and nonmoveable + basic_json_visitor_to_visitor2_adaptor(const basic_json_visitor_to_visitor2_adaptor&) = delete; + basic_json_visitor_to_visitor2_adaptor& operator=(const basic_json_visitor_to_visitor2_adaptor&) = delete; + public: + basic_json_visitor_to_visitor2_adaptor(basic_json_visitor2& visitor) + : destination_(visitor) + { + } + + basic_json_visitor2& destination() + { + return destination_; + } + + private: + void visit_flush() override + { + destination_.flush(); + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.begin_object(tag, context, ec); + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.begin_object(length, tag, context, ec); + } + + bool visit_end_object(const ser_context& context, std::error_code& ec) override + { + return destination_.end_object(context, ec); + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.begin_array(tag, context, ec); + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.begin_array(length, tag, context, ec); + } + + bool visit_end_array(const ser_context& context, std::error_code& ec) override + { + return destination_.end_array(context, ec); + } + + bool visit_key(const string_view_type& name, + const ser_context& context, + std::error_code& ec) override + { + return destination_.visit_string(name, context, ec); + } + + bool visit_string(const string_view_type& value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_.string_value(value, tag, context, ec); + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + return destination_.byte_string_value(b, tag, context, ec); + } + + bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.uint64_value(value, tag, context, ec); + } + + bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.int64_value(value, tag, context, ec); + } + + bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.half_value(value, tag, context, ec); + } + + bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.double_value(value, tag, context, ec); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.bool_value(value, tag, context, ec); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + return destination_.null_value(tag, context, ec); + } + }; + + class diagnostics_visitor2 : public basic_default_json_visitor2 + { + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_object" << std::endl; + return true; + } + + bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_object " << length << std::endl; + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + std::cout << "visit_end_object" << std::endl; + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_array" << std::endl; + return true; + } + + bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_array " << length << std::endl; + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + std::cout << "visit_end_array" << std::endl; + return true; + } + + bool visit_string(const string_view_type& s, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_string " << s << std::endl; + return true; + } + bool visit_int64(int64_t val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_int64 " << val << std::endl; + return true; + } + bool visit_uint64(uint64_t val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_uint64 " << val << std::endl; + return true; + } + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_bool " << val << std::endl; + return true; + } + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_null " << std::endl; + return true; + } + + bool visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + std::cout << "visit_typed_array uint16_t " << tag << std::endl; + for (auto val : s) + { + std::cout << val << "" << std::endl; + } + std::cout << "" << std::endl; + return true; + } + + bool visit_typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + std::cout << "visit_typed_array half_arg_t uint16_t " << tag << "" << std::endl; + for (auto val : s) + { + std::cout << val << "" << std::endl; + } + std::cout << "" << std::endl; + return true; + } + }; + + using json_visitor2 = basic_json_visitor2; + using default_json_visitor2 = basic_default_json_visitor2; + using json_visitor2_to_visitor_adaptor = basic_json_visitor2_to_visitor_adaptor; + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/more_type_traits.hpp b/include/jsoncons/more_type_traits.hpp new file mode 100644 index 0000000..8e6b6c2 --- /dev/null +++ b/include/jsoncons/more_type_traits.hpp @@ -0,0 +1,874 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MORE_TYPE_TRAITS_HPP +#define JSONCONS_MORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include // std::enable_if, std::true_type +#include +#include // std::iterator_traits +#include +#include // std::array +#include // std::byte +#include // std::declval +#include // CHAR_BIT +#include + +namespace jsoncons { +namespace type_traits { + + // is_char8 + template + struct is_char8 : std::false_type {}; + + template + struct is_char8::value && + !std::is_same::value && + sizeof(uint8_t) == sizeof(CharT)>::type> : std::true_type {}; + + // is_char16 + template + struct is_char16 : std::false_type {}; + + template + struct is_char16::value && + !std::is_same::value && + (std::is_same::value || sizeof(uint16_t) == sizeof(CharT))>::type> : std::true_type {}; + + // is_char32 + template + struct is_char32 : std::false_type {}; + + template + struct is_char32::value && + !std::is_same::value && + (std::is_same::value || (!std::is_same::value && sizeof(uint32_t) == sizeof(CharT)))>::type> : std::true_type {}; + + // is_int128 + + template + struct is_int128_type : std::false_type {}; + +#if defined(JSONCONS_HAS_INT128) + template + struct is_int128_type::value>::type> : std::true_type {}; +#endif + + // is_unsigned_integer + + template + struct is_uint128_type : std::false_type {}; + +#if defined (JSONCONS_HAS_INT128) + template + struct is_uint128_type::value>::type> : std::true_type {}; +#endif + + template + class integer_limits + { + public: + static constexpr bool is_specialized = false; + }; + + template + class integer_limits::value && !std::is_same::value>::type> + { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = std::numeric_limits::is_signed; + static constexpr int digits = std::numeric_limits::digits; + static constexpr std::size_t buffer_size = static_cast(sizeof(T)*CHAR_BIT*0.302) + 3; + + static constexpr T(max)() noexcept + { + return (std::numeric_limits::max)(); + } + static constexpr T(min)() noexcept + { + return (std::numeric_limits::min)(); + } + static constexpr T lowest() noexcept + { + return std::numeric_limits::lowest(); + } + }; + + template + class integer_limits::value && is_int128_type::value>::type> + { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr int digits = sizeof(T)*CHAR_BIT - 1; + static constexpr std::size_t buffer_size = (sizeof(T)*CHAR_BIT*0.302) + 3; + + static constexpr T(max)() noexcept + { + return (((((T)1 << (digits - 1)) - 1) << 1) + 1); + } + static constexpr T(min)() noexcept + { + return -(max)() - 1; + } + static constexpr T lowest() noexcept + { + return (min)(); + } + }; + + template + class integer_limits::value && is_uint128_type::value>::type> + { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = false; + static constexpr int digits = sizeof(T)*CHAR_BIT; + + static constexpr T(max)() noexcept + { + return T(T(~0)); + } + static constexpr T(min)() noexcept + { + return 0; + } + static constexpr T lowest() noexcept + { + return std::numeric_limits::lowest(); + } + }; + + #ifndef JSONCONS_HAS_VOID_T + // follows https://en.cppreference.com/w/cpp/types/void_t + template struct make_void { typedef void type;}; + template using void_t = typename make_void::type; + #else + using void_t = std::void_t; + #endif + + // follows http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf + + // detector + + // primary template handles all types not supporting the archetypal Op + template< + class Default, + class, // always void; supplied externally + template class Op, + class... Args + > + struct detector + { + constexpr static auto value = false; + using type = Default; + }; + + // specialization recognizes and handles only types supporting Op + template< + class Default, + template class Op, + class... Args + > + struct detector>, Op, Args...> + { + constexpr static auto value = true; + using type = Op; + }; + + // is_detected, is_detected_t + + template< template class Op, class... Args > + using + is_detected = detector; + + template< template class Op, class... Args > + using + is_detected_t = typename is_detected::type; + + // detected_or, detected_or_t + + template< class Default, template class Op, class... Args > + using + detected_or = detector; + + template< class Default, template class Op, class... Args > + using + detected_or_t = typename detected_or::type; + + // is_detected_exact + + template< class Expected, template class Op, class... Args > + using + is_detected_exact = std::is_same< Expected, is_detected_t >; + + // is_detected_convertible + + template< class To, template class Op, class... Args > + using + is_detected_convertible = std::is_convertible< is_detected_t, To >; + + template + struct is_stateless + : public std::integral_constant::value && + std::is_empty::value)> + {}; + + // to_plain_pointer + + template inline + typename std::pointer_traits::element_type* to_plain_pointer(Pointer ptr) + { + return (std::addressof(*ptr)); + } + + template inline + T * to_plain_pointer(T * ptr) + { + return (ptr); + } + + // is_std_byte + + template + struct is_std_byte : std::false_type {}; +#if defined(JSONCONS_HAS_STD_BYTE) + template + struct is_std_byte::value + >::type> : std::true_type {}; +#endif + // is_byte + + template + struct is_byte : std::false_type {}; + + template + struct is_byte::value || + std::is_same::value || + std::is_same::value || + is_std_byte::value + >::type> : std::true_type {}; + + // is_character + + template + struct is_character : std::false_type {}; + + template + struct is_character::value || + std::is_same::value + >::type> : std::true_type {}; + + // is_narrow_character + + template + struct is_narrow_character : std::false_type {}; + + template + struct is_narrow_character::value && (sizeof(T) == sizeof(char)) + >::type> : std::true_type {}; + + // is_wide_character + + template + struct is_wide_character : std::false_type {}; + + template + struct is_wide_character::value && (sizeof(T) != sizeof(char)) + >::type> : std::true_type {}; + + // From boost + namespace ut_detail { + + template + struct is_cstring_impl : public std::false_type {}; + + template + struct is_cstring_impl : public is_cstring_impl {}; + + template + struct is_cstring_impl : public is_cstring_impl {}; + + template<> + struct is_cstring_impl : public std::true_type {}; + + template<> + struct is_cstring_impl : public std::true_type {}; + + } // namespace ut_detail + + template + struct is_cstring : public ut_detail::is_cstring_impl::type> {}; + + // is_bool + + template + struct is_bool : std::false_type {}; + + template + struct is_bool::value + >::type> : std::true_type {}; + + // is_u8_u16_u32_or_u64 + + template + struct is_u8_u16_u32_or_u64 : std::false_type {}; + + template + struct is_u8_u16_u32_or_u64::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type> : std::true_type {}; + + // is_int + + template + struct is_i8_i16_i32_or_i64 : std::false_type {}; + + template + struct is_i8_i16_i32_or_i64::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type> : std::true_type {}; + + // is_float_or_double + + template + struct is_float_or_double : std::false_type {}; + + template + struct is_float_or_double::value || + std::is_same::value + >::type> : std::true_type {}; + + // make_unsigned + template + struct make_unsigned_impl {using type = typename std::make_unsigned::type;}; + + #if defined(JSONCONS_HAS_INT128) + template <> + struct make_unsigned_impl {using type = uint128_type;}; + template <> + struct make_unsigned_impl {using type = uint128_type;}; + #endif + + template + struct make_unsigned + : make_unsigned_impl::type> + {}; + + // is_integer + + template + struct is_integer : std::false_type {}; + + template + struct is_integer::is_specialized>::type> : std::true_type {}; + + // is_signed_integer + + template + struct is_signed_integer : std::false_type {}; + + template + struct is_signed_integer::is_specialized && + integer_limits::is_signed>::type> : std::true_type {}; + + // is_unsigned_integer + + template + struct is_unsigned_integer : std::false_type {}; + + template + struct is_unsigned_integer::is_specialized && + !integer_limits::is_signed>::type> : std::true_type {}; + + // is_primitive + + template + struct is_primitive : std::false_type {}; + + template + struct is_primitive::value || + is_bool::value || + std::is_floating_point::value + >::type> : std::true_type {}; + + // Containers + + template + using + container_npos_t = decltype(Container::npos); + + template + using + container_allocator_type_t = typename Container::allocator_type; + + template + using + container_mapped_type_t = typename Container::mapped_type; + + template + using + container_key_type_t = typename Container::key_type; + + template + using + container_value_type_t = typename std::iterator_traits::value_type; + + template + using + container_char_traits_t = typename Container::traits_type::char_type; + + template + using + container_push_back_t = decltype(std::declval().push_back(std::declval())); + + template + using + container_push_front_t = decltype(std::declval().push_front(std::declval())); + + template + using + container_insert_t = decltype(std::declval().insert(std::declval())); + + template + using + container_reserve_t = decltype(std::declval().reserve(typename Container::size_type())); + + template + using + container_data_t = decltype(std::declval().data()); + + template + using + container_size_t = decltype(std::declval().size()); + + // is_string_or_string_view + + template + struct is_string_or_string_view : std::false_type {}; + + template + struct is_string_or_string_view::value && + is_detected_exact::value && + is_detected::value + >::type> : std::true_type {}; + + // is_basic_string + + template + struct is_basic_string : std::false_type {}; + + template + struct is_basic_string::value && + is_detected::value + >::type> : std::true_type {}; + + // is_basic_string_view + + template + struct is_basic_string_view : std::false_type {}; + + template + struct is_basic_string_view::value && + !is_detected::value + >::type> : std::true_type {}; + + // is_map_like + + template + struct is_map_like : std::false_type {}; + + template + struct is_map_like::value && + is_detected::value && + is_detected::value && + is_detected::value + >::type> + : std::true_type {}; + + // is_std_array + template + struct is_std_array : std::false_type {}; + + template + struct is_std_array> : std::true_type {}; + + // is_list_like + + template + struct is_list_like : std::false_type {}; + + template + struct is_list_like::value && + is_detected::value && + !is_std_array::value && + !is_detected_exact::value && + !is_map_like::value + >::type> + : std::true_type {}; + + // is_constructible_from_const_pointer_and_size + + template + struct is_constructible_from_const_pointer_and_size : std::false_type {}; + + template + struct is_constructible_from_const_pointer_and_size::value + >::type> + : std::true_type {}; + + // has_reserve + + template + using + has_reserve = is_detected; + + // is_back_insertable + + template + using + is_back_insertable = is_detected; + + // is_front_insertable + + template + using + is_front_insertable = is_detected; + + // is_insertable + + template + using + is_insertable = is_detected; + + // has_data, has_data_exact + + template + using + has_data = is_detected; + + template + using + has_data_exact = is_detected_exact; + + // has_size + + template + using + has_size = is_detected; + + // has_data_and_size + + template + struct has_data_and_size + { + static constexpr bool value = has_data::value && has_size::value; + }; + + // is_byte_sequence + + template + struct is_byte_sequence : std::false_type {}; + + template + struct is_byte_sequence::value && + has_size::value && + is_byte::value + >::type> : std::true_type {}; + + // is_char_sequence + + template + struct is_char_sequence : std::false_type {}; + + template + struct is_char_sequence::value && + has_size::value && + is_character::value + >::type> : std::true_type {}; + + // is_sequence_of + + template + struct is_sequence_of : std::false_type {}; + + template + struct is_sequence_of::value && + has_size::value && + std::is_same::value + >::type> : std::true_type {}; + + // is_back_insertable_byte_container + + template + struct is_back_insertable_byte_container : std::false_type {}; + + template + struct is_back_insertable_byte_container::value && + is_byte::value + >::type> : std::true_type {}; + + // is_back_insertable_char_container + + template + struct is_back_insertable_char_container : std::false_type {}; + + template + struct is_back_insertable_char_container::value && + is_character::value + >::type> : std::true_type {}; + + // is_back_insertable_container_of + + template + struct is_back_insertable_container_of : std::false_type {}; + + template + struct is_back_insertable_container_of::value && + std::is_same::value + >::type> : std::true_type {}; + + // is_c_array + + template + struct is_c_array : std::false_type {}; + + template + struct is_c_array : std::true_type {}; + + template + struct is_c_array : std::true_type {}; + +namespace impl { + + template + struct is_typed_array : std::false_type {}; + + template + struct is_typed_array + < + T, + typename std::enable_if::value && + (std::is_same::type,uint8_t>::value || + std::is_same::type,uint16_t>::value || + std::is_same::type,uint32_t>::value || + std::is_same::type,uint64_t>::value || + std::is_same::type,int8_t>::value || + std::is_same::type,int16_t>::value || + std::is_same::type,int32_t>::value || + std::is_same::type,int64_t>::value || + std::is_same::type,float_t>::value || + std::is_same::type,double_t>::value)>::type + > : std::true_type{}; + +} // namespace impl + + template + using is_typed_array = impl::is_typed_array::type>; + + // is_compatible_element + + template + struct is_compatible_element : std::false_type {}; + + template + struct is_compatible_element + < + Container, Element, + typename std::enable_if::value>::type> + : std::is_convertible< typename std::remove_pointer().data() )>::type(*)[], Element(*)[]> + {}; + + template + using + construct_from_string_t = decltype(T(std::string{})); + + + template + using + is_constructible_from_string = is_detected; + + template + using + construct_from_data_size_t = decltype(T(static_cast(nullptr),Size{})); + + + template + using + is_constructible_from_data_size = is_detected; + + // is_unary_function_object + // is_unary_function_object_exact + + template + using + unary_function_object_t = decltype(std::declval()(std::declval())); + + template + using + is_unary_function_object = is_detected; + + template + using + is_unary_function_object_exact = is_detected_exact; + + // is_binary_function_object + // is_binary_function_object_exact + + template + using + binary_function_object_t = decltype(std::declval()(std::declval(),std::declval())); + + template + using + is_binary_function_object = is_detected; + + template + using + is_binary_function_object_exact = is_detected_exact; + + template + struct is_convertible_to_string_view : std::false_type {}; + + template + struct is_convertible_to_string_view::value || + is_cstring::value + >::type> : std::true_type {}; + + #if defined(JSONCONS_HAS_2017) + template + using is_nothrow_swappable = std::is_nothrow_swappable; + #else + template + struct is_nothrow_swappable { + static const bool value = noexcept(swap(std::declval(), std::declval())); + }; + #endif + + #if defined(JSONCONS_HAS_2014) + template + using alignment_of = std::alignment_of; + + template< class T, T... Ints > + using integer_sequence = std::integer_sequence; + + template + using index_sequence = std::index_sequence; + + template + using make_integer_sequence = std::make_integer_sequence; + + template + using make_index_sequence = std::make_index_sequence; + + template + using index_sequence_for = std::index_sequence_for; + + #else + template + struct alignment_of + : std::integral_constant::type)> {}; + + template + class integer_sequence + { + public: + using value_type = T; + static_assert(std::is_integral::value, "not integral type"); + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } + }; + + template + using index_sequence = integer_sequence; + namespace detail_ { + template + struct IntSeqImpl { + using TValue = T; + static_assert(std::is_integral::value, "not integral type"); + static_assert(Begin >= 0 && Begin < End, "unexpected argument (Begin<0 || Begin<=End)"); + + template + struct IntSeqCombiner; + + template + struct IntSeqCombiner, integer_sequence> { + using TResult = integer_sequence; + }; + + using TResult = + typename IntSeqCombiner::TResult, + typename IntSeqImpl::TResult>::TResult; + }; + + template + struct IntSeqImpl { + using TValue = T; + static_assert(std::is_integral::value, "not integral type"); + static_assert(Begin >= 0, "unexpected argument (Begin<0)"); + using TResult = integer_sequence; + }; + + template + struct IntSeqImpl { + using TValue = T; + static_assert(std::is_integral::value, "not integral type"); + static_assert(Begin >= 0, "unexpected argument (Begin<0)"); + using TResult = integer_sequence; + }; + } // namespace detail_ + + template + using make_integer_sequence = typename detail_::IntSeqImpl::TResult; + + template + using make_index_sequence = make_integer_sequence; + + template + using index_sequence_for = make_index_sequence; + + + #endif + +} // type_traits +} // jsoncons + +#endif diff --git a/include/jsoncons/pretty_print.hpp b/include/jsoncons/pretty_print.hpp new file mode 100644 index 0000000..599c1ce --- /dev/null +++ b/include/jsoncons/pretty_print.hpp @@ -0,0 +1,89 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_PRETTY_PRINT_HPP +#define JSONCONS_PRETTY_PRINT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +template +class json_printable +{ +public: + using char_type = typename Json::char_type; + + json_printable(const Json& j, indenting line_indent) + : j_(&j), indenting_(line_indent) + { + } + + json_printable(const Json& j, + const basic_json_encode_options& options, + indenting line_indent) + : j_(&j), options_(options), indenting_(line_indent) + { + } + + void dump(std::basic_ostream& os) const + { + j_->dump(os, options_, indenting_); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const json_printable& pr) + { + pr.dump(os); + return os; + } + + const Json *j_; + basic_json_encode_options options_; + indenting indenting_; +private: + json_printable(); +}; + +template +json_printable print(const Json& j) +{ + return json_printable(j, indenting::no_indent); +} + +template +json_printable print(const Json& j, + const basic_json_encode_options& options) +{ + return json_printable(j, options, indenting::no_indent); +} + +template +json_printable pretty_print(const Json& j) +{ + return json_printable(j, indenting::indent); +} + +template +json_printable pretty_print(const Json& j, + const basic_json_encode_options& options) +{ + return json_printable(j, options, indenting::indent); +} + +} + +#endif diff --git a/include/jsoncons/ser_context.hpp b/include/jsoncons/ser_context.hpp new file mode 100644 index 0000000..f5b9d14 --- /dev/null +++ b/include/jsoncons/ser_context.hpp @@ -0,0 +1,57 @@ +/// Copyright 2013-2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SER_CONTEXT_HPP +#define JSONCONS_SER_CONTEXT_HPP + +namespace jsoncons { + +class ser_context +{ +public: + virtual ~ser_context() noexcept = default; + + virtual size_t line() const + { + return 0; + } + + virtual size_t column() const + { + return 0; + } + + virtual size_t position() const + { + return 0; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use line()") + std::size_t line_number() const + { + return line(); + } + + JSONCONS_DEPRECATED_MSG("Instead, use column()") + std::size_t column_number() const + { + return column(); + } +#endif +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_ser_context; + +JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context parsing_context; +JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context serializing_context; +JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_parsing_context; +JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_serializing_context; +#endif + +} +#endif diff --git a/include/jsoncons/sink.hpp b/include/jsoncons/sink.hpp new file mode 100644 index 0000000..84771a3 --- /dev/null +++ b/include/jsoncons/sink.hpp @@ -0,0 +1,289 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SINK_HPP +#define JSONCONS_SINK_HPP + +#include +#include +#include +#include +#include +#include +#include // std::addressof +#include // std::memcpy +#include +#include + +namespace jsoncons { + + // stream_sink + + template + class stream_sink + { + public: + using value_type = CharT; + using container_type = std::basic_ostream; + + private: + static constexpr size_t default_buffer_length = 16384; + + std::basic_ostream* stream_ptr_; + std::vector buffer_; + CharT * begin_buffer_; + const CharT* end_buffer_; + CharT* p_; + + // Noncopyable + stream_sink(const stream_sink&) = delete; + stream_sink& operator=(const stream_sink&) = delete; + + public: + stream_sink(stream_sink&&) = default; + + stream_sink(std::basic_ostream& os) + : stream_ptr_(std::addressof(os)), buffer_(default_buffer_length), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_) + { + } + stream_sink(std::basic_ostream& os, std::size_t buflen) + : stream_ptr_(std::addressof(os)), buffer_(buflen), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_) + { + } + ~stream_sink() noexcept + { + stream_ptr_->write(begin_buffer_, buffer_length()); + stream_ptr_->flush(); + } + + // Movable + stream_sink& operator=(stream_sink&&) = default; + + void flush() + { + stream_ptr_->write(begin_buffer_, buffer_length()); + stream_ptr_->flush(); + p_ = buffer_.data(); + } + + void append(const CharT* s, std::size_t length) + { + std::size_t diff = end_buffer_ - p_; + if (diff >= length) + { + std::memcpy(p_, s, length*sizeof(CharT)); + p_ += length; + } + else + { + stream_ptr_->write(begin_buffer_, buffer_length()); + stream_ptr_->write(s,length); + p_ = begin_buffer_; + } + } + + void push_back(CharT ch) + { + if (p_ < end_buffer_) + { + *p_++ = ch; + } + else + { + stream_ptr_->write(begin_buffer_, buffer_length()); + p_ = begin_buffer_; + push_back(ch); + } + } + private: + + std::size_t buffer_length() const + { + return p_ - begin_buffer_; + } + }; + + // binary_stream_sink + + class binary_stream_sink + { + public: + typedef uint8_t value_type; + using container_type = std::basic_ostream; + private: + static constexpr size_t default_buffer_length = 16384; + + std::basic_ostream* stream_ptr_; + std::vector buffer_; + uint8_t * begin_buffer_; + const uint8_t* end_buffer_; + uint8_t* p_; + + // Noncopyable + binary_stream_sink(const binary_stream_sink&) = delete; + binary_stream_sink& operator=(const binary_stream_sink&) = delete; + + public: + binary_stream_sink(binary_stream_sink&&) = default; + + binary_stream_sink(std::basic_ostream& os) + : stream_ptr_(std::addressof(os)), + buffer_(default_buffer_length), + begin_buffer_(buffer_.data()), + end_buffer_(begin_buffer_+buffer_.size()), + p_(begin_buffer_) + { + } + binary_stream_sink(std::basic_ostream& os, std::size_t buflen) + : stream_ptr_(std::addressof(os)), + buffer_(buflen), + begin_buffer_(buffer_.data()), + end_buffer_(begin_buffer_+buffer_.size()), + p_(begin_buffer_) + { + } + ~binary_stream_sink() noexcept + { + stream_ptr_->write((char*)begin_buffer_, buffer_length()); + stream_ptr_->flush(); + } + + binary_stream_sink& operator=(binary_stream_sink&&) = default; + + void flush() + { + stream_ptr_->write((char*)begin_buffer_, buffer_length()); + p_ = buffer_.data(); + } + + void append(const uint8_t* s, std::size_t length) + { + std::size_t diff = end_buffer_ - p_; + if (diff >= length) + { + std::memcpy(p_, s, length*sizeof(uint8_t)); + p_ += length; + } + else + { + stream_ptr_->write((char*)begin_buffer_, buffer_length()); + stream_ptr_->write((const char*)s,length); + p_ = begin_buffer_; + } + } + + void push_back(uint8_t ch) + { + if (p_ < end_buffer_) + { + *p_++ = ch; + } + else + { + stream_ptr_->write((char*)begin_buffer_, buffer_length()); + p_ = begin_buffer_; + push_back(ch); + } + } + private: + + std::size_t buffer_length() const + { + return p_ - begin_buffer_; + } + }; + + // string_sink + + template + class string_sink + { + public: + using value_type = typename StringT::value_type; + using container_type = StringT; + private: + container_type* buf_ptr; + + // Noncopyable + string_sink(const string_sink&) = delete; + string_sink& operator=(const string_sink&) = delete; + public: + string_sink(string_sink&& val) noexcept + : buf_ptr(nullptr) + { + std::swap(buf_ptr,val.buf_ptr); + } + + string_sink(container_type& buf) + : buf_ptr(std::addressof(buf)) + { + } + + string_sink& operator=(string_sink&& val) noexcept + { + // TODO: Shouldn't val.buf_ptr be nullified? + // Also see move constructor above. + std::swap(buf_ptr,val.buf_ptr); + return *this; + } + + void flush() + { + } + + void append(const value_type* s, std::size_t length) + { + buf_ptr->insert(buf_ptr->end(), s, s+length); + } + + void push_back(value_type ch) + { + buf_ptr->push_back(ch); + } + }; + + // bytes_sink + + template + class bytes_sink + { + }; + + template + class bytes_sink::value>::type> + { + public: + using container_type = Container; + using value_type = typename Container::value_type; + private: + container_type* buf_ptr; + + // Noncopyable + bytes_sink(const bytes_sink&) = delete; + bytes_sink& operator=(const bytes_sink&) = delete; + public: + bytes_sink(bytes_sink&&) = default; + + bytes_sink(container_type& buf) + : buf_ptr(std::addressof(buf)) + { + } + + bytes_sink& operator=(bytes_sink&&) = default; + + void flush() + { + } + + void push_back(uint8_t ch) + { + buf_ptr->push_back(static_cast(ch)); + } + }; + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/source.hpp b/include/jsoncons/source.hpp new file mode 100644 index 0000000..e9c0735 --- /dev/null +++ b/include/jsoncons/source.hpp @@ -0,0 +1,777 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SOURCE_HPP +#define JSONCONS_SOURCE_HPP + +#include +#include +#include +#include +#include // std::addressof +#include // std::memcpy +#include +#include +#include // std::enable_if +#include +#include // jsoncons::byte_traits +#include + +namespace jsoncons { + + template + class basic_null_istream : public std::basic_istream + { + class null_buffer : public std::basic_streambuf + { + null_buffer(const null_buffer&) = delete; + null_buffer& operator=(const null_buffer&) = delete; + public: + using typename std::basic_streambuf::int_type; + using typename std::basic_streambuf::traits_type; + + null_buffer() = default; + null_buffer(null_buffer&&) = default; + null_buffer& operator=(null_buffer&&) = default; + + int_type overflow( int_type ch = typename std::basic_streambuf::traits_type::eof() ) override + { + return ch; + } + } nb_; + public: + basic_null_istream() + : std::basic_istream(&nb_) + { + } + + basic_null_istream(const null_buffer&) = delete; + basic_null_istream& operator=(const null_buffer&) = delete; + basic_null_istream(basic_null_istream&&) noexcept + : std::basic_istream(&nb_) + { + } + basic_null_istream& operator=(basic_null_istream&&) noexcept + { + return *this; + } + }; + + template + struct char_result + { + CharT value; + bool eof; + }; + + // text sources + + template + class stream_source + { + static constexpr std::size_t default_max_buffer_size = 16384; + public: + using value_type = CharT; + private: + using char_type = typename std::conditional::type; + basic_null_istream null_is_; + std::basic_istream* stream_ptr_; + std::basic_streambuf* sbuf_; + std::size_t position_; + std::vector buffer_; + const value_type* buffer_data_; + std::size_t buffer_length_; + + // Noncopyable + stream_source(const stream_source&) = delete; + stream_source& operator=(const stream_source&) = delete; + public: + stream_source() + : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), position_(0), + buffer_(1), buffer_data_(buffer_.data()), buffer_length_(0) + { + } + + stream_source(std::basic_istream& is, std::size_t buf_size = default_max_buffer_size) + : stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()), position_(0), + buffer_(buf_size), buffer_data_(buffer_.data()), buffer_length_(0) + { + } + + stream_source(stream_source&& other) noexcept + : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), position_(0), + buffer_(), buffer_data_(buffer_.data()), buffer_length_(0) + { + if (other.stream_ptr_ != &other.null_is_) + { + stream_ptr_ = other.stream_ptr_; + sbuf_ = other.sbuf_; + position_ = other.position_; + buffer_ = std::move(other.buffer_); + buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data()); + buffer_length_ = other.buffer_length_; + other = stream_source(); + } + } + + ~stream_source() + { + } + + stream_source& operator=(stream_source&& other) noexcept + { + if (other.stream_ptr_ != &other.null_is_) + { + stream_ptr_ = other.stream_ptr_; + sbuf_ = other.sbuf_; + position_ = other.position_; + buffer_ = std::move(other.buffer_); + buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data()); + buffer_length_ = other.buffer_length_; + other = stream_source(); + } + else + { + stream_ptr_ = &null_is_; + sbuf_ = null_is_.rdbuf(); + position_ = 0; + buffer_data_ = buffer_.data(); + buffer_length_ = 0; + } + return *this; + } + + bool eof() const + { + return buffer_length_ == 0 && stream_ptr_->eof(); + } + + bool is_error() const + { + return stream_ptr_->bad(); + } + + std::size_t position() const + { + return position_; + } + + void ignore(std::size_t length) + { + std::size_t len = 0; + if (buffer_length_ > 0) + { + len = (std::min)(buffer_length_, length); + position_ += len; + buffer_data_ += len; + buffer_length_ -= len; + } + while (length - len > 0) + { + fill_buffer(); + if (buffer_length_ == 0) + { + break; + } + std::size_t len2 = (std::min)(buffer_length_, length-len); + position_ += len2; + buffer_data_ += len2; + buffer_length_ -= len2; + len += len2; + } + } + + char_result peek() + { + if (buffer_length_ == 0) + { + fill_buffer(); + } + if (buffer_length_ > 0) + { + value_type c = *buffer_data_; + return char_result{c, false}; + } + else + { + return char_result{0, true}; + } + } + + span read_buffer() + { + if (buffer_length_ == 0) + { + fill_buffer(); + } + const value_type* data = buffer_data_; + std::size_t length = buffer_length_; + buffer_data_ += buffer_length_; + position_ += buffer_length_; + buffer_length_ = 0; + + return span(data, length); + } + + std::size_t read(value_type* p, std::size_t length) + { + std::size_t len = 0; + if (buffer_length_ > 0) + { + len = (std::min)(buffer_length_, length); + std::memcpy(p, buffer_data_, len*sizeof(value_type)); + buffer_data_ += len; + buffer_length_ -= len; + position_ += len; + } + if (length - len == 0) + { + return len; + } + else if (length - len < buffer_.size()) + { + fill_buffer(); + if (buffer_length_ > 0) + { + std::size_t len2 = (std::min)(buffer_length_, length-len); + std::memcpy(p+len, buffer_data_, len2*sizeof(value_type)); + buffer_data_ += len2; + buffer_length_ -= len2; + position_ += len2; + len += len2; + } + return len; + } + else + { + if (stream_ptr_->eof()) + { + buffer_length_ = 0; + return 0; + } + JSONCONS_TRY + { + std::streamsize count = sbuf_->sgetn(reinterpret_cast(p+len), length-len); + std::size_t len2 = static_cast(count); + if (len2 < length-len) + { + stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit); + } + len += len2; + position_ += len2; + return len; + } + JSONCONS_CATCH(const std::exception&) + { + stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit); + return 0; + } + } + } + private: + void fill_buffer() + { + if (stream_ptr_->eof()) + { + buffer_length_ = 0; + return; + } + + buffer_data_ = buffer_.data(); + JSONCONS_TRY + { + std::streamsize count = sbuf_->sgetn(reinterpret_cast(buffer_.data()), buffer_.size()); + buffer_length_ = static_cast(count); + + if (buffer_length_ < buffer_.size()) + { + stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit); + } + } + JSONCONS_CATCH(const std::exception&) + { + stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit); + buffer_length_ = 0; + } + } + }; + + // string_source + + template + class string_source + { + public: + using value_type = CharT; + using string_view_type = jsoncons::basic_string_view; + private: + const value_type* data_; + const value_type* current_; + const value_type* end_; + + // Noncopyable + string_source(const string_source&) = delete; + string_source& operator=(const string_source&) = delete; + public: + string_source() + : data_(nullptr), current_(nullptr), end_(nullptr) + { + } + + template + string_source(const Sourceable& s, + typename std::enable_if::value>::type* = 0) + : data_(s.data()), current_(s.data()), end_(s.data()+s.size()) + { + } + + string_source(const value_type* data) + : data_(data), current_(data), end_(data+std::char_traits::length(data)) + { + } + + string_source(string_source&& val) = default; + + string_source& operator=(string_source&& val) = default; + + bool eof() const + { + return current_ == end_; + } + + bool is_error() const + { + return false; + } + + std::size_t position() const + { + return (current_ - data_)/sizeof(value_type); + } + + void ignore(std::size_t count) + { + std::size_t len; + if ((std::size_t)(end_ - current_) < count) + { + len = end_ - current_; + } + else + { + len = count; + } + current_ += len; + } + + char_result peek() + { + return current_ < end_ ? char_result{*current_, false} : char_result{0, true}; + } + + span read_buffer() + { + const value_type* data = current_; + std::size_t length = end_ - current_; + current_ = end_; + + return span(data, length); + } + + std::size_t read(value_type* p, std::size_t length) + { + std::size_t len; + if ((std::size_t)(end_ - current_) < length) + { + len = end_ - current_; + } + else + { + len = length; + } + std::memcpy(p, current_, len*sizeof(value_type)); + current_ += len; + return len; + } + }; + + // iterator source + + template + class iterator_source + { + public: + using value_type = typename std::iterator_traits::value_type; + private: + static constexpr std::size_t default_max_buffer_size = 16384; + + IteratorT current_; + IteratorT end_; + std::size_t position_; + std::vector buffer_; + std::size_t buffer_length_; + + using difference_type = typename std::iterator_traits::difference_type; + using iterator_category = typename std::iterator_traits::iterator_category; + + // Noncopyable + iterator_source(const iterator_source&) = delete; + iterator_source& operator=(const iterator_source&) = delete; + public: + + iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size) + : current_(first), end_(last), position_(0), buffer_(buf_size), buffer_length_(0) + { + } + + iterator_source(iterator_source&& other) = default; + + iterator_source& operator=(iterator_source&& other) = default; + + bool eof() const + { + return !(current_ != end_); + } + + bool is_error() const + { + return false; + } + + std::size_t position() const + { + return position_; + } + + void ignore(std::size_t count) + { + while (count-- > 0 && current_ != end_) + { + ++position_; + ++current_; + } + } + + char_result peek() + { + return current_ != end_ ? char_result{*current_, false} : char_result{0, true}; + } + + span read_buffer() + { + if (buffer_length_ == 0) + { + buffer_length_ = read(buffer_.data(), buffer_.size()); + } + std::size_t length = buffer_length_; + buffer_length_ = 0; + + return span(buffer_.data(), length); + } + + template + typename std::enable_if::value, std::size_t>::type + read(value_type* data, std::size_t length) + { + std::size_t count = (std::min)(length, static_cast(std::distance(current_, end_))); + + JSONCONS_COPY(current_, current_ + count, data); + current_ += count; + position_ += count; + + return count; + } + + template + typename std::enable_if::value, std::size_t>::type + read(value_type* data, std::size_t length) + { + value_type* p = data; + value_type* pend = data + length; + + while (p < pend && current_ != end_) + { + *p = static_cast(*current_); + ++p; + ++current_; + } + + position_ += (p - data); + + return p - data; + } + }; + + // binary sources + + using binary_stream_source = stream_source; + + class bytes_source + { + public: + typedef uint8_t value_type; + private: + const value_type* data_; + const value_type* current_; + const value_type* end_; + + // Noncopyable + bytes_source(const bytes_source&) = delete; + bytes_source& operator=(const bytes_source&) = delete; + public: + bytes_source() + : data_(nullptr), current_(nullptr), end_(nullptr) + { + } + + template + bytes_source(const Sourceable& source, + typename std::enable_if::value,int>::type = 0) + : data_(reinterpret_cast(source.data())), + current_(data_), + end_(data_+source.size()) + { + } + + bytes_source(bytes_source&&) = default; + + bytes_source& operator=(bytes_source&&) = default; + + bool eof() const + { + return current_ == end_; + } + + bool is_error() const + { + return false; + } + + std::size_t position() const + { + return current_ - data_; + } + + void ignore(std::size_t count) + { + std::size_t len; + if ((std::size_t)(end_ - current_) < count) + { + len = end_ - current_; + } + else + { + len = count; + } + current_ += len; + } + + char_result peek() + { + return current_ < end_ ? char_result{*current_, false} : char_result{0, true}; + } + + span read_buffer() + { + const value_type* data = current_; + std::size_t length = end_ - current_; + current_ = end_; + + return span(data, length); + } + + std::size_t read(value_type* p, std::size_t length) + { + std::size_t len; + if ((std::size_t)(end_ - current_) < length) + { + len = end_ - current_; + } + else + { + len = length; + } + std::memcpy(p, current_, len*sizeof(value_type)); + current_ += len; + return len; + } + }; + + // binary_iterator source + + template + class binary_iterator_source + { + public: + using value_type = uint8_t; + private: + static constexpr std::size_t default_max_buffer_size = 16384; + + IteratorT current_; + IteratorT end_; + std::size_t position_; + std::vector buffer_; + std::size_t buffer_length_; + + using difference_type = typename std::iterator_traits::difference_type; + using iterator_category = typename std::iterator_traits::iterator_category; + + // Noncopyable + binary_iterator_source(const binary_iterator_source&) = delete; + binary_iterator_source& operator=(const binary_iterator_source&) = delete; + public: + binary_iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size) + : current_(first), end_(last), position_(0), buffer_(buf_size), buffer_length_(0) + { + } + + binary_iterator_source(binary_iterator_source&& other) = default; + + binary_iterator_source& operator=(binary_iterator_source&& other) = default; + + bool eof() const + { + return !(current_ != end_); + } + + bool is_error() const + { + return false; + } + + std::size_t position() const + { + return position_; + } + + void ignore(std::size_t count) + { + while (count-- > 0 && current_ != end_) + { + ++position_; + ++current_; + } + } + + char_result peek() + { + return current_ != end_ ? char_result{static_cast(*current_), false} : char_result{0, true}; + } + + span read_buffer() + { + if (buffer_length_ == 0) + { + buffer_length_ = read(buffer_.data(), buffer_.size()); + } + std::size_t length = buffer_length_; + buffer_length_ = 0; + + return span(buffer_.data(), length); + } + + template + typename std::enable_if::value, std::size_t>::type + read(value_type* data, std::size_t length) + { + std::size_t count = (std::min)(length, static_cast(std::distance(current_, end_))); + JSONCONS_COPY(current_, current_ + count, data); + current_ += count; + position_ += count; + + return count; + } + + template + typename std::enable_if::value, std::size_t>::type + read(value_type* data, std::size_t length) + { + value_type* p = data; + value_type* pend = data + length; + + while (p < pend && current_ != end_) + { + *p = static_cast(*current_); + ++p; + ++current_; + } + + position_ += (p - data); + + return p - data; + } + }; + + template + struct source_reader + { + using value_type = typename Source::value_type; + static constexpr std::size_t max_buffer_length = 16384; + + template + static + typename std::enable_if::value && + type_traits::has_reserve::value && + type_traits::has_data_exact::value + , std::size_t>::type + read(Source& source, Container& v, std::size_t length) + { + std::size_t unread = length; + + std::size_t n = (std::min)(max_buffer_length, unread); + while (n > 0 && !source.eof()) + { + std::size_t offset = v.size(); + v.resize(v.size()+n); + std::size_t actual = source.read(v.data()+offset, n); + unread -= actual; + n = (std::min)(max_buffer_length, unread); + } + + return length - unread; + } + + template + static + typename std::enable_if::value && + type_traits::has_reserve::value && + !type_traits::has_data_exact::value + , std::size_t>::type + read(Source& source, Container& v, std::size_t length) + { + std::size_t unread = length; + + std::size_t n = (std::min)(max_buffer_length, unread); + while (n > 0 && !source.eof()) + { + v.reserve(v.size()+n); + std::size_t actual = 0; + while (actual < n) + { + typename Source::value_type c; + if (source.read(&c,1) != 1) + { + break; + } + v.push_back(c); + ++actual; + } + unread -= actual; + n = (std::min)(max_buffer_length, unread); + } + + return length - unread; + } + }; + template + constexpr std::size_t source_reader::max_buffer_length; + + #if !defined(JSONCONS_NO_DEPRECATED) + using bin_stream_source = binary_stream_source; + #endif + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons/source_adaptor.hpp b/include/jsoncons/source_adaptor.hpp new file mode 100644 index 0000000..51c74a3 --- /dev/null +++ b/include/jsoncons/source_adaptor.hpp @@ -0,0 +1,148 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BUFFER_READER_HPP +#define JSONCONS_BUFFER_READER_HPP + +#include +#include +#include +#include +#include +#include // std::allocator_traits +#include // std::vector +#include +#include // json_errc +#include +#include + +namespace jsoncons { + + // text_source_adaptor + + template + class text_source_adaptor + { + public: + using value_type = typename Source::value_type; + private: + Source source_; + bool bof_; + + public: + text_source_adaptor() + : bof_(true) + { + } + + template + text_source_adaptor(Sourceable&& source) + : source_(std::forward(source)), bof_(true) + { + } + + bool eof() const + { + return source_.eof(); + } + + bool is_error() const + { + return source_.is_error(); + } + + span read_buffer(std::error_code& ec) + { + if (source_.eof()) + { + return span(); + } + + auto s = source_.read_buffer(); + const value_type* data = s.data(); + std::size_t length = s.size(); + + if (bof_ && length > 0) + { + auto r = unicode_traits::detect_encoding_from_bom(data, length); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return span(); + } + length -= (r.ptr - data); + data = r.ptr; + bof_ = false; + } + return span(data, length); + } + }; + + // json_source_adaptor + + template + class json_source_adaptor + { + public: + using value_type = typename Source::value_type; + private: + Source source_; + bool bof_; + + public: + json_source_adaptor() + : bof_(true) + { + } + + template + json_source_adaptor(Sourceable&& source) + : source_(std::forward(source)), bof_(true) + { + } + + bool eof() const + { + return source_.eof(); + } + + bool is_error() const + { + return source_.is_error(); + } + + span read_buffer(std::error_code& ec) + { + if (source_.eof()) + { + return span(); + } + + auto s = source_.read_buffer(); + const value_type* data = s.data(); + std::size_t length = s.size(); + + if (bof_ && length > 0) + { + auto r = unicode_traits::detect_json_encoding(data, length); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return span(); + } + length -= (r.ptr - data); + data = r.ptr; + bof_ = false; + } + + return span(data, length); + } + }; + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/staj2_cursor.hpp b/include/jsoncons/staj2_cursor.hpp new file mode 100644 index 0000000..4cbbc2f --- /dev/null +++ b/include/jsoncons/staj2_cursor.hpp @@ -0,0 +1,1178 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_STAJ2_CURSOR_HPP +#define JSONCONS_STAJ2_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include // std::enable_if +#include // std::array +#include // std::function +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + enum class staj2_event_type + { + begin_array, + end_array, + begin_object, + end_object, + string_value, + byte_string_value, + null_value, + bool_value, + int64_value, + uint64_value, + half_value, + double_value + }; + + template + std::basic_ostream& operator<<(std::basic_ostream& os, staj2_event_type tag) + { + static constexpr const CharT* begin_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_array"); + static constexpr const CharT* end_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_array"); + static constexpr const CharT* begin_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_object"); + static constexpr const CharT* end_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_object"); + static constexpr const CharT* string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "string_value"); + static constexpr const CharT* byte_string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string_value"); + static constexpr const CharT* null_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "null_value"); + static constexpr const CharT* bool_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "bool_value"); + static constexpr const CharT* uint64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "uint64_value"); + static constexpr const CharT* int64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "int64_value"); + static constexpr const CharT* half_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "half_value"); + static constexpr const CharT* double_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "double_value"); + + switch (tag) + { + case staj2_event_type::begin_array: + { + os << begin_array_name; + break; + } + case staj2_event_type::end_array: + { + os << end_array_name; + break; + } + case staj2_event_type::begin_object: + { + os << begin_object_name; + break; + } + case staj2_event_type::end_object: + { + os << end_object_name; + break; + } + case staj2_event_type::string_value: + { + os << string_value_name; + break; + } + case staj2_event_type::byte_string_value: + { + os << byte_string_value_name; + break; + } + case staj2_event_type::null_value: + { + os << null_value_name; + break; + } + case staj2_event_type::bool_value: + { + os << bool_value_name; + break; + } + case staj2_event_type::int64_value: + { + os << int64_value_name; + break; + } + case staj2_event_type::uint64_value: + { + os << uint64_value_name; + break; + } + case staj2_event_type::half_value: + { + os << half_value_name; + break; + } + case staj2_event_type::double_value: + { + os << double_value_name; + break; + } + } + return os; + } + + template + class basic_staj2_event + { + staj2_event_type event_type_; + semantic_tag tag_; + uint64_t ext_tag_; + union + { + bool bool_value_; + int64_t int64_value_; + uint64_t uint64_value_; + uint16_t half_value_; + double double_value_; + const CharT* string_data_; + const uint8_t* byte_string_data_; + } value_; + std::size_t length_; + public: + using string_view_type = jsoncons::basic_string_view; + + basic_staj2_event(staj2_event_type event_type, semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(0) + { + } + + basic_staj2_event(staj2_event_type event_type, std::size_t length, semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(length) + { + } + + basic_staj2_event(null_type, semantic_tag tag) + : event_type_(staj2_event_type::null_value), tag_(tag), ext_tag_(0), value_(), length_(0) + { + } + + basic_staj2_event(bool value, semantic_tag tag) + : event_type_(staj2_event_type::bool_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.bool_value_ = value; + } + + basic_staj2_event(int64_t value, semantic_tag tag) + : event_type_(staj2_event_type::int64_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.int64_value_ = value; + } + + basic_staj2_event(uint64_t value, semantic_tag tag) + : event_type_(staj2_event_type::uint64_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.uint64_value_ = value; + } + + basic_staj2_event(half_arg_t, uint16_t value, semantic_tag tag) + : event_type_(staj2_event_type::half_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.half_value_ = value; + } + + basic_staj2_event(double value, semantic_tag tag) + : event_type_(staj2_event_type::double_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.double_value_ = value; + } + + basic_staj2_event(const string_view_type& s, + staj2_event_type event_type, + semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.length()) + { + value_.string_data_ = s.data(); + } + + basic_staj2_event(const byte_string_view& s, + staj2_event_type event_type, + semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.size()) + { + value_.byte_string_data_ = s.data(); + } + + basic_staj2_event(const byte_string_view& s, + staj2_event_type event_type, + uint64_t ext_tag) + : event_type_(event_type), tag_(semantic_tag::ext), ext_tag_(ext_tag), length_(s.size()) + { + value_.byte_string_data_ = s.data(); + } + + std::size_t size() const + { + return length_; + } + + template + T get() const + { + std::error_code ec; + T val = get(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return val; + } + + template + typename std::enable_if::value && std::is_same::value, T>::type + get(std::error_code& ec) const + { + converter conv; + switch (event_type_) + { + case staj2_event_type::string_value: + return conv.from(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + case staj2_event_type::byte_string_value: + { + return conv.from(byte_string_view(value_.byte_string_data_,length_), + tag(), + ec); + } + case staj2_event_type::uint64_value: + { + return conv.from(value_.uint64_value_, tag(), ec); + } + case staj2_event_type::int64_value: + { + return conv.from(value_.int64_value_, tag(), ec); + } + case staj2_event_type::half_value: + { + return conv.from(half_arg, value_.half_value_, tag(), ec); + } + case staj2_event_type::double_value: + { + return conv.from(value_.double_value_, tag(), ec); + } + case staj2_event_type::bool_value: + { + return conv.from(value_.bool_value_,tag(),ec); + } + case staj2_event_type::null_value: + { + return conv.from(null_type(),tag(),ec); + } + default: + { + ec = conv_errc::not_string; + return T{}; + } + } + } + + template + typename std::enable_if::value && std::is_same::value, T>::type + get(std::error_code& ec) const + { + T s; + switch (event_type_) + { + case staj2_event_type::string_value: + s = T(value_.string_data_, length_); + break; + default: + ec = conv_errc::not_string_view; + break; + } + return s; + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + T s; + switch (event_type_) + { + case staj2_event_type::byte_string_value: + s = T(value_.byte_string_data_, length_); + break; + default: + ec = conv_errc::not_byte_string_view; + break; + } + return s; + } + + template + typename std::enable_if::value && + std::is_same::value,T>::type + get(std::error_code& ec) const + { + converter conv; + switch (event_type_) + { + case staj2_event_type::byte_string_value: + return conv.from(byte_string_view(value_.byte_string_data_, length_), tag(), ec); + case staj2_event_type::string_value: + return conv.from(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + default: + ec = conv_errc::not_byte_string; + return T{}; + } + } + + template + typename std::enable_if::value, IntegerType>::type + get(std::error_code& ec) const + { + switch (event_type_) + { + case staj2_event_type::string_value: + { + IntegerType val; + auto result = jsoncons::detail::to_integer(value_.string_data_, length_, val); + if (!result) + { + ec = conv_errc::not_integer; + return IntegerType(); + } + return val; + } + case staj2_event_type::half_value: + return static_cast(value_.half_value_); + case staj2_event_type::double_value: + return static_cast(value_.double_value_); + case staj2_event_type::int64_value: + return static_cast(value_.int64_value_); + case staj2_event_type::uint64_value: + return static_cast(value_.uint64_value_); + case staj2_event_type::bool_value: + return static_cast(value_.bool_value_ ? 1 : 0); + default: + ec = conv_errc::not_integer; + return IntegerType(); + } + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + return static_cast(as_double(ec)); + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + return as_bool(ec); + } + + staj2_event_type event_type() const noexcept { return event_type_; } + + semantic_tag tag() const noexcept { return tag_; } + + uint64_t ext_tag() const noexcept { return ext_tag_; } + + private: + + double as_double(std::error_code& ec) const + { + switch (event_type_) + { + case staj2_event_type::string_value: + { + jsoncons::detail::chars_to f; + return f(value_.string_data_, length_); + } + case staj2_event_type::double_value: + return value_.double_value_; + case staj2_event_type::int64_value: + return static_cast(value_.int64_value_); + case staj2_event_type::uint64_value: + return static_cast(value_.uint64_value_); + case staj2_event_type::half_value: + { + double x = binary::decode_half(value_.half_value_); + return static_cast(x); + } + default: + ec = conv_errc::not_double; + return double(); + } + } + + bool as_bool(std::error_code& ec) const + { + switch (event_type_) + { + case staj2_event_type::bool_value: + return value_.bool_value_; + case staj2_event_type::double_value: + return value_.double_value_ != 0.0; + case staj2_event_type::int64_value: + return value_.int64_value_ != 0; + case staj2_event_type::uint64_value: + return value_.uint64_value_ != 0; + default: + ec = conv_errc::not_bool; + return bool(); + } + } + }; + + // basic_staj2_visitor + + enum class staj2_cursor_state + { + typed_array = 1, + multi_dim, + shape + }; + + template + class basic_staj2_visitor : public basic_json_visitor2 + { + using super_type = basic_json_visitor2; + public: + using char_type = CharT; + using typename super_type::string_view_type; + private: + std::function&, const ser_context&)> pred_; + basic_staj2_event event_; + + staj2_cursor_state state_; + typed_array_view data_; + jsoncons::span shape_; + std::size_t index_; + public: + basic_staj2_visitor() + : pred_(accept), event_(staj2_event_type::null_value), + state_(), data_(), shape_(), index_(0) + { + } + + basic_staj2_visitor(std::function&, const ser_context&)> pred) + : pred_(pred), event_(staj2_event_type::null_value), + state_(), data_(), shape_(), index_(0) + { + } + + void reset() + { + event_ = staj2_event_type::null_value; + state_ = {}; + data_ = {}; + shape_ = {}; + index_ = 0; + } + + const basic_staj2_event& event() const + { + return event_; + } + + bool in_available() const + { + return state_ != staj2_cursor_state(); + } + + void send_available(std::error_code& ec) + { + switch (state_) + { + case staj2_cursor_state::typed_array: + advance_typed_array(ec); + break; + case staj2_cursor_state::multi_dim: + case staj2_cursor_state::shape: + advance_multi_dim(ec); + break; + default: + break; + } + } + + bool is_typed_array() const + { + return data_.type() != typed_array_type(); + } + + staj2_cursor_state state() const + { + return state_; + } + + void advance_typed_array(std::error_code& ec) + { + if (is_typed_array()) + { + if (index_ < data_.size()) + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + this->uint64_value(data_.data(uint8_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint16_value: + { + this->uint64_value(data_.data(uint16_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint32_value: + { + this->uint64_value(data_.data(uint32_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint64_value: + { + this->uint64_value(data_.data(uint64_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int8_value: + { + this->int64_value(data_.data(int8_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int16_value: + { + this->int64_value(data_.data(int16_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int32_value: + { + this->int64_value(data_.data(int32_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int64_value: + { + this->int64_value(data_.data(int64_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::half_value: + { + this->half_value(data_.data(half_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::float_value: + { + this->double_value(data_.data(float_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::double_value: + { + this->double_value(data_.data(double_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + default: + break; + } + ++index_; + } + else + { + this->end_array(); + state_ = staj2_cursor_state(); + data_ = typed_array_view(); + index_ = 0; + } + } + } + + void advance_multi_dim(std::error_code& ec) + { + if (shape_.size() != 0) + { + if (state_ == staj2_cursor_state::multi_dim) + { + this->begin_array(shape_.size(), semantic_tag::none, ser_context(), ec); + state_ = staj2_cursor_state::shape; + } + else if (index_ < shape_.size()) + { + this->uint64_value(shape_[index_], semantic_tag::none, ser_context(), ec); + ++index_; + } + else + { + state_ = staj2_cursor_state(); + this->end_array(ser_context(), ec); + shape_ = jsoncons::span(); + index_ = 0; + } + } + } + + bool dump(basic_json_visitor2& visitor, const ser_context& context, std::error_code& ec) + { + bool more = true; + if (is_typed_array()) + { + if (index_ != 0) + { + more = staj2_to_saj_event(event(), visitor, context, ec); + while (more && is_typed_array()) + { + if (index_ < data_.size()) + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + more = visitor.uint64_value(data_.data(uint8_array_arg)[index_]); + break; + } + case typed_array_type::uint16_value: + { + more = visitor.uint64_value(data_.data(uint16_array_arg)[index_]); + break; + } + case typed_array_type::uint32_value: + { + more = visitor.uint64_value(data_.data(uint32_array_arg)[index_]); + break; + } + case typed_array_type::uint64_value: + { + more = visitor.uint64_value(data_.data(uint64_array_arg)[index_]); + break; + } + case typed_array_type::int8_value: + { + more = visitor.int64_value(data_.data(int8_array_arg)[index_]); + break; + } + case typed_array_type::int16_value: + { + more = visitor.int64_value(data_.data(int16_array_arg)[index_]); + break; + } + case typed_array_type::int32_value: + { + more = visitor.int64_value(data_.data(int32_array_arg)[index_]); + break; + } + case typed_array_type::int64_value: + { + more = visitor.int64_value(data_.data(int64_array_arg)[index_]); + break; + } + case typed_array_type::float_value: + { + more = visitor.double_value(data_.data(float_array_arg)[index_]); + break; + } + case typed_array_type::double_value: + { + more = visitor.double_value(data_.data(double_array_arg)[index_]); + break; + } + default: + break; + } + ++index_; + } + else + { + more = visitor.end_array(); + state_ = staj2_cursor_state(); + data_ = typed_array_view(); + index_ = 0; + } + } + } + else + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + more = visitor.typed_array(data_.data(uint8_array_arg)); + break; + } + case typed_array_type::uint16_value: + { + more = visitor.typed_array(data_.data(uint16_array_arg)); + break; + } + case typed_array_type::uint32_value: + { + more = visitor.typed_array(data_.data(uint32_array_arg)); + break; + } + case typed_array_type::uint64_value: + { + more = visitor.typed_array(data_.data(uint64_array_arg)); + break; + } + case typed_array_type::int8_value: + { + more = visitor.typed_array(data_.data(int8_array_arg)); + break; + } + case typed_array_type::int16_value: + { + more = visitor.typed_array(data_.data(int16_array_arg)); + break; + } + case typed_array_type::int32_value: + { + more = visitor.typed_array(data_.data(int32_array_arg)); + break; + } + case typed_array_type::int64_value: + { + more = visitor.typed_array(data_.data(int64_array_arg)); + break; + } + case typed_array_type::float_value: + { + more = visitor.typed_array(data_.data(float_array_arg)); + break; + } + case typed_array_type::double_value: + { + more = visitor.typed_array(data_.data(double_array_arg)); + break; + } + default: + break; + } + + state_ = staj2_cursor_state(); + data_ = typed_array_view(); + } + } + else + { + more = staj2_to_saj_event(event(), visitor, context, ec); + } + return more; + } + + private: + static constexpr bool accept(const basic_staj2_event&, const ser_context&) + { + return true; + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::begin_object, tag); + return !pred_(event_, context); + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::begin_object, length, tag); + return !pred_(event_, context); + } + + bool visit_end_object(const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::end_object); + return !pred_(event_, context); + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::begin_array, tag); + return !pred_(event_, context); + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::begin_array, length, tag); + return !pred_(event_, context); + } + + bool visit_end_array(const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::end_array); + return !pred_(event_, context); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(staj2_event_type::null_value, tag); + return !pred_(event_, context); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(value, tag); + return !pred_(event_, context); + } + + bool visit_string(const string_view_type& s, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj2_event(s, staj2_event_type::string_value, tag); + return !pred_(event_, context); + } + + bool visit_byte_string(const byte_string_view& s, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(s, staj2_event_type::byte_string_value, tag); + return !pred_(event_, context); + } + + bool visit_byte_string(const byte_string_view& s, + uint64_t ext_tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(s, staj2_event_type::byte_string_value, ext_tag); + return !pred_(event_, context); + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(value, tag); + return !pred_(event_, context); + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(value, tag); + return !pred_(event_, context); + } + + bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(half_arg, value, tag); + return !pred_(event_, context); + } + + bool visit_double(double value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj2_event(value, tag); + return !pred_(event_, context); + } + + bool visit_typed_array(const jsoncons::span& v, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(v.data(), v.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(half_arg_t, const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + /* + bool visit_typed_array(const jsoncons::span&, + semantic_tag, + const ser_context&, + std::error_code&) override + { + return true; + } + */ + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj2_cursor_state::multi_dim; + shape_ = shape; + return this->begin_array(2, tag, context, ec); + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + return this->end_array(context, ec); + } + + void visit_flush() override + { + } + }; + + template + bool staj2_to_saj_event(const basic_staj2_event& ev, + basic_json_visitor2& visitor, + const ser_context& context, + std::error_code& ec) + { + switch (ev.event_type()) + { + case staj2_event_type::begin_array: + return visitor.begin_array(ev.tag(), context); + case staj2_event_type::end_array: + return visitor.end_array(context); + case staj2_event_type::begin_object: + return visitor.begin_object(ev.tag(), context, ec); + case staj2_event_type::end_object: + return visitor.end_object(context, ec); + case staj2_event_type::string_value: + return visitor.string_value(ev.template get>(), ev.tag(), context); + case staj2_event_type::byte_string_value: + return visitor.byte_string_value(ev.template get(), ev.tag(), context); + case staj2_event_type::null_value: + return visitor.null_value(ev.tag(), context); + case staj2_event_type::bool_value: + return visitor.bool_value(ev.template get(), ev.tag(), context); + case staj2_event_type::int64_value: + return visitor.int64_value(ev.template get(), ev.tag(), context); + case staj2_event_type::uint64_value: + return visitor.uint64_value(ev.template get(), ev.tag(), context); + case staj2_event_type::half_value: + return visitor.half_value(ev.template get(), ev.tag(), context); + case staj2_event_type::double_value: + return visitor.double_value(ev.template get(), ev.tag(), context); + default: + return false; + } + } + + // basic_staj2_cursor + + template + class basic_staj2_cursor + { + public: + virtual ~basic_staj2_cursor() noexcept = default; + + virtual void array_expected(std::error_code& ec) + { + if (!(current().event_type() == staj2_event_type::begin_array || current().event_type() == staj2_event_type::byte_string_value)) + { + ec = conv_errc::not_vector; + } + } + + virtual bool done() const = 0; + + virtual const basic_staj2_event& current() const = 0; + + virtual void read_to(basic_json_visitor2& visitor) = 0; + + virtual void read_to(basic_json_visitor2& visitor, + std::error_code& ec) = 0; + + virtual void next() = 0; + + virtual void next(std::error_code& ec) = 0; + + virtual const ser_context& context() const = 0; + }; + + template + class basic_staj2_filter_view : basic_staj2_cursor + { + basic_staj2_cursor* cursor_; + std::function&, const ser_context&)> pred_; + public: + basic_staj2_filter_view(basic_staj2_cursor& cursor, + std::function&, const ser_context&)> pred) + : cursor_(std::addressof(cursor)), pred_(pred) + { + while (!done() && !pred_(current(),context())) + { + cursor_->next(); + } + } + + bool done() const override + { + return cursor_->done(); + } + + const basic_staj2_event& current() const override + { + return cursor_->current(); + } + + void read_to(basic_json_visitor2& visitor) override + { + cursor_->read_to(visitor); + } + + void read_to(basic_json_visitor2& visitor, + std::error_code& ec) override + { + cursor_->read_to(visitor, ec); + } + + void next() override + { + cursor_->next(); + while (!done() && !pred_(current(),context())) + { + cursor_->next(); + } + } + + void next(std::error_code& ec) override + { + cursor_->next(ec); + while (!done() && !pred_(current(),context()) && !ec) + { + cursor_->next(ec); + } + } + + const ser_context& context() const override + { + return cursor_->context(); + } + + friend + basic_staj2_filter_view operator|(basic_staj2_filter_view& cursor, + std::function&, const ser_context&)> pred) + { + return basic_staj2_filter_view(cursor, pred); + } + }; + + using staj2_event = basic_staj2_event; + using wstaj2_event = basic_staj2_event; + + using staj2_cursor = basic_staj2_cursor; + using wstaj2_cursor = basic_staj2_cursor; + + using staj2_filter_view = basic_staj2_filter_view; + using wstaj2_filter_view = basic_staj2_filter_view; + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/staj_cursor.hpp b/include/jsoncons/staj_cursor.hpp new file mode 100644 index 0000000..4594ba1 --- /dev/null +++ b/include/jsoncons/staj_cursor.hpp @@ -0,0 +1,1233 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_STAJ_CURSOR_HPP +#define JSONCONS_STAJ_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include // std::enable_if +#include // std::array +#include // std::function +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +enum class staj_event_type +{ + begin_array, + end_array, + begin_object, + end_object, + key, + string_value, + byte_string_value, + null_value, + bool_value, + int64_value, + uint64_value, + half_value, + double_value +#if !defined(JSONCONS_NO_DEPRECATED) + ,name = key +#endif +}; + +template +std::basic_ostream& operator<<(std::basic_ostream& os, staj_event_type tag) +{ + static constexpr const CharT* begin_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_array"); + static constexpr const CharT* end_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_array"); + static constexpr const CharT* begin_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_object"); + static constexpr const CharT* end_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_object"); + static constexpr const CharT* key_name = JSONCONS_CSTRING_CONSTANT(CharT, "key"); + static constexpr const CharT* string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "string_value"); + static constexpr const CharT* byte_string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string_value"); + static constexpr const CharT* null_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "null_value"); + static constexpr const CharT* bool_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "bool_value"); + static constexpr const CharT* uint64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "uint64_value"); + static constexpr const CharT* int64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "int64_value"); + static constexpr const CharT* half_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "half_value"); + static constexpr const CharT* double_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "double_value"); + + switch (tag) + { + case staj_event_type::begin_array: + { + os << begin_array_name; + break; + } + case staj_event_type::end_array: + { + os << end_array_name; + break; + } + case staj_event_type::begin_object: + { + os << begin_object_name; + break; + } + case staj_event_type::end_object: + { + os << end_object_name; + break; + } + case staj_event_type::key: + { + os << key_name; + break; + } + case staj_event_type::string_value: + { + os << string_value_name; + break; + } + case staj_event_type::byte_string_value: + { + os << byte_string_value_name; + break; + } + case staj_event_type::null_value: + { + os << null_value_name; + break; + } + case staj_event_type::bool_value: + { + os << bool_value_name; + break; + } + case staj_event_type::int64_value: + { + os << int64_value_name; + break; + } + case staj_event_type::uint64_value: + { + os << uint64_value_name; + break; + } + case staj_event_type::half_value: + { + os << half_value_name; + break; + } + case staj_event_type::double_value: + { + os << double_value_name; + break; + } + } + return os; +} + +template +class basic_staj_event +{ + staj_event_type event_type_; + semantic_tag tag_; + uint64_t ext_tag_; + union + { + bool bool_value_; + int64_t int64_value_; + uint64_t uint64_value_; + uint16_t half_value_; + double double_value_; + const CharT* string_data_; + const uint8_t* byte_string_data_; + } value_; + std::size_t length_; +public: + using string_view_type = jsoncons::basic_string_view; + + basic_staj_event(staj_event_type event_type, semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(0) + { + } + + basic_staj_event(staj_event_type event_type, std::size_t length, semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(length) + { + } + + basic_staj_event(null_type, semantic_tag tag) + : event_type_(staj_event_type::null_value), tag_(tag), ext_tag_(0), value_(), length_(0) + { + } + + basic_staj_event(bool value, semantic_tag tag) + : event_type_(staj_event_type::bool_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.bool_value_ = value; + } + + basic_staj_event(int64_t value, semantic_tag tag) + : event_type_(staj_event_type::int64_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.int64_value_ = value; + } + + basic_staj_event(uint64_t value, semantic_tag tag) + : event_type_(staj_event_type::uint64_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.uint64_value_ = value; + } + + basic_staj_event(half_arg_t, uint16_t value, semantic_tag tag) + : event_type_(staj_event_type::half_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.half_value_ = value; + } + + basic_staj_event(double value, semantic_tag tag) + : event_type_(staj_event_type::double_value), tag_(tag), ext_tag_(0), length_(0) + { + value_.double_value_ = value; + } + + basic_staj_event(const string_view_type& s, + staj_event_type event_type, + semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.length()) + { + value_.string_data_ = s.data(); + } + + basic_staj_event(const byte_string_view& s, + staj_event_type event_type, + semantic_tag tag = semantic_tag::none) + : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.size()) + { + value_.byte_string_data_ = s.data(); + } + + basic_staj_event(const byte_string_view& s, + staj_event_type event_type, + uint64_t ext_tag) + : event_type_(event_type), tag_(semantic_tag::ext), ext_tag_(ext_tag), length_(s.size()) + { + value_.byte_string_data_ = s.data(); + } + + std::size_t size() const + { + return length_; + } + + template + T get() const + { + std::error_code ec; + T val = get(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + return val; + } + + template + typename std::enable_if::value && std::is_same::value, T>::type + get(std::error_code& ec) const + { + converter conv; + switch (event_type_) + { + case staj_event_type::key: + case staj_event_type::string_value: + return conv.from(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + case staj_event_type::byte_string_value: + { + return conv.from(byte_string_view(value_.byte_string_data_,length_), + tag(), + ec); + } + case staj_event_type::uint64_value: + { + return conv.from(value_.uint64_value_, tag(), ec); + } + case staj_event_type::int64_value: + { + return conv.from(value_.int64_value_, tag(), ec); + } + case staj_event_type::half_value: + { + return conv.from(half_arg, value_.half_value_, tag(), ec); + } + case staj_event_type::double_value: + { + return conv.from(value_.double_value_, tag(), ec); + } + case staj_event_type::bool_value: + { + return conv.from(value_.bool_value_,tag(),ec); + } + case staj_event_type::null_value: + { + return conv.from(null_type(),tag(),ec); + } + default: + { + ec = conv_errc::not_string; + return T{}; + } + } + } + + template + typename std::enable_if::value && std::is_same::value, T>::type + get(std::error_code& ec) const + { + T s; + switch (event_type_) + { + case staj_event_type::key: + case staj_event_type::string_value: + s = T(value_.string_data_, length_); + break; + default: + ec = conv_errc::not_string_view; + break; + } + return s; + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + T s; + switch (event_type_) + { + case staj_event_type::byte_string_value: + s = T(value_.byte_string_data_, length_); + break; + default: + ec = conv_errc::not_byte_string_view; + break; + } + return s; + } + + template + typename std::enable_if::value && + std::is_same::value,T>::type + get(std::error_code& ec) const + { + converter conv; + switch (event_type_) + { + case staj_event_type::byte_string_value: + return conv.from(byte_string_view(value_.byte_string_data_, length_), tag(), ec); + case staj_event_type::string_value: + return conv.from(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + default: + ec = conv_errc::not_byte_string; + return T{}; + } + } + + template + typename std::enable_if::value, IntegerType>::type + get(std::error_code& ec) const + { + switch (event_type_) + { + case staj_event_type::string_value: + { + IntegerType val; + auto result = jsoncons::detail::to_integer(value_.string_data_, length_, val); + if (!result) + { + ec = conv_errc::not_integer; + return IntegerType(); + } + return val; + } + case staj_event_type::half_value: + return static_cast(value_.half_value_); + case staj_event_type::double_value: + return static_cast(value_.double_value_); + case staj_event_type::int64_value: + return static_cast(value_.int64_value_); + case staj_event_type::uint64_value: + return static_cast(value_.uint64_value_); + case staj_event_type::bool_value: + return static_cast(value_.bool_value_ ? 1 : 0); + default: + ec = conv_errc::not_integer; + return IntegerType(); + } + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + return static_cast(as_double(ec)); + } + + template + typename std::enable_if::value, T>::type + get(std::error_code& ec) const + { + return as_bool(ec); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + template + JSONCONS_DEPRECATED_MSG("Instead, use get()") + T as() const + { + return get(); + } + semantic_tag get_semantic_tag() const noexcept { return tag_; } +#endif + + staj_event_type event_type() const noexcept { return event_type_; } + + semantic_tag tag() const noexcept { return tag_; } + + uint64_t ext_tag() const noexcept { return ext_tag_; } + +private: + + double as_double(std::error_code& ec) const + { + switch (event_type_) + { + case staj_event_type::key: + case staj_event_type::string_value: + { + jsoncons::detail::chars_to f; + return f(value_.string_data_, length_); + } + case staj_event_type::double_value: + return value_.double_value_; + case staj_event_type::int64_value: + return static_cast(value_.int64_value_); + case staj_event_type::uint64_value: + return static_cast(value_.uint64_value_); + case staj_event_type::half_value: + { + double x = binary::decode_half(value_.half_value_); + return static_cast(x); + } + default: + ec = conv_errc::not_double; + return double(); + } + } + + bool as_bool(std::error_code& ec) const + { + switch (event_type_) + { + case staj_event_type::bool_value: + return value_.bool_value_; + case staj_event_type::double_value: + return value_.double_value_ != 0.0; + case staj_event_type::int64_value: + return value_.int64_value_ != 0; + case staj_event_type::uint64_value: + return value_.uint64_value_ != 0; + default: + ec = conv_errc::not_bool; + return bool(); + } + } +}; + +// basic_staj_visitor + +enum class staj_cursor_state +{ + typed_array = 1, + multi_dim, + shape +}; + +template +class basic_staj_visitor : public basic_json_visitor +{ + using super_type = basic_json_visitor; +public: + using char_type = CharT; + using typename super_type::string_view_type; +private: + std::function&, const ser_context&)> pred_; + basic_staj_event event_; + + staj_cursor_state state_; + typed_array_view data_; + jsoncons::span shape_; + std::size_t index_; +public: + basic_staj_visitor() + : pred_(accept), event_(staj_event_type::null_value), + state_(), data_(), shape_(), index_(0) + { + } + + basic_staj_visitor(std::function&, const ser_context&)> pred) + : pred_(pred), event_(staj_event_type::null_value), + state_(), data_(), shape_(), index_(0) + { + } + + void reset() + { + event_ = staj_event_type::null_value; + state_ = {}; + data_ = {}; + shape_ = {}; + index_ = 0; + } + + const basic_staj_event& event() const + { + return event_; + } + + bool in_available() const + { + return state_ != staj_cursor_state(); + } + + void send_available(std::error_code& ec) + { + switch (state_) + { + case staj_cursor_state::typed_array: + advance_typed_array(ec); + break; + case staj_cursor_state::multi_dim: + case staj_cursor_state::shape: + advance_multi_dim(ec); + break; + default: + break; + } + } + + bool is_typed_array() const + { + return data_.type() != typed_array_type(); + } + + staj_cursor_state state() const + { + return state_; + } + + void advance_typed_array(std::error_code& ec) + { + if (is_typed_array()) + { + if (index_ < data_.size()) + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + this->uint64_value(data_.data(uint8_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint16_value: + { + this->uint64_value(data_.data(uint16_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint32_value: + { + this->uint64_value(data_.data(uint32_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::uint64_value: + { + this->uint64_value(data_.data(uint64_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int8_value: + { + this->int64_value(data_.data(int8_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int16_value: + { + this->int64_value(data_.data(int16_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int32_value: + { + this->int64_value(data_.data(int32_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::int64_value: + { + this->int64_value(data_.data(int64_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::half_value: + { + this->half_value(data_.data(half_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::float_value: + { + this->double_value(data_.data(float_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + case typed_array_type::double_value: + { + this->double_value(data_.data(double_array_arg)[index_], semantic_tag::none, ser_context(), ec); + break; + } + default: + break; + } + ++index_; + } + else + { + this->end_array(); + state_ = staj_cursor_state(); + data_ = typed_array_view(); + index_ = 0; + } + } + } + + void advance_multi_dim(std::error_code& ec) + { + if (shape_.size() != 0) + { + if (state_ == staj_cursor_state::multi_dim) + { + this->begin_array(shape_.size(), semantic_tag::none, ser_context(), ec); + state_ = staj_cursor_state::shape; + } + else if (index_ < shape_.size()) + { + this->uint64_value(shape_[index_], semantic_tag::none, ser_context(), ec); + ++index_; + } + else + { + state_ = staj_cursor_state(); + this->end_array(ser_context(), ec); + shape_ = jsoncons::span(); + index_ = 0; + } + } + } + + bool dump(basic_json_visitor& visitor, const ser_context& context, std::error_code& ec) + { + bool more = true; + if (is_typed_array()) + { + if (index_ != 0) + { + more = staj_to_saj_event(event(), visitor, context, ec); + while (more && is_typed_array()) + { + if (index_ < data_.size()) + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + more = visitor.uint64_value(data_.data(uint8_array_arg)[index_]); + break; + } + case typed_array_type::uint16_value: + { + more = visitor.uint64_value(data_.data(uint16_array_arg)[index_]); + break; + } + case typed_array_type::uint32_value: + { + more = visitor.uint64_value(data_.data(uint32_array_arg)[index_]); + break; + } + case typed_array_type::uint64_value: + { + more = visitor.uint64_value(data_.data(uint64_array_arg)[index_]); + break; + } + case typed_array_type::int8_value: + { + more = visitor.int64_value(data_.data(int8_array_arg)[index_]); + break; + } + case typed_array_type::int16_value: + { + more = visitor.int64_value(data_.data(int16_array_arg)[index_]); + break; + } + case typed_array_type::int32_value: + { + more = visitor.int64_value(data_.data(int32_array_arg)[index_]); + break; + } + case typed_array_type::int64_value: + { + more = visitor.int64_value(data_.data(int64_array_arg)[index_]); + break; + } + case typed_array_type::float_value: + { + more = visitor.double_value(data_.data(float_array_arg)[index_]); + break; + } + case typed_array_type::double_value: + { + more = visitor.double_value(data_.data(double_array_arg)[index_]); + break; + } + default: + break; + } + ++index_; + } + else + { + more = visitor.end_array(); + state_ = staj_cursor_state(); + data_ = typed_array_view(); + index_ = 0; + } + } + } + else + { + switch (data_.type()) + { + case typed_array_type::uint8_value: + { + more = visitor.typed_array(data_.data(uint8_array_arg)); + break; + } + case typed_array_type::uint16_value: + { + more = visitor.typed_array(data_.data(uint16_array_arg)); + break; + } + case typed_array_type::uint32_value: + { + more = visitor.typed_array(data_.data(uint32_array_arg)); + break; + } + case typed_array_type::uint64_value: + { + more = visitor.typed_array(data_.data(uint64_array_arg)); + break; + } + case typed_array_type::int8_value: + { + more = visitor.typed_array(data_.data(int8_array_arg)); + break; + } + case typed_array_type::int16_value: + { + more = visitor.typed_array(data_.data(int16_array_arg)); + break; + } + case typed_array_type::int32_value: + { + more = visitor.typed_array(data_.data(int32_array_arg)); + break; + } + case typed_array_type::int64_value: + { + more = visitor.typed_array(data_.data(int64_array_arg)); + break; + } + case typed_array_type::float_value: + { + more = visitor.typed_array(data_.data(float_array_arg)); + break; + } + case typed_array_type::double_value: + { + more = visitor.typed_array(data_.data(double_array_arg)); + break; + } + default: + break; + } + + state_ = staj_cursor_state(); + data_ = typed_array_view(); + } + } + else + { + more = staj_to_saj_event(event(), visitor, context, ec); + } + return more; + } + +private: + static constexpr bool accept(const basic_staj_event&, const ser_context&) + { + return true; + } + + bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::begin_object, tag); + return !pred_(event_, context); + } + + bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::begin_object, length, tag); + return !pred_(event_, context); + } + + bool visit_end_object(const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::end_object); + return !pred_(event_, context); + } + + bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::begin_array, tag); + return !pred_(event_, context); + } + + bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::begin_array, length, tag); + return !pred_(event_, context); + } + + bool visit_end_array(const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::end_array); + return !pred_(event_, context); + } + + bool visit_key(const string_view_type& name, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(name, staj_event_type::key); + return !pred_(event_, context); + } + + bool visit_null(semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(staj_event_type::null_value, tag); + return !pred_(event_, context); + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(value, tag); + return !pred_(event_, context); + } + + bool visit_string(const string_view_type& s, semantic_tag tag, const ser_context& context, std::error_code&) override + { + event_ = basic_staj_event(s, staj_event_type::string_value, tag); + return !pred_(event_, context); + } + + bool visit_byte_string(const byte_string_view& s, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(s, staj_event_type::byte_string_value, tag); + return !pred_(event_, context); + } + + bool visit_byte_string(const byte_string_view& s, + uint64_t ext_tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(s, staj_event_type::byte_string_value, ext_tag); + return !pred_(event_, context); + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(value, tag); + return !pred_(event_, context); + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(value, tag); + return !pred_(event_, context); + } + + bool visit_half(uint16_t value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(half_arg, value, tag); + return !pred_(event_, context); + } + + bool visit_double(double value, + semantic_tag tag, + const ser_context& context, + std::error_code&) override + { + event_ = basic_staj_event(value, tag); + return !pred_(event_, context); + } + + bool visit_typed_array(const jsoncons::span& v, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(v.data(), v.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(half_arg_t, const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::typed_array; + data_ = typed_array_view(data.data(), data.size()); + index_ = 0; + return this->begin_array(tag, context, ec); + } +/* + bool visit_typed_array(const jsoncons::span&, + semantic_tag, + const ser_context&, + std::error_code&) override + { + return true; + } +*/ + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + state_ = staj_cursor_state::multi_dim; + shape_ = shape; + return this->begin_array(2, tag, context, ec); + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + return this->end_array(context, ec); + } + + void visit_flush() override + { + } +}; + +template +bool staj_to_saj_event(const basic_staj_event& ev, + basic_json_visitor& visitor, + const ser_context& context, + std::error_code& ec) +{ + switch (ev.event_type()) + { + case staj_event_type::begin_array: + return visitor.begin_array(ev.tag(), context); + case staj_event_type::end_array: + return visitor.end_array(context); + case staj_event_type::begin_object: + return visitor.begin_object(ev.tag(), context, ec); + case staj_event_type::end_object: + return visitor.end_object(context, ec); + case staj_event_type::key: + return visitor.key(ev.template get>(), context); + case staj_event_type::string_value: + return visitor.string_value(ev.template get>(), ev.tag(), context); + case staj_event_type::byte_string_value: + return visitor.byte_string_value(ev.template get(), ev.tag(), context); + case staj_event_type::null_value: + return visitor.null_value(ev.tag(), context); + case staj_event_type::bool_value: + return visitor.bool_value(ev.template get(), ev.tag(), context); + case staj_event_type::int64_value: + return visitor.int64_value(ev.template get(), ev.tag(), context); + case staj_event_type::uint64_value: + return visitor.uint64_value(ev.template get(), ev.tag(), context); + case staj_event_type::half_value: + return visitor.half_value(ev.template get(), ev.tag(), context); + case staj_event_type::double_value: + return visitor.double_value(ev.template get(), ev.tag(), context); + default: + return false; + } +} + +// basic_staj_cursor + +template +class basic_staj_cursor +{ +public: + virtual ~basic_staj_cursor() noexcept = default; + + virtual void array_expected(std::error_code& ec) + { + if (!(current().event_type() == staj_event_type::begin_array || current().event_type() == staj_event_type::byte_string_value)) + { + ec = conv_errc::not_vector; + } + } + + virtual bool done() const = 0; + + virtual const basic_staj_event& current() const = 0; + + virtual void read_to(basic_json_visitor& visitor) = 0; + + virtual void read_to(basic_json_visitor& visitor, + std::error_code& ec) = 0; + + virtual void next() = 0; + + virtual void next(std::error_code& ec) = 0; + + virtual const ser_context& context() const = 0; +}; + +template +class basic_staj_filter_view : basic_staj_cursor +{ + basic_staj_cursor* cursor_; + std::function&, const ser_context&)> pred_; +public: + basic_staj_filter_view(basic_staj_cursor& cursor, + std::function&, const ser_context&)> pred) + : cursor_(std::addressof(cursor)), pred_(pred) + { + while (!done() && !pred_(current(),context())) + { + cursor_->next(); + } + } + + bool done() const override + { + return cursor_->done(); + } + + const basic_staj_event& current() const override + { + return cursor_->current(); + } + + void read_to(basic_json_visitor& visitor) override + { + cursor_->read_to(visitor); + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + cursor_->read_to(visitor, ec); + } + + void next() override + { + cursor_->next(); + while (!done() && !pred_(current(),context())) + { + cursor_->next(); + } + } + + void next(std::error_code& ec) override + { + cursor_->next(ec); + while (!done() && !pred_(current(),context()) && !ec) + { + cursor_->next(ec); + } + } + + const ser_context& context() const override + { + return cursor_->context(); + } + + friend + basic_staj_filter_view operator|(basic_staj_filter_view& cursor, + std::function&, const ser_context&)> pred) + { + return basic_staj_filter_view(cursor, pred); + } +}; + +using staj_event = basic_staj_event; +using wstaj_event = basic_staj_event; + +using staj_cursor = basic_staj_cursor; +using wstaj_cursor = basic_staj_cursor; + +using staj_filter_view = basic_staj_filter_view; +using wstaj_filter_view = basic_staj_filter_view; + +#if !defined(JSONCONS_NO_DEPRECATED) + +JSONCONS_DEPRECATED_MSG("Instead, use staj_event_type") typedef staj_event_type stream_event_type; + +template +using basic_stream_event = basic_staj_event; + +template +using basic_stream_reader = basic_staj_cursor; + +template +using basic_staj_reader = basic_staj_cursor; + +JSONCONS_DEPRECATED_MSG("Instead, use staj_event") typedef basic_staj_event stream_event; +JSONCONS_DEPRECATED_MSG("Instead, use wstaj_event") typedef basic_staj_event wstream_event; + +JSONCONS_DEPRECATED_MSG("Instead, use staj_cursor") typedef basic_staj_cursor stream_reader; +JSONCONS_DEPRECATED_MSG("Instead, use wstaj_cursor") typedef basic_staj_cursor wstream_reader; + +JSONCONS_DEPRECATED_MSG("Instead, use staj_cursor") typedef basic_staj_cursor staj_reader; +JSONCONS_DEPRECATED_MSG("Instead, use wstaj_cursor") typedef basic_staj_cursor wstaj_reader; + +#endif + +} + +#endif + diff --git a/include/jsoncons/staj_iterator.hpp b/include/jsoncons/staj_iterator.hpp new file mode 100644 index 0000000..a6897f7 --- /dev/null +++ b/include/jsoncons/staj_iterator.hpp @@ -0,0 +1,449 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_STAJ_ITERATOR_HPP +#define JSONCONS_STAJ_ITERATOR_HPP + +#include // placement new +#include +#include +#include +#include +#include +#include // std::input_iterator_tag +#include +#include +#include +#include + +namespace jsoncons { + + template + class staj_array_view; + + template + class staj_array_iterator + { + using char_type = typename Json::char_type; + + staj_array_view* view_; + std::exception_ptr eptr_; + + public: + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + using iterator_category = std::input_iterator_tag; + + staj_array_iterator() noexcept + : view_(nullptr) + { + } + + staj_array_iterator(staj_array_view& view) + : view_(std::addressof(view)) + { + if (view_->cursor_->current().event_type() == staj_event_type::begin_array) + { + next(); + } + else + { + view_->cursor_ = nullptr; + } + } + + staj_array_iterator(staj_array_view& view, + std::error_code& ec) + : view_(std::addressof(view)) + { + if (view_->cursor_->current().event_type() == staj_event_type::begin_array) + { + next(ec); + if (ec) {view_ = nullptr;} + } + else + { + view_ = nullptr; + } + } + + ~staj_array_iterator() noexcept + { + } + + bool has_value() const + { + return !eptr_; + } + + const T& operator*() const + { + if (eptr_) + { + std::rethrow_exception(eptr_); + } + else + { + return *view_->value_; + } + } + + const T* operator->() const + { + if (eptr_) + { + std::rethrow_exception(eptr_); + } + else + { + return view_->value_.operator->(); + } + } + + staj_array_iterator& operator++() + { + next(); + return *this; + } + + staj_array_iterator& increment(std::error_code& ec) + { + next(ec); + if (ec) {view_ = nullptr;} + return *this; + } + + staj_array_iterator operator++(int) // postfix increment + { + staj_array_iterator temp(*this); + next(); + return temp; + } + + friend bool operator==(const staj_array_iterator& a, const staj_array_iterator& b) + { + return (!a.view_ && !b.view_) + || (!a.view_ && b.done()) + || (!b.view_ && a.done()); + } + + friend bool operator!=(const staj_array_iterator& a, const staj_array_iterator& b) + { + return !(a == b); + } + + private: + + bool done() const + { + return view_->cursor_->done() || view_->cursor_->current().event_type() == staj_event_type::end_array; + } + + void next() + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column())); + } + } + + void next(std::error_code& ec) + { + using char_type = typename Json::char_type; + + if (!done()) + { + view_->cursor_->next(ec); + if (ec) + { + return; + } + if (!done()) + { + eptr_ = std::exception_ptr(); + JSONCONS_TRY + { + view_->value_ = decode_traits::decode(*view_->cursor_, view_->decoder_, ec); + } + JSONCONS_CATCH(const conv_error&) + { + eptr_ = std::current_exception(); + } + } + } + } + }; + + template + class staj_object_view; + + template + class staj_object_iterator + { + using char_type = typename Json::char_type; + + staj_object_view* view_; + std::exception_ptr eptr_; + public: + using key_type = std::basic_string; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + + public: + + staj_object_iterator() noexcept + : view_(nullptr) + { + } + + staj_object_iterator(staj_object_view& view) + : view_(std::addressof(view)) + { + if (view_->cursor_->current().event_type() == staj_event_type::begin_object) + { + next(); + } + else + { + view_ = nullptr; + } + } + + staj_object_iterator(staj_object_view& view, + std::error_code& ec) + : view_(std::addressof(view)) + { + if (view_->cursor_->current().event_type() == staj_event_type::begin_object) + { + next(ec); + if (ec) {view_ = nullptr;} + } + else + { + view_ = nullptr; + } + } + + ~staj_object_iterator() noexcept + { + } + + bool has_value() const + { + return !eptr_; + } + + const value_type& operator*() const + { + if (eptr_) + { + std::rethrow_exception(eptr_); + } + else + { + return *view_->key_value_; + } + } + + const value_type* operator->() const + { + if (eptr_) + { + std::rethrow_exception(eptr_); + } + else + { + return view_->key_value_.operator->(); + } + } + + staj_object_iterator& operator++() + { + next(); + return *this; + } + + staj_object_iterator& increment(std::error_code& ec) + { + next(ec); + if (ec) + { + view_ = nullptr; + } + return *this; + } + + staj_object_iterator operator++(int) // postfix increment + { + staj_object_iterator temp(*this); + next(); + return temp; + } + + friend bool operator==(const staj_object_iterator& a, const staj_object_iterator& b) + { + return (!a.view_ && !b.view_) + || (!a.view_ && b.done()) + || (!b.view_ && a.done()); + } + + friend bool operator!=(const staj_object_iterator& a, const staj_object_iterator& b) + { + return !(a == b); + } + + private: + + bool done() const + { + return view_->cursor_->done() || view_->cursor_->current().event_type() == staj_event_type::end_object; + } + + void next() + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column())); + } + } + + void next(std::error_code& ec) + { + using char_type = typename Json::char_type; + + view_->cursor_->next(ec); + if (ec) + { + return; + } + if (!done()) + { + JSONCONS_ASSERT(view_->cursor_->current().event_type() == staj_event_type::key); + auto key = view_->cursor_->current(). template get(); + view_->cursor_->next(ec); + if (ec) + { + return; + } + if (!done()) + { + eptr_ = std::exception_ptr(); + JSONCONS_TRY + { + view_->key_value_ = value_type(std::move(key),decode_traits::decode(*view_->cursor_, view_->decoder_, ec)); + } + JSONCONS_CATCH(const conv_error&) + { + eptr_ = std::current_exception(); + } + } + } + } + }; + + // staj_array_view + + template + class staj_array_view + { + friend class staj_array_iterator; + public: + using char_type = typename Json::char_type; + using iterator = staj_array_iterator; + private: + basic_staj_cursor* cursor_; + json_decoder decoder_; + jsoncons::optional value_; + public: + staj_array_view(basic_staj_cursor& cursor) + : cursor_(std::addressof(cursor)) + { + } + + iterator begin() + { + return staj_array_iterator(*this); + } + + iterator end() + { + return staj_array_iterator(); + } + }; + + // staj_object_view + + template + class staj_object_view + { + friend class staj_object_iterator; + public: + using char_type = typename Json::char_type; + using iterator = staj_object_iterator; + using key_type = std::basic_string; + using value_type = std::pair; + private: + basic_staj_cursor* cursor_; + json_decoder decoder_; + jsoncons::optional key_value_; + public: + staj_object_view(basic_staj_cursor& cursor) + : cursor_(std::addressof(cursor)) + { + } + + iterator begin() + { + return staj_object_iterator(*this); + } + + iterator end() + { + return staj_object_iterator(); + } + }; + + template ::value,T,basic_json>::type> + staj_array_view staj_array(basic_staj_cursor& cursor) + { + return staj_array_view(cursor); + } + + template ::value,T,basic_json>::type> + staj_object_view staj_object(basic_staj_cursor& cursor) + { + return staj_object_view(cursor); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + template ::value,T,basic_json>::type> + JSONCONS_DEPRECATED_MSG("Instead, use staj_array()") + staj_array_view make_array_iterator(basic_staj_cursor& cursor) + { + return staj_array_view(cursor); + } + + template ::value,T,basic_json>::type> + JSONCONS_DEPRECATED_MSG("Instead, use staj_object()") + staj_object_view, T, Json> make_object_iterator(basic_staj_cursor& cursor) + { + return staj_object_view, T, Json>(cursor); + } +#endif + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/tag_type.hpp b/include/jsoncons/tag_type.hpp new file mode 100644 index 0000000..ffefd8a --- /dev/null +++ b/include/jsoncons/tag_type.hpp @@ -0,0 +1,245 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TAG_TYPE_HPP +#define JSONCONS_TAG_TYPE_HPP + +#include +#include + +namespace jsoncons { + +struct null_type +{ + explicit null_type() = default; +}; + +struct temp_allocator_arg_t +{ + explicit temp_allocator_arg_t() = default; +}; + +constexpr temp_allocator_arg_t temp_allocator_arg{}; + +struct result_allocator_arg_t +{ + explicit result_allocator_arg_t() = default; +}; + +constexpr result_allocator_arg_t result_allocator_arg{}; + +struct half_arg_t +{ + explicit half_arg_t() = default; +}; + +constexpr half_arg_t half_arg{}; + +struct json_array_arg_t +{ + explicit json_array_arg_t() = default; +}; + +constexpr json_array_arg_t json_array_arg{}; + +struct json_object_arg_t +{ + explicit json_object_arg_t() = default; +}; + +constexpr json_object_arg_t json_object_arg{}; + +struct byte_string_arg_t +{ + explicit byte_string_arg_t() = default; +}; + +constexpr byte_string_arg_t byte_string_arg{}; + +struct json_const_pointer_arg_t +{ + explicit json_const_pointer_arg_t() = default; +}; + +constexpr json_const_pointer_arg_t json_const_pointer_arg{}; + +enum class semantic_tag : uint8_t +{ + none = 0, + undefined = 0x01, + datetime = 0x02, + epoch_second = 0x03, + epoch_milli = 0x04, + epoch_nano = 0x05, + bigint = 0x06, + bigdec = 0x07, + bigfloat = 0x08, + float128 = 0x09, + base16 = 0x1a, + base64 = 0x1b, + base64url = 0x1c, + uri = 0x0d, + clamped = 0x0e, + multi_dim_row_major = 0x0f, + multi_dim_column_major = 0x10, + ext = 0x11, + id = 0x12, + regex = 0x13, + code = 0x14 +#if !defined(JSONCONS_NO_DEPRECATED) + , big_integer = bigint + , big_decimal = bigdec + , big_float = bigfloat + , date_time = datetime + , timestamp = epoch_second +#endif +}; + +template +std::basic_ostream& operator<<(std::basic_ostream& os, semantic_tag tag) +{ + static constexpr const CharT* na_name = JSONCONS_CSTRING_CONSTANT(CharT, "n/a"); + static constexpr const CharT* undefined_name = JSONCONS_CSTRING_CONSTANT(CharT, "undefined"); + static constexpr const CharT* datetime_name = JSONCONS_CSTRING_CONSTANT(CharT, "datetime"); + static constexpr const CharT* epoch_second_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-second"); + static constexpr const CharT* epoch_milli_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-milli"); + static constexpr const CharT* epoch_nano_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-nano"); + static constexpr const CharT* bigint_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigint"); + static constexpr const CharT* bigdec_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigdec"); + static constexpr const CharT* bigfloat_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigfloat"); + static constexpr const CharT* base16_name = JSONCONS_CSTRING_CONSTANT(CharT, "base16"); + static constexpr const CharT* base64_name = JSONCONS_CSTRING_CONSTANT(CharT, "base64"); + static constexpr const CharT* base64url_name = JSONCONS_CSTRING_CONSTANT(CharT, "base64url"); + static constexpr const CharT* uri_name = JSONCONS_CSTRING_CONSTANT(CharT, "uri"); + static constexpr const CharT* clamped_name = JSONCONS_CSTRING_CONSTANT(CharT, "clamped"); + static constexpr const CharT* multi_dim_row_major_name = JSONCONS_CSTRING_CONSTANT(CharT, "multi-dim-row-major"); + static constexpr const CharT* multi_dim_column_major_name = JSONCONS_CSTRING_CONSTANT(CharT, "multi-dim-column-major"); + static constexpr const CharT* ext_name = JSONCONS_CSTRING_CONSTANT(CharT, "ext"); + static constexpr const CharT* id_name = JSONCONS_CSTRING_CONSTANT(CharT, "id"); + static constexpr const CharT* float128_name = JSONCONS_CSTRING_CONSTANT(CharT, "float128"); + static constexpr const CharT* regex_name = JSONCONS_CSTRING_CONSTANT(CharT, "regex"); + static constexpr const CharT* code_name = JSONCONS_CSTRING_CONSTANT(CharT, "code"); + + switch (tag) + { + case semantic_tag::none: + { + os << na_name; + break; + } + case semantic_tag::undefined: + { + os << undefined_name; + break; + } + case semantic_tag::datetime: + { + os << datetime_name; + break; + } + case semantic_tag::epoch_second: + { + os << epoch_second_name; + break; + } + case semantic_tag::epoch_milli: + { + os << epoch_milli_name; + break; + } + case semantic_tag::epoch_nano: + { + os << epoch_nano_name; + break; + } + case semantic_tag::bigint: + { + os << bigint_name; + break; + } + case semantic_tag::bigdec: + { + os << bigdec_name; + break; + } + case semantic_tag::bigfloat: + { + os << bigfloat_name; + break; + } + case semantic_tag::float128: + { + os << float128_name; + break; + } + case semantic_tag::base16: + { + os << base16_name; + break; + } + case semantic_tag::base64: + { + os << base64_name; + break; + } + case semantic_tag::base64url: + { + os << base64url_name; + break; + } + case semantic_tag::uri: + { + os << uri_name; + break; + } + case semantic_tag::clamped: + { + os << clamped_name; + break; + } + case semantic_tag::multi_dim_row_major: + { + os << multi_dim_row_major_name; + break; + } + case semantic_tag::multi_dim_column_major: + { + os << multi_dim_column_major_name; + break; + } + case semantic_tag::ext: + { + os << ext_name; + break; + } + case semantic_tag::id: + { + os << id_name; + break; + } + case semantic_tag::regex: + { + os << regex_name; + break; + } + case semantic_tag::code: + { + os << code_name; + break; + } + } + return os; +} + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use semantic_tag") typedef semantic_tag semantic_tag_type; + JSONCONS_DEPRECATED_MSG("Instead, use byte_string_arg_t") typedef byte_string_arg_t bstr_arg_t; + constexpr byte_string_arg_t bstr_arg{}; +#endif + +} + +#endif diff --git a/include/jsoncons/text_source_adaptor.hpp b/include/jsoncons/text_source_adaptor.hpp new file mode 100644 index 0000000..491e8a6 --- /dev/null +++ b/include/jsoncons/text_source_adaptor.hpp @@ -0,0 +1,144 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TEXT_SOURCE_ADAPTOR_HPP +#define JSONCONS_TEXT_SOURCE_ADAPTOR_HPP + +#include +#include +#include +#include +#include +#include // std::allocator_traits +#include // std::vector +#include +#include // json_errc +#include +#include + +namespace jsoncons { + + // unicode_source_adaptor + + template + class unicode_source_adaptor + { + public: + using value_type = typename Source::value_type; + using source_type = Source; + private: + source_type source_; + bool bof_; + + + public: + template + unicode_source_adaptor(Sourceable&& source) + : source_(std::forward(source)), + bof_(true) + { + } + + bool is_error() const + { + return source_.is_error(); + } + + bool eof() const + { + return source_.eof(); + } + + span read_buffer(std::error_code& ec) + { + if (source_.eof()) + { + return span(); + } + + auto s = source_.read_buffer(); + const value_type* data = s.data(); + std::size_t length = s.size(); + + if (bof_ && length > 0) + { + auto r = unicode_traits::detect_encoding_from_bom(data, length); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return; + } + length -= (r.ptr - data); + data = r.ptr; + bof_ = false; + } + return span(data, length); + } + }; + + // json_source_adaptor + + template + class json_source_adaptor + { + public: + using value_type = typename Source::value_type; + using value_type = typename Source::value_type; + using source_type = Source; + private: + source_type source_; + bool bof_; + + public: + + template + json_source_adaptor(Sourceable&& source) + : source_(std::forward(source)), + bof_(true) + { + } + + bool is_error() const + { + return source_.is_error(); + } + + bool eof() const + { + return source_.eof(); + } + + span read_buffer(std::error_code& ec) + { + if (source_.eof()) + { + return span(); + } + + auto s = source_.read_buffer(); + const value_type* data = s.data(); + std::size_t length = s.size(); + + if (bof_ && length > 0) + { + auto r = unicode_traits::detect_json_encoding(data, length); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return span(); + } + length -= (r.ptr - data); + data = r.ptr; + bof_ = false; + } + return span(data, length); + } + }; + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/typed_array_view.hpp b/include/jsoncons/typed_array_view.hpp new file mode 100644 index 0000000..d1b4906 --- /dev/null +++ b/include/jsoncons/typed_array_view.hpp @@ -0,0 +1,250 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TYPED_ARRAY_VIEW_HPP +#define JSONCONS_TYPED_ARRAY_VIEW_HPP + +#include // std::allocator +#include +#include +#include +#include +#include // std::enable_if +#include // std::array +#include // std::function +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + + struct uint8_array_arg_t {explicit uint8_array_arg_t() = default; }; + constexpr uint8_array_arg_t uint8_array_arg = uint8_array_arg_t(); + struct uint16_array_arg_t {explicit uint16_array_arg_t() = default; }; + struct uint32_array_arg_t {explicit uint32_array_arg_t() = default; }; + constexpr uint32_array_arg_t uint32_array_arg = uint32_array_arg_t(); + struct uint64_array_arg_t {explicit uint64_array_arg_t() = default; }; + constexpr uint64_array_arg_t uint64_array_arg = uint64_array_arg_t(); + struct int8_array_arg_t {explicit int8_array_arg_t() = default; }; + constexpr int8_array_arg_t int8_array_arg = int8_array_arg_t(); + struct int16_array_arg_t {explicit int16_array_arg_t() = default; }; + constexpr int16_array_arg_t int16_array_arg = int16_array_arg_t(); + struct int32_array_arg_t {explicit int32_array_arg_t() = default; }; + constexpr int32_array_arg_t int32_array_arg = int32_array_arg_t(); + struct int64_array_arg_t {explicit int64_array_arg_t() = default; }; + constexpr int64_array_arg_t int64_array_arg = int64_array_arg_t(); + constexpr uint16_array_arg_t uint16_array_arg = uint16_array_arg_t(); + struct half_array_arg_t {explicit half_array_arg_t() = default; }; + constexpr half_array_arg_t half_array_arg = half_array_arg_t(); + struct float_array_arg_t {explicit float_array_arg_t() = default; }; + constexpr float_array_arg_t float_array_arg = float_array_arg_t(); + struct double_array_arg_t {explicit double_array_arg_t() = default; }; + constexpr double_array_arg_t double_array_arg = double_array_arg_t(); + struct float128_array_arg_t {explicit float128_array_arg_t() = default; }; + constexpr float128_array_arg_t float128_array_arg = float128_array_arg_t(); + + enum class typed_array_type{uint8_value=1,uint16_value,uint32_value,uint64_value, + int8_value,int16_value,int32_value,int64_value, + half_value, float_value,double_value}; + + class typed_array_view + { + typed_array_type type_; + union + { + const uint8_t* uint8_data_; + const uint16_t* uint16_data_; + const uint32_t* uint32_data_; + const uint64_t* uint64_data_; + const int8_t* int8_data_; + const int16_t* int16_data_; + const int32_t* int32_data_; + const int64_t* int64_data_; + const float* float_data_; + const double* double_data_; + } data_; + std::size_t size_; + public: + + typed_array_view() + : type_(), data_(), size_(0) + { + } + + typed_array_view(const typed_array_view& other) + : type_(other.type_), data_(other.data_), size_(other.size()) + { + } + + typed_array_view(typed_array_view&& other) noexcept + { + swap(*this,other); + } + + typed_array_view(const uint8_t* data, std::size_t size) + : type_(typed_array_type::uint8_value), size_(size) + { + data_.uint8_data_ = data; + } + + typed_array_view(const uint16_t* data, std::size_t size) + : type_(typed_array_type::uint16_value), size_(size) + { + data_.uint16_data_ = data; + } + + typed_array_view(const uint32_t* data, std::size_t size) + : type_(typed_array_type::uint32_value), size_(size) + { + data_.uint32_data_ = data; + } + + typed_array_view(const uint64_t* data, std::size_t size) + : type_(typed_array_type::uint64_value), size_(size) + { + data_.uint64_data_ = data; + } + + typed_array_view(const int8_t* data, std::size_t size) + : type_(typed_array_type::int8_value), size_(size) + { + data_.int8_data_ = data; + } + + typed_array_view(const int16_t* data, std::size_t size) + : type_(typed_array_type::int16_value), size_(size) + { + data_.int16_data_ = data; + } + + typed_array_view(const int32_t* data, std::size_t size) + : type_(typed_array_type::int32_value), size_(size) + { + data_.int32_data_ = data; + } + + typed_array_view(const int64_t* data, std::size_t size) + : type_(typed_array_type::int64_value), size_(size) + { + data_.int64_data_ = data; + } + + typed_array_view(half_array_arg_t, const uint16_t* data, std::size_t size) + : type_(typed_array_type::half_value), size_(size) + { + data_.uint16_data_ = data; + } + + typed_array_view(const float* data, std::size_t size) + : type_(typed_array_type::float_value), size_(size) + { + data_.float_data_ = data; + } + + typed_array_view(const double* data, std::size_t size) + : type_(typed_array_type::double_value), size_(size) + { + data_.double_data_ = data; + } + + typed_array_view& operator=(const typed_array_view& other) + { + typed_array_view temp(other); + swap(*this,temp); + return *this; + } + + typed_array_type type() const {return type_;} + + std::size_t size() const + { + return size_; + } + + jsoncons::span data(uint8_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::uint8_value); + return jsoncons::span(data_.uint8_data_, size_); + } + + jsoncons::span data(uint16_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::uint16_value); + return jsoncons::span(data_.uint16_data_, size_); + } + + jsoncons::span data(uint32_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::uint32_value); + return jsoncons::span(data_.uint32_data_, size_); + } + + jsoncons::span data(uint64_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::uint64_value); + return jsoncons::span(data_.uint64_data_, size_); + } + + jsoncons::span data(int8_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::int8_value); + return jsoncons::span(data_.int8_data_, size_); + } + + jsoncons::span data(int16_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::int16_value); + return jsoncons::span(data_.int16_data_, size_); + } + + jsoncons::span data(int32_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::int32_value); + return jsoncons::span(data_.int32_data_, size_); + } + + jsoncons::span data(int64_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::int64_value); + return jsoncons::span(data_.int64_data_, size_); + } + + jsoncons::span data(half_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::half_value); + return jsoncons::span(data_.uint16_data_, size_); + } + + jsoncons::span data(float_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::float_value); + return jsoncons::span(data_.float_data_, size_); + } + + jsoncons::span data(double_array_arg_t) const + { + JSONCONS_ASSERT(type_ == typed_array_type::double_value); + return jsoncons::span(data_.double_data_, size_); + } + + friend void swap(typed_array_view& a, typed_array_view& b) noexcept + { + std::swap(a.data_,b.data_); + std::swap(a.type_,b.type_); + std::swap(a.size_,b.size_); + } + }; + +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons/unicode_traits.hpp b/include/jsoncons/unicode_traits.hpp new file mode 100644 index 0000000..f45bafe --- /dev/null +++ b/include/jsoncons/unicode_traits.hpp @@ -0,0 +1,1330 @@ +// Copyright 2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/unicode_traits for latest version + +/* + * Includes code derived from Unicode, Inc decomposition code in ConvertUTF.h and ConvertUTF.c + * http://www.unicode.org/ + * + * "Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard." +*/ + +#ifndef JSONCONS_UNICODE_TRAITS_HPP +#define JSONCONS_UNICODE_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace unicode_traits { + + enum class encoding_kind {undetected,utf8,utf16le,utf16be,utf32le,utf32be}; + + inline + std::string to_string(encoding_kind encoding) + { + switch (encoding) + { + case encoding_kind::utf8: + return "utf8"; + case encoding_kind::utf16le: + return "utf16le"; + case encoding_kind::utf16be: + return "utf16be"; + case encoding_kind::utf32le: + return "utf32le"; + case encoding_kind::utf32be: + return "utf32be"; + default: + return "undetected"; + } + } + + template + struct detect_encoding_result + { + const Byte* ptr; + encoding_kind encoding; + }; + + template + typename std::enable_if::value,detect_encoding_result>::type + detect_encoding_from_bom(const CharT* data, std::size_t length) + { + const uint8_t bom_utf8[] = {0xef,0xbb,0xbf}; + const uint8_t bom_utf16le[] = {0xff,0xfe}; + const uint8_t bom_utf16be[] = {0xfe,0xff}; + const uint8_t bom_utf32le[] = {0xff,0xfe,0x00,0x00}; + const uint8_t bom_utf32be[] = {0x00,0x00,0xfe,0xff}; + + if (length >= 4 && !memcmp(data,bom_utf32le,4)) + { + return detect_encoding_result{data+4,encoding_kind::utf32le}; + } + else if (length >= 4 && !memcmp(data,bom_utf32be,4)) + { + return detect_encoding_result{data+4,encoding_kind::utf32be}; + } + else if (length >= 2 && !memcmp(data,bom_utf16le,2)) + { + return detect_encoding_result{data+2,encoding_kind::utf16le}; + } + else if (length >= 2 && !memcmp(data,bom_utf16be,2)) + { + return detect_encoding_result{data+2,encoding_kind::utf16be}; + } + else if (length >= 3 && !memcmp(data,bom_utf8,3)) + { + return detect_encoding_result{data+3,encoding_kind::utf8}; + } + else + { + return detect_encoding_result{data,encoding_kind::undetected}; + } + } + + template + typename std::enable_if::value || type_traits::is_char32::value,detect_encoding_result>::type + detect_encoding_from_bom(const CharT* data, std::size_t) + { + return detect_encoding_result{data,encoding_kind::undetected}; + } + + template + typename std::enable_if::value,detect_encoding_result>::type + detect_json_encoding(const CharT* data, std::size_t length) + { + detect_encoding_result r = detect_encoding_from_bom(data,length); + if (r.encoding != encoding_kind::undetected) + { + return r; + } + else if (length < 4) + { + return detect_encoding_result{data,encoding_kind::utf8}; + } + else if (*data == 0 && *(data+1) == 0 && *(data+2) == 0) + { + return detect_encoding_result{data,encoding_kind::utf32be}; + } + else if (*data == 0 && *(data+2) == 0) + { + return detect_encoding_result{data,encoding_kind::utf16be}; + } + else if (*(data+1) == 0 && *(data+2) == 0 && *(data+3) == 0) + { + return detect_encoding_result{data,encoding_kind::utf32le}; + } + else if (*(data+1) == 0 && *(data+3) == 0) + { + return detect_encoding_result{data,encoding_kind::utf16le}; + } + else + { + return detect_encoding_result{data,encoding_kind::utf8}; + } + } + + template + typename std::enable_if::value || type_traits::is_char32::value,detect_encoding_result>::type + detect_json_encoding(const CharT* data, std::size_t) + { + return detect_encoding_result{data,encoding_kind::undetected}; + } + + /* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. Source: ConvertUTF.c + */ + const uint32_t offsets_from_utf8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + + /* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. Source: ConvertUTF.c + */ + const uint8_t first_byte_mark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + /* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. Source: ConvertUTF.c + */ + const uint8_t trailing_bytes_for_utf8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 + }; + + // Some fundamental constants. Source: ConvertUTF.h + const uint32_t replacement_char = 0x0000FFFD; + const uint32_t max_bmp = 0x0000FFFF; + const uint32_t max_utf16 = 0x0010FFFF; + const uint32_t max_utf32 = 0x7FFFFFFF; + const uint32_t max_legal_utf32 = 0x0010FFFF; + + const int half_shift = 10; // used for shifting by 10 bits + const uint32_t half_base = 0x0010000UL; + const uint32_t half_mask = 0x3FFUL; + + const uint16_t sur_high_start = 0xD800; + const uint16_t sur_high_end = 0xDBFF; + const uint16_t sur_low_start = 0xDC00; + const uint16_t sur_low_end = 0xDFFF; + + inline + static bool is_continuation_byte(unsigned char ch) + { + return (ch & 0xC0) == 0x80; + } + + inline + bool is_high_surrogate(uint32_t ch) noexcept + { + return (ch >= sur_high_start && ch <= sur_high_end); + } + + inline + bool is_low_surrogate(uint32_t ch) noexcept + { + return (ch >= sur_low_start && ch <= sur_low_end); + } + + inline + bool is_surrogate(uint32_t ch) noexcept + { + return (ch >= sur_high_start && ch <= sur_low_end); + } + + enum class conv_flags + { + strict = 0, + lenient + }; + + // conv_errc + + enum class conv_errc + { + success = 0, + over_long_utf8_sequence = 1, // over long utf8 sequence + expected_continuation_byte, // expected continuation byte + unpaired_high_surrogate, // unpaired high surrogate UTF-16 + illegal_surrogate_value, // UTF-16 surrogate values are illegal in UTF-32 + source_exhausted, // partial character in source, but hit end + source_illegal // source sequence is illegal/malformed + }; + + class Unicode_traits_error_category_impl_ + : public std::error_category + { + public: + virtual const char* name() const noexcept + { + return "unicode_traits conversion error"; + } + virtual std::string message(int ev) const + { + switch (static_cast(ev)) + { + case conv_errc::over_long_utf8_sequence: + return "Over long utf8 sequence"; + case conv_errc::expected_continuation_byte: + return "Expected continuation byte"; + case conv_errc::unpaired_high_surrogate: + return "Unpaired high surrogate UTF-16"; + case conv_errc::illegal_surrogate_value: + return "UTF-16 surrogate values are illegal in UTF-32"; + case conv_errc::source_exhausted: + return "Partial character in source, but hit end"; + case conv_errc::source_illegal: + return "Source sequence is illegal/malformed"; + default: + return ""; + break; + } + } + }; + + inline + const std::error_category& unicode_traits_error_category() + { + static Unicode_traits_error_category_impl_ instance; + return instance; + } + + inline + std::error_code make_error_code(conv_errc result) + { + return std::error_code(static_cast(result),unicode_traits_error_category()); + } + +} // unicode_traits +} // jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace jsoncons { namespace unicode_traits { + + // utf8 + + template + typename std::enable_if::value, conv_errc>::type + is_legal_utf8(const CharT* first, std::size_t length) + { + uint8_t a; + const CharT* srcptr = first+length; + switch (length) { + default: + return conv_errc::over_long_utf8_sequence; + case 4: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + JSONCONS_FALLTHROUGH; + case 3: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + JSONCONS_FALLTHROUGH; + case 2: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + + switch (static_cast(*first)) + { + // no fall-through in this inner switch + case 0xE0: if (a < 0xA0) return conv_errc::source_illegal; break; + case 0xED: if (a > 0x9F) return conv_errc::source_illegal; break; + case 0xF0: if (a < 0x90) return conv_errc::source_illegal; break; + case 0xF4: if (a > 0x8F) return conv_errc::source_illegal; break; + default: if (a < 0x80) return conv_errc::source_illegal; + } + + JSONCONS_FALLTHROUGH; + case 1: + if (static_cast(*first) >= 0x80 && static_cast(*first) < 0xC2) + return conv_errc::source_illegal; + break; + } + if (static_cast(*first) > 0xF4) + return conv_errc::source_illegal; + + return conv_errc(); + } + + template using void_t = void; + + template + struct is_output_iterator : std::false_type {}; + + template + struct is_output_iterator::iterator_category, + decltype(*std::declval() = std::declval())>> : std::true_type {}; + + // is_same_size fixes issue with vs2013 + + // primary template + template + struct is_same_size : std::false_type + { + }; + + // specialization for non void types + template + struct is_same_size::value && !std::is_void::value>::type> + { + static constexpr bool value = (sizeof(T1) == sizeof(T2)); + }; + + // convert + + template + struct convert_result + { + const CharT* ptr; + conv_errc ec; + }; + + // to_codepoint + + template + typename std::enable_if::value && type_traits::is_char32::value, + convert_result>::type + to_codepoint(const CharT* first, const CharT* last, + CodepointT& ch, + conv_flags flags = conv_flags::strict) noexcept + { + ch = 0; + if (first >= last) + { + return convert_result{first, conv_errc::source_exhausted}; + } + conv_errc result = conv_errc(); + + unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast(*first)]; + if (extra_bytes_to_read >= last - first) + { + result = conv_errc::source_exhausted; + return convert_result{first, result}; + } + // Do this check whether lenient or strict + if ((result=is_legal_utf8(first, extra_bytes_to_read+1)) != conv_errc()) + { + return convert_result{first, result}; + } + // The cases all fall through. See "Note A" below. + switch (extra_bytes_to_read) + { + case 5: + ch += static_cast(*first++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 4: + ch += static_cast(*first++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 3: + ch += static_cast(*first++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 2: + ch += static_cast(*first++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 1: + ch += static_cast(*first++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 0: + ch += static_cast(*first++); + break; + } + ch -= offsets_from_utf8[extra_bytes_to_read]; + + if (ch <= max_legal_utf32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (is_surrogate(ch) ) + { + if (flags == conv_flags::strict) + { + first -= (extra_bytes_to_read+1); // return to the illegal value itself + result = conv_errc::source_illegal; + return convert_result{first, result}; + } + else + { + ch = replacement_char; + } + } + } + else // i.e., ch > max_legal_utf32 + { + result = conv_errc::source_illegal; + ch = replacement_char; + } + + return convert_result{first,result} ; + } + + template + typename std::enable_if::value && type_traits::is_char32::value, + convert_result>::type + to_codepoint(const CharT* first, const CharT* last, + CodepointT& ch, + conv_flags flags = conv_flags::strict) noexcept + { + ch = 0; + if (first >= last) + { + return convert_result{first, conv_errc::source_exhausted}; + } + conv_errc result = conv_errc(); + + ch = *first++; + // If we have a surrogate pair, convert to UTF32 first. + if (is_high_surrogate(ch)) + { + // If the 16 bits following the high surrogate are in the first buffer... + if (first < last) + { + uint32_t ch2 = *first; + // If ptr's a low surrogate, convert to UTF32. + if (ch2 >= sur_low_start && ch2 <= sur_low_end ) + { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + ++first; + } + else if (flags == conv_flags::strict) // ptr's an unpaired high surrogate + { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + return convert_result{first, result}; + } + } + else + { /* We don't have the 16 bits following the high surrogate. */ + --first; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + return convert_result{first, result}; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch) ) + { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + return convert_result{first, result}; + } + } + + return convert_result{first,result} ; + } + + template + typename std::enable_if::value && type_traits::is_char32::value, + convert_result>::type + to_codepoint(const CharT* first, const CharT* last, + CodepointT& ch, + conv_flags flags = conv_flags::strict) noexcept + { + ch = 0; + if (first >= last) + { + return convert_result{first, conv_errc::source_exhausted}; + } + conv_errc result = conv_errc(); + + ch = *first++; + if (flags == conv_flags::strict ) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) + { + --first; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + return convert_result{first,result} ; + } + } + if (!(ch <= max_legal_utf32)) + { + ch = replacement_char; + result = conv_errc::source_illegal; + } + + return convert_result{first,result} ; + } + + // convert + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char8::value, + convert_result>::type + convert(const CharT* data, std::size_t length, Container& target, conv_flags flags=conv_flags::strict) + { + (void)flags; + + conv_errc result = conv_errc(); + const CharT* last = data + length; + while (data != last) + { + std::size_t len = trailing_bytes_for_utf8[static_cast(*data)] + 1; + if (len > (std::size_t)(last - data)) + { + return convert_result{data, conv_errc::source_exhausted}; + } + if ((result=is_legal_utf8(data, len)) != conv_errc()) + { + return convert_result{data,result}; + } + + switch (len) { + case 4: target.push_back(static_cast(*data++)); + JSONCONS_FALLTHROUGH; + case 3: target.push_back(static_cast(*data++)); + JSONCONS_FALLTHROUGH; + case 2: target.push_back(static_cast(*data++)); + JSONCONS_FALLTHROUGH; + case 1: target.push_back(static_cast(*data++)); + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char16::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast(*data)]; + if (extra_bytes_to_read >= last - data) + { + result = conv_errc::source_exhausted; + break; + } + /* Do this check whether lenient or strict */ + if ((result=is_legal_utf8(data, extra_bytes_to_read+1)) != conv_errc()) + { + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + uint32_t ch = 0; + switch (extra_bytes_to_read) { + case 5: ch += static_cast(*data++); ch <<= 6; /* remember, illegal UTF-8 */ + JSONCONS_FALLTHROUGH; + case 4: ch += static_cast(*data++); ch <<= 6; /* remember, illegal UTF-8 */ + JSONCONS_FALLTHROUGH; + case 3: ch += static_cast(*data++); ch <<= 6; + JSONCONS_FALLTHROUGH; + case 2: ch += static_cast(*data++); ch <<= 6; + JSONCONS_FALLTHROUGH; + case 1: ch += static_cast(*data++); ch <<= 6; + JSONCONS_FALLTHROUGH; + case 0: ch += static_cast(*data++); + break; + } + ch -= offsets_from_utf8[extra_bytes_to_read]; + + if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch) ) + { + if (flags == conv_flags::strict) { + data -= (extra_bytes_to_read+1); /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + target.push_back(replacement_char); + } + } else { + target.push_back((uint16_t)ch); /* normal case */ + } + } else if (ch > max_utf16) { + if (flags == conv_flags::strict) { + result = conv_errc::source_illegal; + data -= (extra_bytes_to_read+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + target.push_back(replacement_char); + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + ch -= half_base; + target.push_back((uint16_t)((ch >> half_shift) + sur_high_start)); + target.push_back((uint16_t)((ch & half_mask) + sur_low_start)); + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char32::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data < last) + { + uint32_t ch = 0; + unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast(*data)]; + if (extra_bytes_to_read >= last - data) + { + result = conv_errc::source_exhausted; + break; + } + /* Do this check whether lenient or strict */ + if ((result=is_legal_utf8(data, extra_bytes_to_read+1)) != conv_errc()) + { + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extra_bytes_to_read) + { + case 5: + ch += static_cast(*data++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 4: + ch += static_cast(*data++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 3: + ch += static_cast(*data++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 2: + ch += static_cast(*data++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 1: + ch += static_cast(*data++); + ch <<= 6; + JSONCONS_FALLTHROUGH; + case 0: + ch += static_cast(*data++); + break; + } + ch -= offsets_from_utf8[extra_bytes_to_read]; + + if (ch <= max_legal_utf32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (is_surrogate(ch) ) + { + if (flags == conv_flags::strict) { + data -= (extra_bytes_to_read+1); /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + target.push_back(replacement_char); + } + } else { + target.push_back(ch); + } + } else { /* i.e., ch > max_legal_utf32 */ + result = conv_errc::source_illegal; + target.push_back(replacement_char); + } + } + return convert_result{data,result} ; + } + + // utf16 + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char8::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data < last) { + unsigned short bytes_to_write = 0; + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + uint32_t ch = *data++; + /* If we have a surrogate pair, convert to uint32_t data. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the data buffer... */ + if (data < last) { + uint32_t ch2 = *data; + /* If ptr's a low surrogate, convert to uint32_t. */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + ++data; + } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */ + --data; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --data; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch)) + { + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (uint32_t)0x80) { + bytes_to_write = 1; + } else if (ch < (uint32_t)0x800) { + bytes_to_write = 2; + } else if (ch < (uint32_t)0x10000) { + bytes_to_write = 3; + } else if (ch < (uint32_t)0x110000) { + bytes_to_write = 4; + } else { + bytes_to_write = 3; + ch = replacement_char; + } + + uint8_t byte1 = 0; + uint8_t byte2 = 0; + uint8_t byte3 = 0; + uint8_t byte4 = 0; + + switch (bytes_to_write) { // note: everything falls through + case 4: byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 3: byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 2: byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 1: byte1 = (uint8_t)(ch | first_byte_mark[bytes_to_write]); + break; + } + switch (bytes_to_write) + { + case 4: + target.push_back(byte1); + target.push_back(byte2); + target.push_back(byte3); + target.push_back(byte4); + break; + case 3: + target.push_back(byte1); + target.push_back(byte2); + target.push_back(byte3); + break; + case 2: + target.push_back(byte1); + target.push_back(byte2); + break; + case 1: + target.push_back(byte1); + break; + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char16::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + /* If we have a surrogate pair, convert to uint32_t data. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the data buffer... */ + if (data < last) { + uint32_t ch2 = *data; + /* If ptr's a low surrogate, */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + target.push_back((uint16_t)ch); + target.push_back((uint16_t)ch2); + ++data; + } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */ + --data; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --data; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (is_low_surrogate(ch)) + { + // illegal leading low surrogate + if (flags == conv_flags::strict) { + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + else + { + target.push_back((uint16_t)ch); + } + } + else + { + target.push_back((uint16_t)ch); + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char32::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + /* If we have a surrogate pair, convert to UTF32 data. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the data buffer... */ + if (data < last) { + uint32_t ch2 = *data; + /* If ptr's a low surrogate, convert to UTF32. */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end ) + { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + ++data; + } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */ + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --data; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch) ) + { + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + target.push_back(ch); + } + return convert_result{data,result} ; + } + + // utf32 + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char8::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + const CharT* last = data + length; + while (data < last) + { + unsigned short bytes_to_write = 0; + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + uint32_t ch = *data++; + if (flags == conv_flags::strict ) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) + { + --data; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (uint32_t)0x80) { bytes_to_write = 1; + } else if (ch < (uint32_t)0x800) { bytes_to_write = 2; + } else if (ch < (uint32_t)0x10000) { bytes_to_write = 3; + } else if (ch <= max_legal_utf32) { bytes_to_write = 4; + } else { + bytes_to_write = 3; + ch = replacement_char; + result = conv_errc::source_illegal; + } + + uint8_t byte1 = 0; + uint8_t byte2 = 0; + uint8_t byte3 = 0; + uint8_t byte4 = 0; + + switch (bytes_to_write) { + case 4: + byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 3: + byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 2: + byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + JSONCONS_FALLTHROUGH; + case 1: + byte1 = (uint8_t) (ch | first_byte_mark[bytes_to_write]); + break; + } + + switch (bytes_to_write) + { + case 4: + target.push_back(byte1); + target.push_back(byte2); + target.push_back(byte3); + target.push_back(byte4); + break; + case 3: + target.push_back(byte1); + target.push_back(byte2); + target.push_back(byte3); + break; + case 2: + target.push_back(byte1); + target.push_back(byte2); + break; + case 1: + target.push_back(byte1); + break; + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char16::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (is_surrogate(ch) ) + { + if (flags == conv_flags::strict) { + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + target.push_back(replacement_char); + } + } else { + target.push_back((uint16_t)ch); /* normal case */ + } + } else if (ch > max_legal_utf32) { + if (flags == conv_flags::strict) { + result = conv_errc::source_illegal; + } else { + target.push_back(replacement_char); + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + ch -= half_base; + target.push_back((uint16_t)((ch >> half_shift) + sur_high_start)); + target.push_back((uint16_t)((ch & half_mask) + sur_low_start)); + } + } + return convert_result{data,result} ; + } + + template + typename std::enable_if::value + && type_traits::is_back_insertable::value + && type_traits::is_char32::value, + convert_result>::type + convert(const CharT* data, std::size_t length, + Container& target, + conv_flags flags = conv_flags::strict) + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + if (flags == conv_flags::strict ) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) + { + --data; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + } + if (ch <= max_legal_utf32) + { + target.push_back(ch); + } + else + { + target.push_back(replacement_char); + result = conv_errc::source_illegal; + } + } + return convert_result{data,result} ; + } + + // validate + + template + typename std::enable_if::value, + convert_result>::type + validate(const CharT* data, std::size_t length) noexcept + { + conv_errc result = conv_errc(); + const CharT* last = data + length; + while (data != last) + { + std::size_t len = static_cast(trailing_bytes_for_utf8[static_cast(*data)]) + 1; + if (len > (std::size_t)(last - data)) + { + return convert_result{data, conv_errc::source_exhausted}; + } + if ((result=is_legal_utf8(data, len)) != conv_errc()) + { + return convert_result{data,result} ; + } + data += len; + } + return convert_result{data,result} ; + } + + // utf16 + + template + typename std::enable_if::value, + convert_result>::type + validate(const CharT* data, std::size_t length) noexcept + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + /* If we have a surrogate pair, validate to uint32_t data. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the data buffer... */ + if (data < last) { + uint32_t ch2 = *data; + /* If ptr's a low surrogate, */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + ++data; + } else { + --data; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } + else // We don't have the 16 bits following the high surrogate. + { + --data; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } + else if (is_low_surrogate(ch)) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + --data; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + return convert_result{data,result} ; + } + + // utf32 + + template + typename std::enable_if::value, + convert_result>::type + validate(const CharT* data, std::size_t length) noexcept + { + conv_errc result = conv_errc(); + + const CharT* last = data + length; + while (data != last) + { + uint32_t ch = *data++; + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) + { + --data; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + if (!(ch <= max_legal_utf32)) + { + result = conv_errc::source_illegal; + } + } + return convert_result{data, result} ; + } + + enum class encoding {u8,u16le,u16be,u32le,u32be,undetected}; + + template + struct determine_encoding_result + { + Iterator it; + encoding ec; + }; + + template + typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t), + determine_encoding_result>::type + detect_encoding(Iterator first, Iterator last) noexcept + { + Iterator it1 = first; + if (std::distance(first,last) < 4) + { + if (std::distance(first,last) == 3) + { + Iterator it2 = ++first; + Iterator it3 = ++first; + if (static_cast(*it1) == 0xEF && static_cast(*it2) == 0xBB && static_cast(*it3) == 0xBF) + { + return determine_encoding_result{last,encoding::u8}; + } + } + return determine_encoding_result{it1,encoding::undetected}; + } + else + { + Iterator it2 = ++first; + Iterator it3 = ++first; + Iterator it4 = ++first; + + uint32_t bom = static_cast(*it1) | (static_cast(*it2) << 8) | (static_cast(*it3) << 16) | (static_cast(*it4) << 24); + if (bom == 0xFFFE0000) + { + return determine_encoding_result{it4++,encoding::u32be}; + } + else if (bom == 0x0000FEFF) + { + return determine_encoding_result{first,encoding::u32le}; + } + else if ((bom & 0xFFFF) == 0xFFFE) + { + return determine_encoding_result{it3,encoding::u16be}; + } + else if ((bom & 0xFFFF) == 0xFEFF) + { + return determine_encoding_result{it3,encoding::u16le}; + } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) + { + return determine_encoding_result{it4,encoding::u8}; + } + else + { + uint32_t pattern = (static_cast(*it1) ? 1 : 0) | (static_cast(*it2) ? 2 : 0) | (static_cast(*it3) ? 4 : 0) | (static_cast(*it4) ? 8 : 0); + switch (pattern) { + case 0x08: + return determine_encoding_result{it1,encoding::u32be}; + case 0x0A: + return determine_encoding_result{it1,encoding::u16be}; + case 0x01: + return determine_encoding_result{it1,encoding::u32le}; + case 0x05: + return determine_encoding_result{it1,encoding::u16le}; + case 0x0F: + return determine_encoding_result{it1,encoding::u8}; + default: + return determine_encoding_result{it1,encoding::undetected}; + } + } + } + } + + // count_codepoints + + template + typename std::enable_if::value || type_traits::is_char16::value || type_traits::is_char32::value, std::size_t>::type + count_codepoints(const CharT* data, std::size_t length, + conv_flags flags = conv_flags::strict) noexcept + { + conv_errc ec = conv_errc(); + + std::size_t count = 0; + const CharT* ptr = data; + const CharT* last = data + length; + + for (; ptr < last; ++count) + { + uint32_t cp = 0; + auto r = to_codepoint(ptr, last, cp, flags); + if (r.ec != conv_errc()) + { + ec = r.ec; + break; + } + ptr = r.ptr; + } + return ec == conv_errc() && ptr == last ? count : 0; + } + +} // unicode_traits +} // jsoncons + +#endif + diff --git a/include/jsoncons/uri.hpp b/include/jsoncons/uri.hpp new file mode 100644 index 0000000..51249ef --- /dev/null +++ b/include/jsoncons/uri.hpp @@ -0,0 +1,635 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_URI_HPP +#define JSONCONS_URI_HPP + +#include // std::string +#include +#include +#include +#include + +namespace jsoncons { + + class uri + { + using part_type = std::pair; + + std::string uri_; + part_type scheme_; + part_type userinfo_; + part_type host_; + part_type port_; + part_type path_; + part_type query_; + part_type fragment_; + public: + + uri() = default; + + uri(const std::string& uri) + { + *this = parse(uri); + } + + uri(jsoncons::string_view scheme, + jsoncons::string_view userinfo, + jsoncons::string_view host, + jsoncons::string_view port, + jsoncons::string_view path, + jsoncons::string_view query, + jsoncons::string_view fragment) + { + if (!scheme.empty()) + { + uri_.append(std::string(scheme)); + scheme_.second = uri_.length(); + } + if (!userinfo.empty() || !host.empty() || !port.empty()) + { + if (!scheme.empty()) + { + uri_.append("://"); + } + + if (!userinfo.empty()) + { + userinfo_.first = uri_.length(); + uri_.append(std::string(userinfo)); + userinfo_.second = uri_.length(); + uri_.append("@"); + } + else + { + userinfo_.first = userinfo_.second = uri_.length(); + } + + if (!host.empty()) + { + host_.first = uri_.length(); + uri_.append(std::string(host)); + host_.second = uri_.length(); + } + else + { + JSONCONS_THROW(json_runtime_error("uri error.")); + } + + if (!port.empty()) + { + uri_.append(":"); + port_.first = uri_.length(); + uri_.append(std::string(port)); + port_.second = uri_.length(); + } + else + { + port_.first = port_.second = uri_.length(); + } + } + else + { + userinfo_.first = userinfo_.second = uri_.length(); + host_.first = host_.second = uri_.length(); + port_.first = port_.second = uri_.length(); + if (!scheme.empty()) + { + if (!path.empty() || !query.empty() || !fragment.empty()) + { + uri_.append(":"); + } + else + { + JSONCONS_THROW(json_runtime_error("uri error.")); + } + } + } + + if (!path.empty()) + { + // if the URI is not opaque and the path is not already prefixed + // with a '/', add one. + path_.first = uri_.length(); + if (!host.empty() && (path.front() != '/')) + { + uri_.push_back('/'); + } + uri_.append(std::string(path)); + path_.second = uri_.length(); + } + else + { + path_.first = path_.second = uri_.length(); + } + + if (!query.empty()) + { + uri_.append("?"); + query_.first = uri_.length(); + uri_.append(std::string(query)); + query_.second = uri_.length(); + } + else + { + query_.first = query_.second = uri_.length(); + } + + if (!fragment.empty()) + { + uri_.append("#"); + fragment_.first = uri_.length(); + uri_.append(std::string(fragment)); + fragment_.second = uri_.length(); + } + else + { + fragment_.first = fragment_.second = uri_.length(); + } + } + + const std::string& string() const + { + return uri_; + } + + bool is_absolute() const noexcept + { + return scheme_.first != scheme_.second; + } + + bool is_opaque() const noexcept + { + return is_absolute() && !authority().empty(); + } + + string_view base() const noexcept { return string_view(uri_.data()+scheme_.first,(path_.second-scheme_.first)); } + + string_view scheme() const noexcept { return string_view(uri_.data()+scheme_.first,(scheme_.second-scheme_.first)); } + + string_view userinfo() const noexcept { return string_view(uri_.data()+userinfo_.first,(userinfo_.second-userinfo_.first)); } + + string_view host() const noexcept { return string_view(uri_.data()+host_.first,(host_.second-host_.first)); } + + string_view port() const noexcept { return string_view(uri_.data()+port_.first,(port_.second-port_.first)); } + + string_view path() const noexcept { return string_view(uri_.data()+path_.first,(path_.second-path_.first)); } + + string_view query() const noexcept { return string_view(uri_.data()+query_.first,(query_.second-query_.first)); } + + string_view fragment() const noexcept { return string_view(uri_.data()+fragment_.first,(fragment_.second-fragment_.first)); } + + string_view authority() const noexcept { return string_view(uri_.data()+userinfo_.first,(port_.second-userinfo_.first)); } + + uri resolve(const uri& base) const + { + // This implementation uses the psuedo-code given in + // http://tools.ietf.org/html/rfc3986#section-5.2.2 + + if (is_absolute() && !is_opaque()) + { + return *this; + } + + if (is_opaque()) + { + return *this; + } + + std::string userinfo, host, port, path, query, fragment; + + if (!authority().empty()) + { + // g -> http://g + if (!this->userinfo().empty()) + { + userinfo = std::string(this->userinfo()); + } + + if (!this->host().empty()) + { + host = std::string(this->host()); + } + + if (!this->port().empty()) + { + port = std::string(this->port()); + } + + if (!this->path().empty()) + { + path = remove_dot_segments(this->path()); + } + + if (!this->query().empty()) + { + query = std::string(this->query()); + } + } + else + { + if (this->path().empty()) + { + if (!base.path().empty()) + { + path = std::string(base.path()); + } + + if (!this->query().empty()) + { + query = std::string(this->query()); + } + else if (!base.query().empty()) + { + query = std::string(base.query()); + } + } + else + { + if (this->path().front() == '/') + { + path = remove_dot_segments(this->path()); + } + else + { + path = merge_paths(base, *this); + } + + if (!this->query().empty()) + { + query = std::string(this->query()); + } + } + + if (!base.userinfo().empty()) + { + userinfo = std::string(base.userinfo()); + } + + if (!base.host().empty()) + { + host = std::string(base.host()); + } + + if (!base.port().empty()) + { + port = std::string(base.port()); + } + } + + if (!this->fragment().empty()) + { + fragment = std::string(this->fragment()); + } + + return uri(std::string(base.scheme()), userinfo, host, port, path, query, fragment); + } + + int compare(const uri& other) const + { + int result = scheme().compare(other.scheme()); + if (result != 0) return result; + result = userinfo().compare(other.userinfo()); + if (result != 0) return result; + result = host().compare(other.host()); + if (result != 0) return result; + result = port().compare(other.port()); + if (result != 0) return result; + result = path().compare(other.path()); + if (result != 0) return result; + result = query().compare(other.query()); + if (result != 0) return result; + result = fragment().compare(other.fragment()); + + return result; + } + + friend bool operator==(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) == 0; + } + + friend bool operator!=(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) != 0; + } + + friend bool operator<(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) < 0; + } + + friend bool operator<=(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) <= 0; + } + + friend bool operator>(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) > 0; + } + + friend bool operator>=(const uri& lhs, const uri& rhs) + { + return lhs.compare(rhs) >= 0; + } + + private: + enum class parse_state {expect_scheme, + expect_first_slash, + expect_second_slash, + expect_authority, + expect_host_ipv6, + expect_userinfo, + expect_host, + expect_port, + expect_path, + expect_query, + expect_fragment}; + + uri(const std::string& uri, part_type scheme, part_type userinfo, + part_type host, part_type port, part_type path, + part_type query, part_type fragment) + : uri_(uri), scheme_(scheme), userinfo_(userinfo), + host_(host), port_(port), path_(path), + query_(query), fragment_(fragment) + { + } + + static uri parse(const std::string& s) + { + part_type scheme; + part_type userinfo; + part_type host; + part_type port; + part_type path; + part_type query; + part_type fragment; + + std::size_t start = 0; + + parse_state state = parse_state::expect_scheme; + for (std::size_t i = 0; i < s.size(); ++i) + { + char c = s[i]; + switch (state) + { + case parse_state::expect_scheme: + switch (c) + { + case ':': + scheme = std::make_pair(start,i); + state = parse_state::expect_first_slash; + start = i; + break; + case '#': + userinfo = std::make_pair(start,start); + host = std::make_pair(start,start); + port = std::make_pair(start,start); + path = std::make_pair(start,i); + query = std::make_pair(i,i); + state = parse_state::expect_fragment; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_first_slash: + switch (c) + { + case '/': + state = parse_state::expect_second_slash; + break; + default: + start = i; + state = parse_state::expect_path; + break; + } + break; + case parse_state::expect_second_slash: + switch (c) + { + case '/': + state = parse_state::expect_authority; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_authority: + switch (c) + { + case '[': + state = parse_state::expect_host_ipv6; + start = i+1; + break; + default: + state = parse_state::expect_userinfo; + start = i; + --i; + break; + } + break; + case parse_state::expect_host_ipv6: + switch (c) + { + case ']': + userinfo = std::make_pair(start,start); + host = std::make_pair(start,i); + port = std::make_pair(i,i); + state = parse_state::expect_path; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_userinfo: + switch (c) + { + case '@': + userinfo = std::make_pair(start,i); + state = parse_state::expect_host; + start = i+1; + break; + case ':': + userinfo = std::make_pair(start,start); + host = std::make_pair(start,i); + state = parse_state::expect_port; + start = i+1; + break; + case '/': + userinfo = std::make_pair(start,start); + host = std::make_pair(start,i); + port = std::make_pair(i,i); + state = parse_state::expect_path; + start = i; + break; + default: + break; + } + break; + case parse_state::expect_host: + switch (c) + { + case ':': + host = std::make_pair(start,i); + state = parse_state::expect_port; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_port: + switch (c) + { + case '/': + port = std::make_pair(start,i); + state = parse_state::expect_path; + start = i; + break; + default: + break; + } + break; + case parse_state::expect_path: + switch (c) + { + case '?': + path = std::make_pair(start,i); + state = parse_state::expect_query; + start = i+1; + break; + case '#': + path = std::make_pair(start,i); + query = std::make_pair(start,start); + state = parse_state::expect_fragment; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_query: + switch (c) + { + case '#': + query = std::make_pair(start,i); + state = parse_state::expect_fragment; + start = i+1; + break; + default: + break; + } + break; + case parse_state::expect_fragment: + break; + } + } + switch (state) + { + case parse_state::expect_scheme: + userinfo = std::make_pair(start,start); + host = std::make_pair(start,start); + port = std::make_pair(start,start); + path = std::make_pair(start,s.size()); + break; + case parse_state::expect_userinfo: + userinfo = std::make_pair(start,start); + host = std::make_pair(start,start); + port = std::make_pair(start,start); + path = std::make_pair(start,s.size()); + break; + case parse_state::expect_path: + path = std::make_pair(start,s.size()); + break; + case parse_state::expect_query: + query = std::make_pair(start,s.size()); + break; + case parse_state::expect_fragment: + fragment = std::make_pair(start,s.size()); + break; + default: + JSONCONS_THROW(std::invalid_argument("Invalid uri")); + break; + } + + return uri(s, scheme, userinfo, host, port, path, query, fragment); + } + + static std::string remove_dot_segments(const jsoncons::string_view& input) + { + std::string result = std::string(input); +/* + std::size_t pos = 0; + while (pos < input.size()) + { + if (input.compare(0, 3, "../")) + { + network_boost::erase_head(input, 3); + } else if (network_boost::starts_with(input, "./")) { + network_boost::erase_head(input, 2); + } else if (network_boost::starts_with(input, "/./")) { + network_boost::replace_head(input, 3, "/"); + } else if (input == "/.") { + network_boost::replace_head(input, 2, "/"); + } else if (network_boost::starts_with(input, "/../")) { + network_boost::erase_head(input, 3); + remove_last_segment(result); + } else if (network_boost::starts_with(input, "/..")) { + network_boost::replace_head(input, 3, "/"); + remove_last_segment(result); + } else if (network_boost::algorithm::all(input, [](char ch) { return ch == '.'; })) { + input.clear(); + } + else { + int n = (input.front() == '/')? 1 : 0; + auto slash = network_boost::find_nth(input, "/", n); + result.append(std::begin(input), std::begin(slash)); + input.erase(std::begin(input), std::begin(slash)); + } + } +*/ + return result; + } + + static std::string merge_paths(const uri& base, const uri& relative) + { + std::string result; + + if (base.path().empty()) + { + result = "/"; + } + else + { + const auto& base_path = base.path(); + auto last_slash = base_path.rfind('/'); + result.append(std::string(base_path.substr(0,last_slash+1))); + } + if (!relative.path().empty()) + { + result.append(relative.path().begin(), relative.path().end()); + } + return remove_dot_segments(jsoncons::string_view(result)); + } + + static void remove_last_segment(std::string& path) + { + auto last_slash = path.rfind('/'); + if (last_slash != std::string::npos) + { + path.erase(last_slash); + } + } + }; + +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/LICENSE b/include/jsoncons_ext/LICENSE new file mode 100644 index 0000000..ecf46ab --- /dev/null +++ b/include/jsoncons_ext/LICENSE @@ -0,0 +1,28 @@ +// Copyright Daniel Parker 2013 - 2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/include/jsoncons_ext/bson/bson.hpp b/include/jsoncons_ext/bson/bson.hpp new file mode 100644 index 0000000..ec3192d --- /dev/null +++ b/include/jsoncons_ext/bson/bson.hpp @@ -0,0 +1,23 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_HPP +#define JSONCONS_BSON_BSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/jsoncons_ext/bson/bson_cursor.hpp b/include/jsoncons_ext/bson/bson_cursor.hpp new file mode 100644 index 0000000..8baee53 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_cursor.hpp @@ -0,0 +1,320 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_CURSOR_HPP +#define JSONCONS_BSON_BSON_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace bson { + +template> +class basic_bson_cursor : public basic_staj_cursor, private virtual ser_context +{ + using super_type = basic_staj_cursor; +public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; +private: + basic_bson_parser parser_; + basic_staj_visitor cursor_visitor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_bson_cursor(const basic_bson_cursor&) = delete; + basic_bson_cursor& operator=(const basic_bson_cursor&) = delete; + +public: + using string_view_type = string_view; + + template + basic_bson_cursor(Sourceable&& source, + const bson_decode_options& options = bson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_bson_cursor(Sourceable&& source, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward(source), + bson_decode_options(), + ec) + { + } + + template + basic_bson_cursor(Sourceable&& source, + const bson_decode_options& options, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const bson_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + void array_expected(std::error_code& ec) override + { + if (cursor_visitor_.event().event_type() == staj_event_type::begin_object) + { + parser_.array_expected(cursor_visitor_, ec); + } + else + { + super_type::array_expected(ec); + } + } + + const staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj_filter_view operator|(basic_bson_cursor& cursor, + std::function pred) + { + return staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(Sourceable&& source, + std::function filter, + std::error_code& ec) + : basic_bson_cursor(std::allocator_arg, Allocator(), + std::forward(source), filter, ec) + { + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(Sourceable&& source, + std::function filter, + const bson_decode_options& options = bson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(); + } + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source),alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&)") + void read(basic_json_visitor& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&, std::error_code&)") + void read(basic_json_visitor& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + static bool accept_all(const staj_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_visitor_, ec); + if (ec) return; + } + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(visitor, ec); + if (ec) return; + } + } +}; + +using bson_stream_cursor = basic_bson_cursor; +using bson_bytes_cursor = basic_bson_cursor; + +} // namespace bson +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp b/include/jsoncons_ext/bson/bson_decimal128.hpp new file mode 100644 index 0000000..b487a04 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp @@ -0,0 +1,865 @@ +#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP +#define JSONCONS_BSON_BSON_DECIMAL128_HPP + +/* + * Implements decimal128_to_chars and decimal128_from_chars + * + * Based on the libjson functions bson_decimal128_to_string + * and bson_decimal128_from_string_w_len, available at + * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.h + * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.c + * +*/ + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + + struct decimal128_to_chars_result + { + char* ptr; + std::errc ec; + }; + + struct decimal128_from_chars_result + { + const char* ptr; + std::errc ec; + }; + +/** + * BSON_DECIMAL128_STRING: + * + * The length of a decimal128 string (with null terminator). + * + * 1 for the sign + * 35 for digits and radix + * 2 for exponent indicator and sign + * 4 for exponent digits + */ +#define BSON_DECIMAL128_STRING 43 + + struct TP1 + { + uint64_t low; + uint64_t high; + + constexpr TP1() : low(0), high(0) {} + constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {} + }; + struct TP2 + { + uint64_t high; + uint64_t low; + + constexpr TP2() : high(0), low(0) {} + constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {} + }; + + typedef std::conditional< + jsoncons::endian::native == jsoncons::endian::little, + TP1, + TP2 + >::type decimal128_t; + + inline + bool operator==(const decimal128_t& lhs, const decimal128_t& rhs) + { + return lhs.high == rhs.high && lhs.low == rhs.low; + } + + inline + bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs) + { + return !(lhs == rhs); + } + + struct decimal128_limits + { + // The length of a decimal128 string (without null terminator). + // + // 1 for the sign + // 35 for digits and radix + // 2 for exponent indicator and sign + // 4 for exponent digits + static constexpr int buf_size = 42; + static constexpr int exponent_max = 6111; + static constexpr int exponent_min = -6176; + static constexpr int exponent_bias = 6176; + static constexpr int max_digits = 34; + + static constexpr decimal128_t nan() {return decimal128_t(0x7c00000000000000ull, 0);} + static constexpr decimal128_t infinity() {return decimal128_t(0x7800000000000000ull, 0);} + static constexpr decimal128_t neg_infinity() {return decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0);} + }; + + inline + bool is_nan(decimal128_t dec) { return dec == decimal128_limits::nan(); } + + inline + bool is_inf(decimal128_t dec) { return dec == decimal128_limits::infinity(); } + + inline + bool is_neg_inf(decimal128_t dec) { return dec == decimal128_limits::neg_infinity(); } + + /** + * bson_uint128_t: + * + * This struct represents a 128 bit integer. + */ + typedef struct { + uint32_t parts[4]; /* 32-bit words stored high to low. */ + } bson_uint128_t; + + typedef struct { + uint64_t high, low; + } bson_uint128_6464_t; + + namespace detail { + + /** + *------------------------------------------------------------------------------ + * + * bson_uint128_divide1B -- + * + * This function divides a #bson_uint128_t by 1000000000 (1 billion) and + * computes the quotient and remainder. + * + * The remainder will contain 9 decimal digits for conversion to string. + * + * @value The #bson_uint128_t operand. + * @quotient A pointer to store the #bson_uint128_t quotient. + * @rem A pointer to store the #uint64_t remainder. + * + * Returns: + * The quotient at @quotient and the remainder at @rem. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void bson_uint128_divide1B (bson_uint128_t value, /* IN */ + bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ + { + const uint32_t DIVISOR = 1000 * 1000 * 1000; + uint64_t _rem = 0; + int i = 0; + + if (!value.parts[0] && !value.parts[1] && !value.parts[2] && + !value.parts[3]) { + *quotient = value; + *rem = 0; + return; + } + + for (i = 0; i <= 3; i++) { + _rem <<= 32; /* Adjust remainder to match value of next dividend */ + _rem += value.parts[i]; /* Add the divided to _rem */ + value.parts[i] = (uint32_t) (_rem / DIVISOR); + _rem %= DIVISOR; /* Store the remainder */ + } + + *quotient = value; + *rem = (uint32_t) _rem; + } + + /** + *------------------------------------------------------------------------- + * + * mul64x64 -- + * + * This function multiplies two &uint64_t into a &bson_uint128_6464_t. + * + * Returns: + * The product of @left and @right. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ + + inline + void mul_64x64 (uint64_t left, /* IN */ + uint64_t right, /* IN */ + bson_uint128_6464_t *product) /* OUT */ + { + uint64_t left_high, left_low, right_high, right_low, product_high, + product_mid, product_mid2, product_low; + bson_uint128_6464_t rt = {0,0}; + + if (!left && !right) { + *product = rt; + return; + } + + left_high = left >> 32; + left_low = (uint32_t) left; + right_high = right >> 32; + right_low = (uint32_t) right; + + product_high = left_high * right_high; + product_mid = left_high * right_low; + product_mid2 = left_low * right_high; + product_low = left_low * right_low; + + product_high += product_mid >> 32; + product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + + product_high = product_high + (product_mid >> 32); + product_low = (product_mid << 32) + (uint32_t) product_low; + + rt.high = product_high; + rt.low = product_low; + *product = rt; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_tolower -- + * + * This function converts the ASCII character @c to lowercase. It is locale + * insensitive (unlike the stdlib tolower). + * + * Returns: + * The lowercased character. + */ + + inline + char dec128_tolower (char c) + { + if (isupper (c)) { + c += 32; + } + + return c; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_istreq -- + * + * This function compares the null-terminated *ASCII* strings @a and @b + * for case-insensitive equality. + * + * Returns: + * true if the strings are equal, false otherwise. + */ + + inline + bool dec128_istreq (const char* a, const char* lasta, + const char* b, const char* lastb) + { + while (!(a == lasta && b == lastb)) + { + // strings are different lengths + if (a == lasta || b == lastb) + { + return false; + } + + if (dec128_tolower (*a) != dec128_tolower (*b)) { + return false; + } + + a++; + b++; + } + + return true; + } + + } // namespace detail + + + /** + *------------------------------------------------------------------------------ + * + * decimal128_to_chars -- + * + * This function converts a BID formatted decimal128 value to string, + * accepting a &decimal128_t as @dec. The string is stored at @str. + * + * @dec : The BID formatted decimal to convert. + * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING + *characters. + * + * Returns: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_to_chars_result decimal128_to_chars(char* first, char* last, const decimal128_t& dec) + { + const std::string bson_decimal128_inf = "Infinity"; + const std::string bson_decimal128_nan = "NaN"; + + const uint32_t combination_mask = 0x1f; /* Extract least significant 5 bits */ + const uint32_t exponent_mask = 0x3fff; /* Extract least significant 14 bits */ + const uint32_t combination_infinity = 30; /* Value of combination field for Inf */ + const uint32_t combination_nan = 31; /* Value of combination field for NaN */ + const uint32_t exponent_bias = 6176; /* decimal128 exponent bias */ + + char* str_out = first; /* output pointer in string */ + char significand_str[35]; /* decoded significand digits */ + + /* Note: bits in this routine are referred to starting at 0, */ + /* from the sign bit, towards the coefficient. */ + uint32_t high; /* bits 0 - 31 */ + uint32_t midh; /* bits 32 - 63 */ + uint32_t midl; /* bits 64 - 95 */ + uint32_t low; /* bits 96 - 127 */ + uint32_t combination; /* bits 1 - 5 */ + uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ + uint32_t significand_digits = 0; /* the number of significand digits */ + uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ + uint32_t *significand_read = significand; /* read pointer into significand */ + int32_t exponent; /* unbiased exponent */ + int32_t scientific_exponent; /* the exponent if scientific notation is + * used */ + bool is_zero = false; /* true if the number is zero */ + + uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ + bson_uint128_t + significand128; /* temporary storage for significand decoding */ + + memset (significand_str, 0, sizeof (significand_str)); + + if ((int64_t) dec.high < 0) { /* negative */ + *(str_out++) = '-'; + } + + low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32), + midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32); + + /* Decode combination field and exponent */ + combination = (high >> 26) & combination_mask; + + if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) { + /* Check for 'special' values */ + if (combination == combination_infinity) { /* Infinity */ + if (last-str_out >= static_cast(bson_decimal128_inf.size())) + { + std::memcpy(str_out, bson_decimal128_inf.data(), bson_decimal128_inf.size()); + str_out += bson_decimal128_inf.size(); + } + *str_out = 0; + //strcpy_s (str_out, last-str_out, bson_decimal128_inf.c_str()); + return decimal128_to_chars_result{str_out, std::errc()}; + } else if (combination == combination_nan) { /* NaN */ + /* first, not str_out, to erase the sign */ + str_out = first; + if (last-str_out >= static_cast(bson_decimal128_nan.size())) + { + std::memcpy(str_out, bson_decimal128_nan.data(), bson_decimal128_nan.size()); + str_out += bson_decimal128_nan.size(); + } + *str_out = 0; + //strcpy_s (first, last-first, bson_decimal128_nan.c_str()); + /* we don't care about the NaN payload. */ + return decimal128_to_chars_result{str_out, std::errc()}; + } else { + biased_exponent = (high >> 15) & exponent_mask; + significand_msb = 0x8 + ((high >> 14) & 0x1); + } + } else { + significand_msb = (high >> 14) & 0x7; + biased_exponent = (high >> 17) & exponent_mask; + } + + exponent = biased_exponent - exponent_bias; + /* Create string of significand digits */ + + /* Convert the 114-bit binary number represented by */ + /* (high, midh, midl, low) to at most 34 decimal */ + /* digits through modulo and division. */ + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[1] = midh; + significand128.parts[2] = midl; + significand128.parts[3] = low; + + if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && + significand128.parts[2] == 0 && significand128.parts[3] == 0) { + is_zero = true; + } else if (significand128.parts[0] >= (1 << 17)) { + /* The significand is non-canonical or zero. + * In order to preserve compatibility with the densely packed decimal + * format, the maximum value for the significand of decimal128 is + * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 + * standard dictates that the significand is interpreted as zero. + */ + is_zero = true; + } else { + for (int k = 3; k >= 0; k--) { + uint32_t least_digits = 0; + detail::bson_uint128_divide1B ( + significand128, &significand128, &least_digits); + + /* We now have the 9 least significant digits (in base 2). */ + /* Convert and output to string. */ + if (!least_digits) { + continue; + } + + for (int j = 8; j >= 0; j--) { + significand[k * 9 + j] = least_digits % 10; + least_digits /= 10; + } + } + } + + /* Output format options: */ + /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ + /* Regular - ddd.ddd */ + + if (is_zero) { + significand_digits = 1; + *significand_read = 0; + } else { + significand_digits = 36; + while (!(*significand_read)) { + significand_digits--; + significand_read++; + } + } + + scientific_exponent = significand_digits - 1 + exponent; + + /* The scientific exponent checks are dictated by the string conversion + * specification and are somewhat arbitrary cutoffs. + * + * We must check exponent > 0, because if this is the case, the number + * has trailing zeros. However, we *cannot* output these trailing zeros, + * because doing so would change the precision of the value, and would + * change stored data if the string converted number is round tripped. + */ + if (scientific_exponent < -6 || exponent > 0) { + /* Scientific format */ + *(str_out++) = char(*(significand_read++)) + '0'; + significand_digits--; + + if (significand_digits) { + *(str_out++) = '.'; + } + + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + /* Exponent */ + *(str_out++) = 'E'; + + std::string s; + if (scientific_exponent >= 0) { + s.push_back('+'); + } + jsoncons::detail::from_integer(scientific_exponent, s); + if (str_out + s.size() < last) + { + std::memcpy(str_out, s.data(), s.size()); + } + else + { + return decimal128_to_chars_result{str_out, std::errc::value_too_large}; + } + str_out += s.size(); + } else { + /* Regular format with no decimal place */ + if (exponent >= 0) { + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { + int32_t radix_position = significand_digits + exponent; + + if (radix_position > 0) { /* non-zero digits before radix */ + for (int32_t i = 0; + i < radix_position && (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { /* leading zero before radix point */ + *(str_out++) = '0'; + } + + *(str_out++) = '.'; + while (radix_position++ < 0) { /* add leading zeros after radix */ + *(str_out++) = '0'; + } + + for (std::size_t i = 0; + (i < significand_digits - (std::max) (radix_position - 1, 0)) && + (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } + } + return decimal128_to_chars_result{str_out, std::errc()}; + } + + + + /** + *------------------------------------------------------------------------------ + * + * bson_decimal128_from_string_w_len -- + * + * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to + * decimal128. Out of range values are converted to +/-Infinity. Invalid + * strings are converted to NaN. @len is the length of the string, or -1 + * meaning the string is null-terminated. + * + * If more digits are provided than the available precision allows, + * round to the nearest expressable decimal128 with ties going to even will + * occur. + * + * Note: @string must be ASCII only! + * + * Returns: + * true on success, or false on failure. @dec will be NaN if @str was invalid + * The &decimal128_t converted from @string at @dec. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_from_chars_result decimal128_from_chars(const char* first, const char* last, decimal128_t& dec) + { + const string_view inf_str = "inf"; + const string_view infinity_str = "infinity"; + const string_view nan_str = "nan"; + + ptrdiff_t len = last - first; + + bson_uint128_6464_t significand = {0,0}; + + const char* str_read = first; /* Read pointer for consuming str. */ + + /* Parsing state tracking */ + bool is_negative = false; + bool saw_radix = false; + bool includes_sign = false; /* True if the input first contains a sign. */ + bool found_nonzero = false; + + size_t significant_digits = 0; /* Total number of significant digits + * (no leading or trailing zero) */ + size_t ndigits_read = 0; /* Total number of significand digits read */ + size_t ndigits = 0; /* Total number of digits (no leading zeros) */ + size_t radix_position = 0; /* The number of the digits after radix */ + size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ + + uint16_t digits[decimal128_limits::max_digits] = {0}; + uint16_t ndigits_stored = 0; /* The number of digits in digits */ + uint16_t *digits_insert = digits; /* Insertion pointer for digits */ + size_t first_digit = 0; /* The index of the first non-zero digit */ + size_t last_digit = 0; /* The index of the last digit */ + + int32_t exponent = 0; + uint64_t significand_high = 0; /* The high 17 digits of the significand */ + uint64_t significand_low = 0; /* The low 17 digits of the significand */ + uint16_t biased_exponent = 0; /* The biased exponent */ + + dec.high = 0; + dec.low = 0; + + if (*str_read == '+' || *str_read == '-') { + is_negative = *(str_read++) == '-'; + includes_sign = true; + } + + /* Check for Infinity or NaN */ + if (!isdigit (*str_read) && *str_read != '.') { + if (detail::dec128_istreq (str_read, last, inf_str.data(), inf_str.data()+inf_str.length()) || + detail::dec128_istreq (str_read, last, infinity_str.data(), infinity_str.data()+infinity_str.length())) + { + dec = is_negative ? decimal128_limits::neg_infinity() : decimal128_limits::infinity(); + return decimal128_from_chars_result{str_read,std::errc()}; + } else if (detail::dec128_istreq (str_read, last, nan_str.data(), nan_str.data()+nan_str.length())) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc()}; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read digits */ + while (((isdigit (*str_read) || *str_read == '.')) && + (len == -1 || str_read < first + len)) { + if (*str_read == '.') { + if (saw_radix) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + saw_radix = true; + str_read++; + continue; + } + + if (ndigits_stored < 34) { + if (*str_read != '0' || found_nonzero) { + if (!found_nonzero) { + first_nonzero = ndigits_read; + } + + found_nonzero = true; + *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + ndigits_stored++; + } + } + + if (found_nonzero) { + ndigits++; + } + + if (saw_radix) { + radix_position++; + } + + ndigits_read++; + str_read++; + } + + if (saw_radix && !ndigits_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read exponent if exists */ + if (*str_read == 'e' || *str_read == 'E') { + ++str_read; + if (*str_read == '+') { + ++str_read; + } + auto result = jsoncons::detail::to_integer(str_read, last - str_read, exponent); + if (result.ec != jsoncons::detail::to_integer_errc()) + { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + str_read = result.ptr; + } + + if ((len == -1 || str_read < first + len) && *str_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Done reading input. */ + /* Find first non-zero digit in digits */ + first_digit = 0; + + if (!ndigits_stored) { /* value is zero */ + first_digit = 0; + last_digit = 0; + digits[0] = 0; + ndigits = 1; + ndigits_stored = 1; + significant_digits = 0; + } else { + last_digit = ndigits_stored - 1; + significant_digits = ndigits; + /* Mark trailing zeros as non-significant */ + while (first[first_nonzero + significant_digits - 1 + includes_sign + + saw_radix] == '0') { + significant_digits--; + } + } + + + /* Normalization of exponent */ + /* Correct exponent based on radix position, and shift significand as needed + */ + /* to represent user input */ + + /* Overflow prevention */ + if (exponent <= static_cast(radix_position) && static_cast(radix_position) - exponent > (1 << 14)) { + exponent = decimal128_limits::exponent_min; + } else { + exponent -= static_cast(radix_position); + } + + /* Attempt to normalize the exponent */ + while (exponent > decimal128_limits::exponent_max) { + /* Shift exponent to significand and decrease */ + last_digit++; + + if (last_digit - first_digit > decimal128_limits::max_digits) { + /* The exponent is too great to shift into the significand. */ + if (significant_digits == 0) { + /* Value is zero, we are allowed to clamp the exponent. */ + exponent = decimal128_limits::exponent_max; + break; + } + + /* Overflow is not permitted, error. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + exponent--; + } + + while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) { + /* Shift last digit */ + if (last_digit == 0) { + /* underflow is not allowed, but zero clamping is */ + if (significant_digits == 0) { + exponent = decimal128_limits::exponent_min; + break; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + if (ndigits_stored < ndigits) { + if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && + significant_digits != 0) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + ndigits--; /* adjust to match digits not stored */ + } else { + if (digits[last_digit] != 0) { + /* Inexact rounding is not allowed. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + + last_digit--; /* adjust to round */ + } + + if (exponent < decimal128_limits::exponent_max) { + exponent++; + } else { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Round */ + /* We've normalized the exponent, but might still need to round. */ + if (last_digit - first_digit + 1 < significant_digits) { + uint8_t round_digit; + + /* There are non-zero digits after last_digit that need rounding. */ + /* We round to nearest, ties to even */ + round_digit = + first[first_nonzero + last_digit + includes_sign + saw_radix + 1] - + '0'; + + if (round_digit != 0) { + /* Inexact (non-zero) rounding is not allowed */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Encode significand */ + significand_high = 0, /* The high 17 digits of the significand */ + significand_low = 0; /* The low 17 digits of the significand */ + + if (significant_digits == 0) { /* read a zero */ + significand_high = 0; + significand_low = 0; + } else if (last_digit - first_digit < 17) { + size_t d_idx = first_digit; + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + significand_high = 0; + } + } else { + size_t d_idx = first_digit; + significand_high = digits[d_idx++]; + + for (; d_idx <= last_digit - 17; d_idx++) { + significand_high *= 10; + significand_high += digits[d_idx]; + } + + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + } + } + + detail::mul_64x64 (significand_high, 100000000000000000ull, &significand); + significand.low += significand_low; + + if (significand.low < significand_low) { + significand.high += 1; + } + + + biased_exponent = static_cast(exponent + static_cast(decimal128_limits::exponent_bias)); + + /* Encode combination, exponent, and significand. */ + if ((significand.high >> 49) & 1) { + /* Encode '11' into bits 1 to 3 */ + dec.high |= (0x3ull << 61); + dec.high |= (biased_exponent & 0x3fffull) << 47; + dec.high |= significand.high & 0x7fffffffffffull; + } else { + dec.high |= (biased_exponent & 0x3fffull) << 49; + dec.high |= significand.high & 0x1ffffffffffffull; + } + + dec.low = significand.low; + + /* Encode sign */ + if (is_negative) { + dec.high |= 0x8000000000000000ull; + } + + return decimal128_from_chars_result{str_read,std::errc()}; + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp.bak b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak new file mode 100644 index 0000000..8e27ee1 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak @@ -0,0 +1,816 @@ +#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP +#define JSONCONS_BSON_BSON_DECIMAL128_HPP + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + + struct decimal128_to_chars_result + { + char* ptr; + std::errc ec; + }; + + struct decimal128_from_chars_result + { + const char* ptr; + std::errc ec; + }; + +/** + * BSON_DECIMAL128_STRING: + * + * The length of a decimal128 string (with null terminator). + * + * 1 for the sign + * 35 for digits and radix + * 2 for exponent indicator and sign + * 4 for exponent digits + */ +#define BSON_DECIMAL128_STRING 43 +#define BSON_DECIMAL128_INF "Infinity" +#define BSON_DECIMAL128_NAN "NaN" + + struct TP1 + { + uint64_t low; + uint64_t high; + + constexpr TP1() : low(0), high(0) {} + constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {} + }; + struct TP2 + { + uint64_t high; + uint64_t low; + + constexpr TP2() : high(0), low(0) {} + constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {} + }; + + typedef typename std::conditional< + jsoncons::endian::native == jsoncons::endian::little, + TP1, + TP2 + >::type decimal128_t; + + inline + bool operator==(const decimal128_t& lhs, const decimal128_t& rhs) + { + return lhs.high == rhs.high && lhs.low == rhs.low; + } + + inline + bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs) + { + return !(lhs == rhs); + } + + struct decimal128_limits + { + // The length of a decimal128 string (without null terminator). + // + // 1 for the sign + // 35 for digits and radix + // 2 for exponent indicator and sign + // 4 for exponent digits + static constexpr int recommended_buffer_size = 42; + static constexpr decimal128_t nan = decimal128_t(0x7c00000000000000ull, 0); + static constexpr decimal128_t infinity = decimal128_t(0x7800000000000000ull, 0); + static constexpr decimal128_t neg_infinity = decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0); + static constexpr int exponent_max = 6111; + static constexpr int exponent_min = -6176; + static constexpr int exponent_bias = 6176; + static constexpr int max_digits = 34; + }; + + /** + * bson_uint128_t: + * + * This struct represents a 128 bit integer. + */ + typedef struct { + uint32_t parts[4]; /* 32-bit words stored high to low. */ + } bson_uint128_t; + + typedef struct { + uint64_t high, low; + } bson_uint128_6464_t; + + namespace detail { + + /** + *------------------------------------------------------------------------------ + * + * bson_uint128_divide1B -- + * + * This function divides a #bson_uint128_t by 1000000000 (1 billion) and + * computes the quotient and remainder. + * + * The remainder will contain 9 decimal digits for conversion to string. + * + * @value The #bson_uint128_t operand. + * @quotient A pointer to store the #bson_uint128_t quotient. + * @rem A pointer to store the #uint64_t remainder. + * + * Returns: + * The quotient at @quotient and the remainder at @rem. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void bson_uint128_divide1B (bson_uint128_t value, /* IN */ + bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ + { + const uint32_t DIVISOR = 1000 * 1000 * 1000; + uint64_t _rem = 0; + int i = 0; + + if (!value.parts[0] && !value.parts[1] && !value.parts[2] && + !value.parts[3]) { + *quotient = value; + *rem = 0; + return; + } + + for (i = 0; i <= 3; i++) { + _rem <<= 32; /* Adjust remainder to match value of next dividend */ + _rem += value.parts[i]; /* Add the divided to _rem */ + value.parts[i] = (uint32_t) (_rem / DIVISOR); + _rem %= DIVISOR; /* Store the remainder */ + } + + *quotient = value; + *rem = (uint32_t) _rem; + } + + /** + *------------------------------------------------------------------------- + * + * mul64x64 -- + * + * This function multiplies two &uint64_t into a &bson_uint128_6464_t. + * + * Returns: + * The product of @left and @right. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ + + inline + void mul_64x64 (uint64_t left, /* IN */ + uint64_t right, /* IN */ + bson_uint128_6464_t *product) /* OUT */ + { + uint64_t left_high, left_low, right_high, right_low, product_high, + product_mid, product_mid2, product_low; + bson_uint128_6464_t rt = {0}; + + if (!left && !right) { + *product = rt; + return; + } + + left_high = left >> 32; + left_low = (uint32_t) left; + right_high = right >> 32; + right_low = (uint32_t) right; + + product_high = left_high * right_high; + product_mid = left_high * right_low; + product_mid2 = left_low * right_high; + product_low = left_low * right_low; + + product_high += product_mid >> 32; + product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + + product_high = product_high + (product_mid >> 32); + product_low = (product_mid << 32) + (uint32_t) product_low; + + rt.high = product_high; + rt.low = product_low; + *product = rt; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_tolower -- + * + * This function converts the ASCII character @c to lowercase. It is locale + * insensitive (unlike the stdlib tolower). + * + * Returns: + * The lowercased character. + */ + + inline + char dec128_tolower (char c) + { + if (isupper (c)) { + c += 32; + } + + return c; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_istreq -- + * + * This function compares the null-terminated *ASCII* strings @a and @b + * for case-insensitive equality. + * + * Returns: + * true if the strings are equal, false otherwise. + */ + + inline + bool dec128_istreq (const char* a, /* IN */ + const char* b /* IN */) + { + while (*a != '\0' || *b != '\0') { + /* strings are different lengths. */ + if (*a == '\0' || *b == '\0') { + return false; + } + + if (dec128_tolower (*a) != dec128_tolower (*b)) { + return false; + } + + a++; + b++; + } + + return true; + } + + } // namespace detail + + + /** + *------------------------------------------------------------------------------ + * + * decimal128_to_chars -- + * + * This function converts a BID formatted decimal128 value to string, + * accepting a &decimal128_t as @dec. The string is stored at @str. + * + * @dec : The BID formatted decimal to convert. + * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING + *characters. + * + * Returns: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void decimal128_to_chars(char* first, char* last, const decimal128_t& dec) + { + uint32_t COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */ + uint32_t EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */ + uint32_t COMBINATION_INFINITY = 30; /* Value of combination field for Inf */ + uint32_t COMBINATION_NAN = 31; /* Value of combination field for NaN */ + uint32_t EXPONENT_BIAS = 6176; /* decimal128 exponent bias */ + + char* str_out = first; /* output pointer in string */ + char significand_str[35]; /* decoded significand digits */ + + + /* Note: bits in this routine are referred to starting at 0, */ + /* from the sign bit, towards the coefficient. */ + uint32_t high; /* bits 0 - 31 */ + uint32_t midh; /* bits 32 - 63 */ + uint32_t midl; /* bits 64 - 95 */ + uint32_t low; /* bits 96 - 127 */ + uint32_t combination; /* bits 1 - 5 */ + uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ + uint32_t significand_digits = 0; /* the number of significand digits */ + uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ + uint32_t *significand_read = significand; /* read pointer into significand */ + int32_t exponent; /* unbiased exponent */ + int32_t scientific_exponent; /* the exponent if scientific notation is + * used */ + bool is_zero = false; /* true if the number is zero */ + + uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ + bson_uint128_t + significand128; /* temporary storage for significand decoding */ + size_t i; /* indexing variables */ + int j, k; + + memset (significand_str, 0, sizeof (significand_str)); + + if ((int64_t) dec.high < 0) { /* negative */ + *(str_out++) = '-'; + } + + low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32), + midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32); + + /* Decode combination field and exponent */ + combination = (high >> 26) & COMBINATION_MASK; + + if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) { + /* Check for 'special' values */ + if (combination == COMBINATION_INFINITY) { /* Infinity */ + strcpy (str_out, BSON_DECIMAL128_INF); + return; + } else if (combination == COMBINATION_NAN) { /* NaN */ + /* first, not str_out, to erase the sign */ + strcpy (first, BSON_DECIMAL128_NAN); + /* we don't care about the NaN payload. */ + return; + } else { + biased_exponent = (high >> 15) & EXPONENT_MASK; + significand_msb = 0x8 + ((high >> 14) & 0x1); + } + } else { + significand_msb = (high >> 14) & 0x7; + biased_exponent = (high >> 17) & EXPONENT_MASK; + } + + exponent = biased_exponent - EXPONENT_BIAS; + /* Create string of significand digits */ + + /* Convert the 114-bit binary number represented by */ + /* (high, midh, midl, low) to at most 34 decimal */ + /* digits through modulo and division. */ + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[1] = midh; + significand128.parts[2] = midl; + significand128.parts[3] = low; + + if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && + significand128.parts[2] == 0 && significand128.parts[3] == 0) { + is_zero = true; + } else if (significand128.parts[0] >= (1 << 17)) { + /* The significand is non-canonical or zero. + * In order to preserve compatibility with the densely packed decimal + * format, the maximum value for the significand of decimal128 is + * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 + * standard dictates that the significand is interpreted as zero. + */ + is_zero = true; + } else { + for (k = 3; k >= 0; k--) { + uint32_t least_digits = 0; + detail::bson_uint128_divide1B ( + significand128, &significand128, &least_digits); + + /* We now have the 9 least significant digits (in base 2). */ + /* Convert and output to string. */ + if (!least_digits) { + continue; + } + + for (j = 8; j >= 0; j--) { + significand[k * 9 + j] = least_digits % 10; + least_digits /= 10; + } + } + } + + /* Output format options: */ + /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ + /* Regular - ddd.ddd */ + + if (is_zero) { + significand_digits = 1; + *significand_read = 0; + } else { + significand_digits = 36; + while (!(*significand_read)) { + significand_digits--; + significand_read++; + } + } + + scientific_exponent = significand_digits - 1 + exponent; + + /* The scientific exponent checks are dictated by the string conversion + * specification and are somewhat arbitrary cutoffs. + * + * We must check exponent > 0, because if this is the case, the number + * has trailing zeros. However, we *cannot* output these trailing zeros, + * because doing so would change the precision of the value, and would + * change stored data if the string converted number is round tripped. + */ + if (scientific_exponent < -6 || exponent > 0) { + /* Scientific format */ + *(str_out++) = *(significand_read++) + '0'; + significand_digits--; + + if (significand_digits) { + *(str_out++) = '.'; + } + + for (i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = *(significand_read++) + '0'; + } + /* Exponent */ + *(str_out++) = 'E'; + snprintf (str_out, 6, "%+d", scientific_exponent); + } else { + /* Regular format with no decimal place */ + if (exponent >= 0) { + for (i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = *(significand_read++) + '0'; + } + *str_out = '\0'; + } else { + int32_t radix_position = significand_digits + exponent; + + if (radix_position > 0) { /* non-zero digits before radix */ + for (i = 0; + i < radix_position && (str_out < last); + i++) { + *(str_out++) = *(significand_read++) + '0'; + } + } else { /* leading zero before radix point */ + *(str_out++) = '0'; + } + + *(str_out++) = '.'; + while (radix_position++ < 0) { /* add leading zeros after radix */ + *(str_out++) = '0'; + } + + for (i = 0; + (i < significand_digits - (std::max) (radix_position - 1, 0)) && + (str_out < last); + i++) { + *(str_out++) = *(significand_read++) + '0'; + } + *str_out = '\0'; + } + } + } + + + + /** + *------------------------------------------------------------------------------ + * + * bson_decimal128_from_string_w_len -- + * + * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to + * decimal128. Out of range values are converted to +/-Infinity. Invalid + * strings are converted to NaN. @len is the length of the string, or -1 + * meaning the string is null-terminated. + * + * If more digits are provided than the available precision allows, + * round to the nearest expressable decimal128 with ties going to even will + * occur. + * + * Note: @string must be ASCII only! + * + * Returns: + * true on success, or false on failure. @dec will be NaN if @str was invalid + * The &decimal128_t converted from @string at @dec. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + bool decimal128_from_chars(const char* first, const char* last, decimal128_t& dec) + { + int len = last - first; + + bson_uint128_6464_t significand = {0}; + + const char* str_read = first; /* Read pointer for consuming str. */ + + /* Parsing state tracking */ + bool is_negative = false; + bool saw_radix = false; + bool includes_sign = false; /* True if the input first contains a sign. */ + bool found_nonzero = false; + + size_t significant_digits = 0; /* Total number of significant digits + * (no leading or trailing zero) */ + size_t ndigits_read = 0; /* Total number of significand digits read */ + size_t ndigits = 0; /* Total number of digits (no leading zeros) */ + size_t radix_position = 0; /* The number of the digits after radix */ + size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ + + uint16_t digits[decimal128_limits::max_digits] = {0}; + uint16_t ndigits_stored = 0; /* The number of digits in digits */ + uint16_t *digits_insert = digits; /* Insertion pointer for digits */ + size_t first_digit = 0; /* The index of the first non-zero digit */ + size_t last_digit = 0; /* The index of the last digit */ + + int32_t exponent = 0; + uint64_t significand_high = 0; /* The high 17 digits of the significand */ + uint64_t significand_low = 0; /* The low 17 digits of the significand */ + uint16_t biased_exponent = 0; /* The biased exponent */ + + dec.high = 0; + dec.low = 0; + + if (*str_read == '+' || *str_read == '-') { + is_negative = *(str_read++) == '-'; + includes_sign = true; + } + + /* Check for Infinity or NaN */ + if (!isdigit (*str_read) && *str_read != '.') { + if (detail::dec128_istreq (str_read, "inf") || + detail::dec128_istreq (str_read, "infinity")) { + dec = is_negative ? decimal128_limits::neg_infinity : decimal128_limits::infinity; + return true; + } else if (detail::dec128_istreq (str_read, "nan")) { + dec = decimal128_limits::nan; + return true; + } + + dec = decimal128_limits::nan; + return false; + } + + /* Read digits */ + while (((isdigit (*str_read) || *str_read == '.')) && + (len == -1 || str_read < first + len)) { + if (*str_read == '.') { + if (saw_radix) { + dec = decimal128_limits::nan; + return false; + } + + saw_radix = true; + str_read++; + continue; + } + + if (ndigits_stored < 34) { + if (*str_read != '0' || found_nonzero) { + if (!found_nonzero) { + first_nonzero = ndigits_read; + } + + found_nonzero = true; + *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + ndigits_stored++; + } + } + + if (found_nonzero) { + ndigits++; + } + + if (saw_radix) { + radix_position++; + } + + ndigits_read++; + str_read++; + } + + if (saw_radix && !ndigits_read) { + dec = decimal128_limits::nan; + return false; + } + + /* Read exponent if exists */ + if (*str_read == 'e' || *str_read == 'E') { + int nread = 0; + #ifdef _MSC_VER + #define SSCANF sscanf_s + #else + #define SSCANF sscanf + #endif + int read_exponent = SSCANF (++str_read, "%d%n", &exponent, &nread); + str_read += nread; + + if (!read_exponent || nread == 0) { + dec = decimal128_limits::nan; + return false; + } + + #undef SSCANF + } + + if ((len == -1 || str_read < first + len) && *str_read) { + dec = decimal128_limits::nan; + return false; + } + + /* Done reading input. */ + /* Find first non-zero digit in digits */ + first_digit = 0; + + if (!ndigits_stored) { /* value is zero */ + first_digit = 0; + last_digit = 0; + digits[0] = 0; + ndigits = 1; + ndigits_stored = 1; + significant_digits = 0; + } else { + last_digit = ndigits_stored - 1; + significant_digits = ndigits; + /* Mark trailing zeros as non-significant */ + while (first[first_nonzero + significant_digits - 1 + includes_sign + + saw_radix] == '0') { + significant_digits--; + } + } + + + /* Normalization of exponent */ + /* Correct exponent based on radix position, and shift significand as needed + */ + /* to represent user input */ + + /* Overflow prevention */ + if (exponent <= radix_position && radix_position - exponent > (1 << 14)) { + exponent = decimal128_limits::exponent_min; + } else { + exponent -= radix_position; + } + + /* Attempt to normalize the exponent */ + while (exponent > decimal128_limits::exponent_max) { + /* Shift exponent to significand and decrease */ + last_digit++; + + if (last_digit - first_digit > decimal128_limits::max_digits) { + /* The exponent is too great to shift into the significand. */ + if (significant_digits == 0) { + /* Value is zero, we are allowed to clamp the exponent. */ + exponent = decimal128_limits::exponent_max; + break; + } + + /* Overflow is not permitted, error. */ + dec = decimal128_limits::nan; + return false; + } + + exponent--; + } + + while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) { + /* Shift last digit */ + if (last_digit == 0) { + /* underflow is not allowed, but zero clamping is */ + if (significant_digits == 0) { + exponent = decimal128_limits::exponent_min; + break; + } + + dec = decimal128_limits::nan; + return false; + } + + if (ndigits_stored < ndigits) { + if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && + significant_digits != 0) { + dec = decimal128_limits::nan; + return false; + } + + ndigits--; /* adjust to match digits not stored */ + } else { + if (digits[last_digit] != 0) { + /* Inexact rounding is not allowed. */ + dec = decimal128_limits::nan; + return false; + } + + + last_digit--; /* adjust to round */ + } + + if (exponent < decimal128_limits::exponent_max) { + exponent++; + } else { + dec = decimal128_limits::nan; + return false; + } + } + + /* Round */ + /* We've normalized the exponent, but might still need to round. */ + if (last_digit - first_digit + 1 < significant_digits) { + uint8_t round_digit; + + /* There are non-zero digits after last_digit that need rounding. */ + /* We round to nearest, ties to even */ + round_digit = + first[first_nonzero + last_digit + includes_sign + saw_radix + 1] - + '0'; + + if (round_digit != 0) { + /* Inexact (non-zero) rounding is not allowed */ + dec = decimal128_limits::nan; + return false; + } + } + + /* Encode significand */ + significand_high = 0, /* The high 17 digits of the significand */ + significand_low = 0; /* The low 17 digits of the significand */ + + if (significant_digits == 0) { /* read a zero */ + significand_high = 0; + significand_low = 0; + } else if (last_digit - first_digit < 17) { + size_t d_idx = first_digit; + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + significand_high = 0; + } + } else { + size_t d_idx = first_digit; + significand_high = digits[d_idx++]; + + for (; d_idx <= last_digit - 17; d_idx++) { + significand_high *= 10; + significand_high += digits[d_idx]; + } + + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + } + } + + detail::mul_64x64 (significand_high, 100000000000000000ull, &significand); + significand.low += significand_low; + + if (significand.low < significand_low) { + significand.high += 1; + } + + + biased_exponent = (exponent + (int16_t) decimal128_limits::exponent_bias); + + /* Encode combination, exponent, and significand. */ + if ((significand.high >> 49) & 1) { + /* Encode '11' into bits 1 to 3 */ + dec.high |= (0x3ull << 61); + dec.high |= (biased_exponent & 0x3fffull) << 47; + dec.high |= significand.high & 0x7fffffffffffull; + } else { + dec.high |= (biased_exponent & 0x3fffull) << 49; + dec.high |= significand.high & 0x1ffffffffffffull; + } + + dec.low = significand.low; + + /* Encode sign */ + if (is_negative) { + dec.high |= 0x8000000000000000ull; + } + + return true; + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_encoder.hpp b/include/jsoncons_ext/bson/bson_encoder.hpp new file mode 100644 index 0000000..4569f06 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_encoder.hpp @@ -0,0 +1,585 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_ENCODER_HPP +#define JSONCONS_BSON_BSON_ENCODER_HPP + +#include +#include +#include // std::numeric_limits +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + +template> +class basic_bson_encoder final : public basic_json_visitor +{ + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; +public: + using allocator_type = Allocator; + using char_type = char; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + +private: + struct stack_item + { + jsoncons::bson::bson_container_type type_; + std::size_t offset_; + std::size_t name_offset_; + std::size_t index_; + + stack_item(jsoncons::bson::bson_container_type type, std::size_t offset) noexcept + : type_(type), offset_(offset), name_offset_(0), index_(0) + { + } + + std::size_t offset() const + { + return offset_; + } + + std::size_t member_offset() const + { + return name_offset_; + } + + void member_offset(std::size_t offset) + { + name_offset_ = offset; + } + + std::size_t next_index() + { + return index_++; + } + + bool is_object() const + { + return type_ == jsoncons::bson::bson_container_type::document; + } + + + }; + + sink_type sink_; + const bson_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + std::vector buffer_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_bson_encoder(const basic_bson_encoder&) = delete; + basic_bson_encoder& operator=(const basic_bson_encoder&) = delete; +public: + explicit basic_bson_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_bson_encoder(std::forward(sink), + bson_encode_options(), + alloc) + { + } + + explicit basic_bson_encoder(Sink&& sink, + const bson_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + ~basic_bson_encoder() noexcept + { + sink_.flush(); + } + + void reset() + { + stack_.clear(); + buffer_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + +private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::document_type); + } + + stack_.emplace_back(jsoncons::bson::bson_container_type::document, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::array_type); + } + stack_.emplace_back(jsoncons::bson::bson_container_type::array, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + stack_.back().member_offset(buffer_.size()); + buffer_.push_back(0x00); // reserve space for code + for (auto c : name) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + return true; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + switch (tag) + { + case semantic_tag::undefined: + before_value(jsoncons::bson::bson_type::undefined_type); + break; + default: + before_value(jsoncons::bson::bson_type::null_type); + break; + } + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::bool_type); + if (val) + { + buffer_.push_back(0x01); + } + else + { + buffer_.push_back(0x00); + } + + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::float128: + { + before_value(jsoncons::bson::bson_type::decimal128_type); + decimal128_t dec; + auto rc = decimal128_from_chars(sv.data(), sv.data()+sv.size(), dec); + if (rc.ec != std::errc()) + { + ec = bson_errc::invalid_decimal128_string; + return false; + } + binary::native_to_little(dec.low,std::back_inserter(buffer_)); + binary::native_to_little(dec.high,std::back_inserter(buffer_)); + break; + } + case semantic_tag::id: + { + before_value(jsoncons::bson::bson_type::object_id_type); + oid_t oid(sv); + for (auto b : oid) + { + buffer_.push_back(b); + } + break; + } + case semantic_tag::regex: + { + before_value(jsoncons::bson::bson_type::regex_type); + std::size_t first = sv.find_first_of('/'); + std::size_t last = sv.find_last_of('/'); + if (first == string_view::npos || last == string_view::npos || first == last) + { + ec = bson_errc::invalid_regex_string; + return false; + } + string_view regex = sv.substr(first+1,last-1); + for (auto c : regex) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + string_view options = sv.substr(last+1); + for (auto c : options) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + break; + } + default: + switch (tag) + { + case semantic_tag::code: + before_value(jsoncons::bson::bson_type::javascript_type); + break; + default: + before_value(jsoncons::bson::bson_type::string_type); + break; + } + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + ec = bson_errc::invalid_utf8_text_string; + return false; + } + for (auto c : sv) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + std::size_t length = buffer_.size() - string_offset; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + break; + } + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(0x80); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(static_cast(ext_tag)); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + + return true; + } + + bool visit_int64(int64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr int64_t min_value_div_1000 = (std::numeric_limits::min)() / 1000; + static constexpr int64_t max_value_div_1000 = (std::numeric_limits::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val < min_value_div_1000) + { + ec = bson_errc::datetime_too_small; + return false; + } + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val*millis_in_second,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_milli; + } + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + default: + { + if (val >= (std::numeric_limits::lowest)() && val <= (std::numeric_limits::max)()) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + } + else + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + } + return true; + } + } + } + + bool visit_uint64(uint64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr uint64_t max_value_div_1000 = (std::numeric_limits::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast(val*millis_in_second),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_second; + } + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + default: + { + bool more; + if (val <= static_cast((std::numeric_limits::max)())) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + more = true; + } + else if (val <= static_cast((std::numeric_limits::max)())) + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + more = true; + } + else + { + ec = bson_errc::number_too_large; + more = false; + } + return more; + } + } + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::double_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + } + + void before_value(uint8_t code) + { + JSONCONS_ASSERT(!stack_.empty()); + if (stack_.back().is_object()) + { + buffer_[stack_.back().member_offset()] = code; + } + else + { + buffer_.push_back(code); + std::string name = std::to_string(stack_.back().next_index()); + buffer_.insert(buffer_.end(), name.begin(), name.end()); + buffer_.push_back(0x00); + } + } +}; + +using bson_stream_encoder = basic_bson_encoder; +using bson_bytes_encoder = basic_bson_encoder>>; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +using basic_bson_serializer = basic_bson_encoder; + +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_encoder") typedef bson_bytes_encoder bson_buffer_serializer; + +#endif + +}} +#endif diff --git a/include/jsoncons_ext/bson/bson_error.hpp b/include/jsoncons_ext/bson/bson_error.hpp new file mode 100644 index 0000000..85cf24d --- /dev/null +++ b/include/jsoncons_ext/bson/bson_error.hpp @@ -0,0 +1,103 @@ +/// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_ERROR_HPP +#define JSONCONS_BSON_BSON_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace bson { + +enum class bson_errc +{ + success = 0, + unexpected_eof = 1, + source_error, + invalid_utf8_text_string, + max_nesting_depth_exceeded, + string_length_is_non_positive, + length_is_negative, + number_too_large, + invalid_decimal128_string, + datetime_too_small, + datetime_too_large, + expected_bson_document, + invalid_regex_string, + size_mismatch, + unknown_type +}; + +class bson_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/bson"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case bson_errc::unexpected_eof: + return "Unexpected end of file"; + case bson_errc::source_error: + return "Source error"; + case bson_errc::invalid_utf8_text_string: + return "Illegal UTF-8 encoding in text string"; + case bson_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case bson_errc::string_length_is_non_positive: + return "Request for the length of a string returned a non-positive result"; + case bson_errc::length_is_negative: + return "Request for the length of a binary returned a negative result"; + case bson_errc::unknown_type: + return "An unknown type was found in the stream"; + case bson_errc::number_too_large: + return "Number too large"; + case bson_errc::invalid_decimal128_string: + return "Invalid decimal128 string"; + case bson_errc::datetime_too_large: + return "datetime too large"; + case bson_errc::datetime_too_small: + return "datetime too small"; + case bson_errc::expected_bson_document: + return "Expected BSON document"; + case bson_errc::invalid_regex_string: + return "Invalid regex string"; + case bson_errc::size_mismatch: + return "Document or array size doesn't match bytes read"; + default: + return "Unknown BSON parser error"; + } + } +}; + +inline +const std::error_category& bson_error_category() +{ + static bson_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(bson_errc result) +{ + return std::error_code(static_cast(result),bson_error_category()); +} + + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/bson/bson_oid.hpp b/include/jsoncons_ext/bson/bson_oid.hpp new file mode 100644 index 0000000..065d8da --- /dev/null +++ b/include/jsoncons_ext/bson/bson_oid.hpp @@ -0,0 +1,245 @@ +#ifndef JSONCONS_BSON_BSON_OID_HPP +#define JSONCONS_BSON_BSON_OID_HPP + +/* + * Implements class oid_t and non member function bson_oid_to_string + * + * Based on the libjson functions bson_oid_to_string + * and bson_oid_init_from_string_unsafe , available at + * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.h + * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.c + * +*/ + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + + class oid_t + { + std::array bytes_; + public: + using iterator = std::array::iterator; + using const_iterator = std::array::const_iterator; + + oid_t(const std::array& bytes) + : bytes_(bytes) + { + } + oid_t(uint8_t data[12]) + { + std::memcpy(bytes_.data(),data,12); + } + + oid_t(const string_view& str) + { + for (std::size_t i = 0; i < bytes_.size(); i++) + { + bytes_[i] = ((parse_hex_char (str[2 * i]) << 4) | + (parse_hex_char (str[2 * i + 1]))); + } + } + + const uint8_t* data() const + { + return bytes_.data(); + } + + std::size_t size() const + { + return bytes_.size(); + } + + iterator begin() + { + return bytes_.begin(); + } + + iterator end() + { + return bytes_.end(); + } + + const_iterator begin() const + { + return bytes_.begin(); + } + + const_iterator end() const + { + return bytes_.end(); + } + + private: + + static uint8_t parse_hex_char (char hex) + { + switch (hex) { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + case 'a': + case 'A': + return 0xa; + case 'b': + case 'B': + return 0xb; + case 'c': + case 'C': + return 0xc; + case 'd': + case 'D': + return 0xd; + case 'e': + case 'E': + return 0xe; + case 'f': + case 'F': + return 0xf; + default: + return 0; + } + } + }; + + namespace detail { + + inline + const uint16_t* get_hex_char_pairs(std::true_type) // big endian + { + static const uint16_t hex_char_pairs[] = { + 12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345, 12385, + 12386, 12387, 12388, 12389, 12390, 12592, 12593, 12594, 12595, 12596, 12597, + 12598, 12599, 12600, 12601, 12641, 12642, 12643, 12644, 12645, 12646, 12848, + 12849, 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12897, 12898, + 12899, 12900, 12901, 12902, 13104, 13105, 13106, 13107, 13108, 13109, 13110, + 13111, 13112, 13113, 13153, 13154, 13155, 13156, 13157, 13158, 13360, 13361, + 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13409, 13410, 13411, + 13412, 13413, 13414, 13616, 13617, 13618, 13619, 13620, 13621, 13622, 13623, + 13624, 13625, 13665, 13666, 13667, 13668, 13669, 13670, 13872, 13873, 13874, + 13875, 13876, 13877, 13878, 13879, 13880, 13881, 13921, 13922, 13923, 13924, + 13925, 13926, 14128, 14129, 14130, 14131, 14132, 14133, 14134, 14135, 14136, + 14137, 14177, 14178, 14179, 14180, 14181, 14182, 14384, 14385, 14386, 14387, + 14388, 14389, 14390, 14391, 14392, 14393, 14433, 14434, 14435, 14436, 14437, + 14438, 14640, 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649, + 14689, 14690, 14691, 14692, 14693, 14694, 24880, 24881, 24882, 24883, 24884, + 24885, 24886, 24887, 24888, 24889, 24929, 24930, 24931, 24932, 24933, 24934, + 25136, 25137, 25138, 25139, 25140, 25141, 25142, 25143, 25144, 25145, 25185, + 25186, 25187, 25188, 25189, 25190, 25392, 25393, 25394, 25395, 25396, 25397, + 25398, 25399, 25400, 25401, 25441, 25442, 25443, 25444, 25445, 25446, 25648, + 25649, 25650, 25651, 25652, 25653, 25654, 25655, 25656, 25657, 25697, 25698, + 25699, 25700, 25701, 25702, 25904, 25905, 25906, 25907, 25908, 25909, 25910, + 25911, 25912, 25913, 25953, 25954, 25955, 25956, 25957, 25958, 26160, 26161, + 26162, 26163, 26164, 26165, 26166, 26167, 26168, 26169, 26209, 26210, 26211, + 26212, 26213, 26214}; + + return hex_char_pairs; + } + + inline + const uint16_t* get_hex_char_pairs(std::false_type) // little endian + { + static const uint16_t hex_char_pairs[] = { + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, + 25136, 25392, 25648, 25904, 26160, 12337, 12593, 12849, 13105, 13361, 13617, + 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, 12338, + 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, + 25394, 25650, 25906, 26162, 12339, 12595, 12851, 13107, 13363, 13619, 13875, + 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, 12340, 12596, + 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, + 25652, 25908, 26164, 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, + 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854, + 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, + 25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, + 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600, 12856, 13112, + 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, + 26168, 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, + 24889, 25145, 25401, 25657, 25913, 26169, 12385, 12641, 12897, 13153, 13409, + 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, + 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, + 25186, 25442, 25698, 25954, 26210, 12387, 12643, 12899, 13155, 13411, 13667, + 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388, + 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, + 25444, 25700, 25956, 26212, 12389, 12645, 12901, 13157, 13413, 13669, 13925, + 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, 12390, 12646, + 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, + 25702, 25958, 26214}; + + return hex_char_pairs; + } + + inline + void init_hex_char_pairs(const oid_t& oid, uint16_t* data) + { + const uint8_t* bytes = oid.data(); + const uint16_t* gHexCharPairs = get_hex_char_pairs(std::integral_constant()); + + data[0] = gHexCharPairs[bytes[0]]; + data[1] = gHexCharPairs[bytes[1]]; + data[2] = gHexCharPairs[bytes[2]]; + data[3] = gHexCharPairs[bytes[3]]; + data[4] = gHexCharPairs[bytes[4]]; + data[5] = gHexCharPairs[bytes[5]]; + data[6] = gHexCharPairs[bytes[6]]; + data[7] = gHexCharPairs[bytes[7]]; + data[8] = gHexCharPairs[bytes[8]]; + data[9] = gHexCharPairs[bytes[9]]; + data[10] = gHexCharPairs[bytes[10]]; + data[11] = gHexCharPairs[bytes[11]]; + } + + } // namsepace detail + + template + inline + void to_string(const oid_t& oid, StringT& s) + { + s.resize(24); + detail::init_hex_char_pairs(oid, reinterpret_cast(&s[0])); + } + +} // namespace bson +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/bson_options.hpp b/include/jsoncons_ext/bson/bson_options.hpp new file mode 100644 index 0000000..0e4620e --- /dev/null +++ b/include/jsoncons_ext/bson/bson_options.hpp @@ -0,0 +1,75 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_OPTIONS_HPP +#define JSONCONS_BSON_BSON_OPTIONS_HPP + +#include +#include // std::numeric_limits +#include +#include +#include + +namespace jsoncons { namespace bson { + +class bson_options; + +class bson_options_common +{ + friend class bson_options; + + int max_nesting_depth_; +protected: + virtual ~bson_options_common() = default; + + bson_options_common() + : max_nesting_depth_(1024) + { + } + + bson_options_common(const bson_options_common&) = default; + bson_options_common& operator=(const bson_options_common&) = default; + bson_options_common(bson_options_common&&) = default; + bson_options_common& operator=(bson_options_common&&) = default; +public: + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class bson_decode_options : public virtual bson_options_common +{ + friend class bson_options; +public: + bson_decode_options() + { + } +}; + +class bson_encode_options : public virtual bson_options_common +{ + friend class bson_options; +public: + bson_encode_options() + { + } +}; + +class bson_options final : public bson_decode_options, public bson_encode_options +{ +public: + using bson_options_common::max_nesting_depth; + + bson_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } +}; + +}} +#endif diff --git a/include/jsoncons_ext/bson/bson_parser.hpp b/include/jsoncons_ext/bson/bson_parser.hpp new file mode 100644 index 0000000..2dc6e75 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_parser.hpp @@ -0,0 +1,645 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_PARSER_HPP +#define JSONCONS_BSON_BSON_PARSER_HPP + +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + +enum class parse_mode {root,accept,document,array,value}; + +struct parse_state +{ + parse_mode mode; + std::size_t length; + std::size_t pos; + uint8_t type; + std::size_t index; + + parse_state(parse_mode mode_, std::size_t length_, std::size_t pos_, uint8_t type_ = 0) noexcept + : mode(mode_), length(length_), pos(pos_), type(type_), index(0) + { + } + + parse_state(const parse_state&) = default; + parse_state(parse_state&&) = default; + parse_state& operator=(const parse_state&) = default; + parse_state& operator=(parse_state&&) = default; +}; + +template > +class basic_bson_parser : public ser_context +{ + using char_type = char; + using char_traits_type = std::char_traits; + using temp_allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + Source source_; + bson_decode_options options_; + bool more_; + bool done_; + std::vector bytes_buffer_; + std::basic_string,char_allocator_type> text_buffer_; + std::vector state_stack_; +public: + template + basic_bson_parser(Sourceable&& source, + const bson_decode_options& options = bson_decode_options(), + const Allocator alloc = Allocator()) + : source_(std::forward(source)), + options_(options), + more_(true), + done_(false), + text_buffer_(alloc), + state_stack_(alloc) + { + state_stack_.emplace_back(parse_mode::root,0,0); + } + + void restart() + { + more_ = true; + } + + void reset() + { + more_ = true; + done_ = false; + bytes_buffer_.clear(); + text_buffer_.clear(); + state_stack_.clear(); + state_stack_.emplace_back(parse_mode::root,0,0); + } + + template + void reset(Sourceable&& source) + { + source_ = std::forward(source); + reset(); + } + + bool done() const + { + return done_; + } + + bool stopped() const + { + return !more_; + } + + std::size_t line() const override + { + return 0; + } + + std::size_t column() const override + { + return source_.position(); + } + + void array_expected(json_visitor& visitor, std::error_code& ec) + { + if (state_stack_.size() == 2 && state_stack_.back().mode == parse_mode::document) + { + state_stack_.back().mode = parse_mode::array; + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + } + } + + void parse(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(source_.is_error())) + { + ec = bson_errc::source_error; + more_ = false; + return; + } + + while (!done_ && more_) + { + switch (state_stack_.back().mode) + { + case parse_mode::root: + state_stack_.back().mode = parse_mode::accept; + begin_document(visitor, ec); + break; + case parse_mode::document: + { + uint8_t type; + std::size_t n = source_.read(&type, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (type != 0x00) + { + read_e_name(visitor,jsoncons::bson::bson_container_type::document,ec); + state_stack_.back().mode = parse_mode::value; + state_stack_.back().type = type; + } + else + { + end_document(visitor,ec); + } + break; + } + case parse_mode::array: + { + uint8_t type; + std::size_t n = source_.read(&type, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (type != 0x00) + { + read_e_name(visitor,jsoncons::bson::bson_container_type::array,ec); + read_value(visitor, type, ec); + } + else + { + end_array(visitor,ec); + } + break; + } + case parse_mode::value: + state_stack_.back().mode = parse_mode::document; + read_value(visitor,state_stack_.back().type,ec); + break; + case parse_mode::accept: + { + JSONCONS_ASSERT(state_stack_.size() == 1); + state_stack_.clear(); + more_ = false; + done_ = true; + visitor.flush(); + break; + } + } + } + } + +private: + + void begin_document(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + + uint8_t buf[sizeof(int32_t)]; + size_t n = source_.read(buf, sizeof(int32_t)); + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + auto length = binary::little_to_native(buf, sizeof(buf)); + + more_ = visitor.begin_object(semantic_tag::none, *this, ec); + state_stack_.emplace_back(parse_mode::document,length,n); + } + + void end_document(json_visitor& visitor, std::error_code& ec) + { + JSONCONS_ASSERT(state_stack_.size() >= 2); + + more_ = visitor.end_object(*this,ec); + if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length)) + { + ec = bson_errc::size_mismatch; + more_ = false; + return; + } + std::size_t pos = state_stack_.back().pos; + state_stack_.pop_back(); + state_stack_.back().pos += pos; + } + + void begin_array(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto length = binary::little_to_native(buf, sizeof(buf)); + + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + if (ec) + { + return; + } + state_stack_.emplace_back(parse_mode::array, length, n); + } + + void end_array(json_visitor& visitor, std::error_code& ec) + { + JSONCONS_ASSERT(state_stack_.size() >= 2); + + more_ = visitor.end_array(*this, ec); + if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length)) + { + ec = bson_errc::size_mismatch; + more_ = false; + return; + } + std::size_t pos = state_stack_.back().pos; + state_stack_.pop_back(); + state_stack_.back().pos += pos; + } + + void read_e_name(json_visitor& visitor, jsoncons::bson::bson_container_type type, std::error_code& ec) + { + text_buffer_.clear(); + read_cstring(ec); + if (ec) + { + return; + } + if (type == jsoncons::bson::bson_container_type::document) + { + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.key(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), *this, ec); + } + } + + void read_value(json_visitor& visitor, uint8_t type, std::error_code& ec) + { + switch (type) + { + case jsoncons::bson::bson_type::double_type: + { + uint8_t buf[sizeof(double)]; + std::size_t n = source_.read(buf, sizeof(double)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(double))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + double res = binary::little_to_native(buf, sizeof(buf)); + more_ = visitor.double_value(res, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::symbol_type: + case jsoncons::bson::bson_type::min_key_type: + case jsoncons::bson::bson_type::max_key_type: + case jsoncons::bson::bson_type::string_type: + { + text_buffer_.clear(); + read_string(ec); + if (ec) + { + return; + } + auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::javascript_type: + { + text_buffer_.clear(); + read_string(ec); + if (ec) + { + return; + } + auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size()); + if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc())) + { + ec = bson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::code, *this, ec); + break; + } + case jsoncons::bson::bson_type::regex_type: + { + text_buffer_.clear(); + text_buffer_.push_back('/'); + read_cstring(ec); + if (ec) + { + return; + } + text_buffer_.push_back('/'); + read_cstring(ec); + if (ec) + { + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::regex, *this, ec); + break; + } + case jsoncons::bson::bson_type::document_type: + { + begin_document(visitor,ec); + break; + } + + case jsoncons::bson::bson_type::array_type: + { + begin_array(visitor,ec); + break; + } + case jsoncons::bson::bson_type::undefined_type: + { + more_ = visitor.null_value(semantic_tag::undefined, *this, ec); + break; + } + case jsoncons::bson::bson_type::null_type: + { + more_ = visitor.null_value(semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::bool_type: + { + uint8_t c; + std::size_t n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + more_ = visitor.bool_value(c != 0, semantic_tag::none, *this, ec); + break; + } + case jsoncons::bson::bson_type::int32_type: + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::timestamp_type: + { + uint8_t buf[sizeof(uint64_t)]; + std::size_t n = source_.read(buf, sizeof(uint64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(uint64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native(buf, sizeof(buf)); + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::int64_type: + { + uint8_t buf[sizeof(int64_t)]; + std::size_t n = source_.read(buf, sizeof(int64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::bson::bson_type::datetime_type: + { + uint8_t buf[sizeof(int64_t)]; + std::size_t n = source_.read(buf, sizeof(int64_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int64_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto val = binary::little_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::epoch_milli, *this, ec); + break; + } + case jsoncons::bson::bson_type::binary_type: + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + const auto len = binary::little_to_native(buf, sizeof(buf)); + if (JSONCONS_UNLIKELY(len < 0)) + { + ec = bson_errc::length_is_negative; + more_ = false; + return; + } + uint8_t subtype; + n = source_.read(&subtype, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + bytes_buffer_.clear(); + n = source_reader::read(source_, bytes_buffer_, len); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != static_cast(len))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + more_ = visitor.byte_string_value(bytes_buffer_, + subtype, + *this, + ec); + break; + } + case jsoncons::bson::bson_type::decimal128_type: + { + uint8_t buf[sizeof(uint64_t)*2]; + std::size_t n = source_.read(buf, sizeof(buf)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(buf))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + decimal128_t dec; + dec.low = binary::little_to_native(buf, sizeof(uint64_t)); + dec.high = binary::little_to_native(buf+sizeof(uint64_t), sizeof(uint64_t)); + + text_buffer_.clear(); + text_buffer_.resize(bson::decimal128_limits::buf_size); + auto r = bson::decimal128_to_chars(&text_buffer_[0], &text_buffer_[0]+text_buffer_.size(), dec); + more_ = visitor.string_value(string_view(text_buffer_.data(),static_cast(r.ptr-text_buffer_.data())), semantic_tag::float128, *this, ec); + break; + } + case jsoncons::bson::bson_type::object_id_type: + { + uint8_t buf[12]; + std::size_t n = source_.read(buf, sizeof(buf)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(buf))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + + oid_t oid(buf); + to_string(oid, text_buffer_); + + more_ = visitor.string_value(text_buffer_, semantic_tag::id, *this, ec); + break; + } + default: + { + ec = bson_errc::unknown_type; + more_ = false; + return; + } + } + } + + void read_cstring(std::error_code& ec) + { + uint8_t c = 0xff; + while (true) + { + std::size_t n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + if (c == 0) + { + break; + } + text_buffer_.push_back(c); + } + } + + void read_string(std::error_code& ec) + { + uint8_t buf[sizeof(int32_t)]; + std::size_t n = source_.read(buf, sizeof(int32_t)); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != sizeof(int32_t))) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + auto len = binary::little_to_native(buf, sizeof(buf)); + if (JSONCONS_UNLIKELY(len < 1)) + { + ec = bson_errc::string_length_is_non_positive; + more_ = false; + return; + } + + std::size_t size = static_cast(len) - static_cast(1); + n = source_reader::read(source_, text_buffer_, size); + state_stack_.back().pos += n; + + if (JSONCONS_UNLIKELY(n != size)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + uint8_t c; + n = source_.read(&c, 1); + state_stack_.back().pos += n; + if (JSONCONS_UNLIKELY(n != 1)) + { + ec = bson_errc::unexpected_eof; + more_ = false; + return; + } + } +}; + +}} + +#endif diff --git a/include/jsoncons_ext/bson/bson_reader.hpp b/include/jsoncons_ext/bson/bson_reader.hpp new file mode 100644 index 0000000..0079cc9 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_reader.hpp @@ -0,0 +1,92 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_READER_HPP +#define JSONCONS_BSON_BSON_READER_HPP + +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + +template > +class basic_bson_reader +{ + basic_bson_parser parser_; + json_visitor& visitor_; +public: + template + basic_bson_reader(Sourceable&& source, + json_visitor& visitor, + const Allocator alloc) + : basic_bson_reader(std::forward(source), + visitor, + bson_decode_options(), + alloc) + { + } + + template + basic_bson_reader(Sourceable&& source, + json_visitor& visitor, + const bson_decode_options& options = bson_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + visitor_(visitor) + { + } + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line(),column())); + } + } + + void read(std::error_code& ec) + { + parser_.reset(); + parser_.parse(visitor_, ec); + if (ec) + { + return; + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } +}; + +using bson_stream_reader = basic_bson_reader; +using bson_bytes_reader = basic_bson_reader; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_reader") typedef bson_stream_reader bson_reader; +JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_reader") typedef bson_bytes_reader bson_buffer_reader; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/bson/bson_type.hpp b/include/jsoncons_ext/bson/bson_type.hpp new file mode 100644 index 0000000..cf12344 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_type.hpp @@ -0,0 +1,44 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_TYPE_HPP +#define JSONCONS_BSON_BSON_TYPE_HPP + +#include +#include +#include + +namespace jsoncons { namespace bson { + + namespace bson_type + { + const uint8_t double_type = 0x01; + const uint8_t string_type = 0x02; // UTF-8 string + const uint8_t document_type = 0x03; + const uint8_t array_type = 0x04; + const uint8_t binary_type = 0x05; + const uint8_t undefined_type = 0x06; // map to null + const uint8_t object_id_type = 0x07; + const uint8_t bool_type = 0x08; + const uint8_t datetime_type = 0x09; + const uint8_t null_type = 0x0a; + const uint8_t regex_type = 0x0b; + const uint8_t javascript_type = 0x0d; + const uint8_t symbol_type = 0x0e; // deprecated, mapped to string + const uint8_t javascript_with_scope_type = 0x0f; // unsupported + const uint8_t int32_type = 0x10; + const uint8_t timestamp_type = 0x11; // MongoDB internal Timestamp, uint64 + const uint8_t int64_type = 0x12; + const uint8_t decimal128_type = 0x13; + const uint8_t min_key_type = 0xff; + const uint8_t max_key_type = 0x7f; + } + + enum class bson_container_type {document, array}; + +}} + +#endif diff --git a/include/jsoncons_ext/bson/decode_bson.hpp b/include/jsoncons_ext/bson/decode_bson.hpp new file mode 100644 index 0000000..ad352f6 --- /dev/null +++ b/include/jsoncons_ext/bson/decode_bson.hpp @@ -0,0 +1,201 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_DECODE_BSON_HPP +#define JSONCONS_BSON_DECODE_BSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include + +namespace jsoncons { +namespace bson { + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_bson(const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(v, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_bson(const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor cursor(v, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + bson_stream_reader reader(is, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor cursor(is, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor> cursor(binary_iterator_source(first, last), options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(v, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor cursor(v, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(is, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) + { + basic_bson_cursor cursor(is, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // bson +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/bson/encode_bson.hpp b/include/jsoncons_ext/bson/encode_bson.hpp new file mode 100644 index 0000000..55f8cf5 --- /dev/null +++ b/include/jsoncons_ext/bson/encode_bson.hpp @@ -0,0 +1,144 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_ENCODE_BSON_HPP +#define JSONCONS_BSON_ENCODE_BSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include + +namespace jsoncons { +namespace bson { + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_bson(const T& j, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder> encoder(v, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_bson(const T& val, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder> encoder(v, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_bson(const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + bson_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_bson(const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + bson_stream_encoder encoder(os, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // with temp_allocator_rag + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder,TempAllocator> encoder(v, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& v, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder,TempAllocator> encoder(v, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + using char_type = typename T::char_type; + basic_bson_encoder encoder(os, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) + { + basic_bson_encoder encoder(os, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // bson +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/cbor/cbor.hpp b/include/jsoncons_ext/cbor/cbor.hpp new file mode 100644 index 0000000..3f7329f --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor.hpp @@ -0,0 +1,26 @@ +// Copyright 2017 Daniel Parkerstd +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + + +#ifndef JSONCONS_CBOR_CBOR_HPP +#define JSONCONS_CBOR_CBOR_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/include/jsoncons_ext/cbor/cbor_cursor.hpp b/include/jsoncons_ext/cbor/cbor_cursor.hpp new file mode 100644 index 0000000..af0d1a8 --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_cursor.hpp @@ -0,0 +1,351 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_CURSOR_HPP +#define JSONCONS_CBOR_CBOR_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace cbor { + +template> +class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_context +{ +public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; +private: + basic_cbor_parser parser_; + basic_staj_visitor cursor_visitor_; + basic_json_visitor2_to_visitor_adaptor cursor_handler_adaptor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_cbor_cursor(const basic_cbor_cursor&) = delete; + basic_cbor_cursor& operator=(const basic_cbor_cursor&) = delete; + +public: + using string_view_type = string_view; + + template + basic_cbor_cursor(Sourceable&& source, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_cbor_cursor(Sourceable&& source, + std::error_code& ec) + : basic_cbor_cursor(std::allocator_arg, Allocator(), + std::forward(source), + cbor_decode_options(), + ec) + { + } + + template + basic_cbor_cursor(Sourceable&& source, + const cbor_decode_options& options, + std::error_code& ec) + : basic_cbor_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_cbor_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const cbor_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + bool is_typed_array() const + { + return cursor_visitor_.is_typed_array(); + } + + const staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (cursor_visitor_.dump(visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj_filter_view operator|(basic_cbor_cursor& cursor, + std::function pred) + { + return staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_cbor_cursor(Sourceable&& source, + std::function filter, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(filter), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(); + } + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_cbor_cursor(Sourceable&& source, + std::function filter, + std::error_code& ec) + : basic_cbor_cursor(std::allocator_arg, Allocator(), + std::forward(source), filter, ec) + { + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_cbor_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source), alloc), + cursor_visitor_(filter), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&)") + void read(basic_json_visitor& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&, std::error_code&)") + void read(basic_json_visitor& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + static bool accept_all(const staj_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + if (cursor_visitor_.in_available()) + { + cursor_visitor_.send_available(ec); + } + else + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_handler_adaptor_, ec); + if (ec) return; + } + } + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + { + struct resource_wrapper + { + basic_json_visitor2_to_visitor_adaptor& adaptor; + basic_json_visitor& original; + + resource_wrapper(basic_json_visitor2_to_visitor_adaptor& adaptor, + basic_json_visitor& visitor) + : adaptor(adaptor), original(adaptor.destination()) + { + adaptor.destination(visitor); + } + + ~resource_wrapper() + { + adaptor.destination(original); + } + } wrapper(cursor_handler_adaptor_, visitor); + + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_handler_adaptor_, ec); + if (ec) + { + return; + } + } + } + } +}; + +using cbor_stream_cursor = basic_cbor_cursor; +using cbor_bytes_cursor = basic_cbor_cursor; + +} // namespace cbor +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/cbor/cbor_cursor2.hpp b/include/jsoncons_ext/cbor/cbor_cursor2.hpp new file mode 100644 index 0000000..eee7445 --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_cursor2.hpp @@ -0,0 +1,265 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_CURSOR2_HPP +#define JSONCONS_CBOR_CBOR_CURSOR2_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace cbor { + + template> + class basic_cbor_cursor2 : public basic_staj2_cursor, private virtual ser_context + { + public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; + private: + basic_cbor_parser parser_; + basic_staj2_visitor cursor_visitor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_cbor_cursor2(const basic_cbor_cursor2&) = delete; + basic_cbor_cursor2& operator=(const basic_cbor_cursor2&) = delete; + + public: + using string_view_type = string_view; + + template + basic_cbor_cursor2(Sourceable&& source, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_cbor_cursor2(Sourceable&& source, + std::error_code& ec) + : basic_cbor_cursor2(std::allocator_arg, Allocator(), + std::forward(source), + cbor_decode_options(), + ec) + { + } + + template + basic_cbor_cursor2(Sourceable&& source, + const cbor_decode_options& options, + std::error_code& ec) + : basic_cbor_cursor2(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_cbor_cursor2(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const cbor_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + bool is_typed_array() const + { + return cursor_visitor_.is_typed_array(); + } + + const staj2_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor2& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor2& visitor, + std::error_code& ec) override + { + if (cursor_visitor_.dump(visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj2_filter_view operator|(basic_cbor_cursor2& cursor, + std::function pred) + { + return staj2_filter_view(cursor, pred); + } + + private: + static bool accept_all(const staj2_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + if (cursor_visitor_.in_available()) + { + cursor_visitor_.send_available(ec); + } + else + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_visitor_, ec); + if (ec) return; + } + } + } + + void read_next(basic_json_visitor2& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(visitor, ec); + if (ec) + { + return; + } + } + } + }; + + using cbor_stream_cursor2 = basic_cbor_cursor2; + using cbor_bytes_cursor2 = basic_cbor_cursor2; + +} // namespace cbor +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/cbor/cbor_detail.hpp b/include/jsoncons_ext/cbor/cbor_detail.hpp new file mode 100644 index 0000000..9acfc3c --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_detail.hpp @@ -0,0 +1,93 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_DETAIL_HPP +#define JSONCONS_CBOR_CBOR_DETAIL_HPP + +#include +#include +#include +#include // std::forward_iterator_tag +#include // std::numeric_limits +#include // std::move +#include +#include +#include + +// 0x00..0x17 (0..23) +#define JSONCONS_CBOR_0x00_0x17 \ + 0x00:case 0x01:case 0x02:case 0x03:case 0x04:case 0x05:case 0x06:case 0x07:case 0x08:case 0x09:case 0x0a:case 0x0b:case 0x0c:case 0x0d:case 0x0e:case 0x0f:case 0x10:case 0x11:case 0x12:case 0x13:case 0x14:case 0x15:case 0x16:case 0x17 + +#define JSONCONS_CBOR_ARRAY_TAGS \ + 0x40:case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:case 0x47:case 0x48:case 0x49:case 0x4a:case 0x4b:case 0x4c:case 0x4d:case 0x4e:case 0x4f:case 0x50:case 0x51:case 0x52:case 0x53:case 0x54:case 0x55:case 0x56:case 0x57 + +namespace jsoncons { namespace cbor { namespace detail { + +//const uint8_t cbor_array_tags_010_mask = 0b11100000; +//const uint8_t cbor_array_tags_f_mask = 0b00010000; +//const uint8_t cbor_array_tags_s_mask = 0b00001000; +//const uint8_t cbor_array_tags_e_mask = 0b00000100; +//const uint8_t cbor_array_tags_ll_mask = 0b00000011; + +const uint8_t cbor_array_tags_010_mask = 0xE0; +const uint8_t cbor_array_tags_f_mask = 0x10; +const uint8_t cbor_array_tags_s_mask = 0x08; +const uint8_t cbor_array_tags_e_mask = 0x04; +const uint8_t cbor_array_tags_ll_mask = 0x03; + +const uint8_t cbor_array_tags_010_shift = 5; +const uint8_t cbor_array_tags_f_shift = 4; +const uint8_t cbor_array_tags_s_shift = 3; +const uint8_t cbor_array_tags_e_shift = 2; +const uint8_t cbor_array_tags_ll_shift = 0; + +enum class cbor_major_type : uint8_t +{ + unsigned_integer = 0x00, + negative_integer = 0x01, + byte_string = 0x02, + text_string = 0x03, + array = 0x04, + map = 0x05, + semantic_tag = 0x06, + simple = 0x7 +}; + +namespace additional_info +{ + const uint8_t indefinite_length = 0x1f; +} + +inline +size_t min_length_for_stringref(uint64_t index) +{ + std::size_t n; + if (index <= 23) + { + n = 3; + } + else if (index <= 255) + { + n = 4; + } + else if (index <= 65535) + { + n = 5; + } + else if (index <= 4294967295) + { + n = 7; + } + else + { + n = 11; + } + return n; +} + +}}} + +#endif diff --git a/include/jsoncons_ext/cbor/cbor_encoder.hpp b/include/jsoncons_ext/cbor/cbor_encoder.hpp new file mode 100644 index 0000000..f4699ee --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_encoder.hpp @@ -0,0 +1,1766 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_ENCODER_HPP +#define JSONCONS_CBOR_CBOR_ENCODER_HPP + +#include +#include +#include // std::numeric_limits +#include +#include // std::move +#include // jsoncons::ser_error +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace cbor { + +enum class cbor_container_type {object, indefinite_length_object, array, indefinite_length_array}; + +template> +class basic_cbor_encoder final : public basic_json_visitor +{ + using super_type = basic_json_visitor; + + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; + enum class hexfloat_parse_state { start, expect_0, expect_x, integer, exp1, exp2, fraction1 }; + + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; + +public: + using allocator_type = Allocator; + using sink_type = Sink; + using typename super_type::char_type; + using typename super_type::string_view_type; + +private: + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + using string_type = std::basic_string,char_allocator_type>; + using byte_string_type = basic_byte_string; + + struct stack_item + { + cbor_container_type type_; + std::size_t length_; + std::size_t count_; + + stack_item(cbor_container_type type, std::size_t length = 0) noexcept + : type_(type), length_(length), count_(0) + { + } + + std::size_t length() const + { + return length_; + } + + std::size_t count() const + { + return count_; + } + + bool is_object() const + { + return type_ == cbor_container_type::object || type_ == cbor_container_type::indefinite_length_object; + } + + bool is_indefinite_length() const + { + return type_ == cbor_container_type::indefinite_length_array || type_ == cbor_container_type::indefinite_length_object; + } + + }; + + typedef typename std::allocator_traits:: template rebind_alloc> string_size_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc> byte_string_size_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc stack_item_allocator_type; + + Sink sink_; + const cbor_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + std::map,string_size_allocator_type> stringref_map_; + std::map,byte_string_size_allocator_type> bytestringref_map_; + std::size_t next_stringref_ = 0; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_cbor_encoder(const basic_cbor_encoder&) = delete; + basic_cbor_encoder& operator=(const basic_cbor_encoder&) = delete; +public: + explicit basic_cbor_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_cbor_encoder(std::forward(sink), cbor_encode_options(), alloc) + { + } + basic_cbor_encoder(Sink&& sink, + const cbor_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + stack_(alloc), +#if !defined(JSONCONS_NO_MAP_CONS_TAKES_ALLOCATOR) + stringref_map_(alloc), + bytestringref_map_(alloc), +#endif + nesting_depth_(0) + { + if (options.pack_strings()) + { + write_tag(256); + } + } + + ~basic_cbor_encoder() noexcept + { + JSONCONS_TRY + { + sink_.flush(); + } + JSONCONS_CATCH(...) + { + } + } + + void reset() + { + stack_.clear(); + stringref_map_.clear(); + bytestringref_map_.clear(); + next_stringref_ = 0; + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + +private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(cbor_container_type::indefinite_length_object); + + sink_.push_back(0xbf); + return true; + } + + bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(cbor_container_type::object, length); + + if (length <= 0x17) + { + binary::native_to_big(static_cast(0xa0 + length), + std::back_inserter(sink_)); + } + else if (length <= 0xff) + { + binary::native_to_big(static_cast(0xb8), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffff) + { + binary::native_to_big(static_cast(0xb9), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffff) + { + binary::native_to_big(static_cast(0xba), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffffffffffff) + { + binary::native_to_big(static_cast(0xbb), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().is_indefinite_length()) + { + sink_.push_back(0xff); + } + else + { + if (stack_.back().count() < stack_.back().length()) + { + ec = cbor_errc::too_few_items; + return false; + } + if (stack_.back().count() > stack_.back().length()) + { + ec = cbor_errc::too_many_items; + return false; + } + } + + stack_.pop_back(); + end_value(); + + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(cbor_container_type::indefinite_length_array); + sink_.push_back(0x9f); + return true; + } + + bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(cbor_container_type::array, length); + if (length <= 0x17) + { + binary::native_to_big(static_cast(0x80 + length), + std::back_inserter(sink_)); + } + else if (length <= 0xff) + { + binary::native_to_big(static_cast(0x98), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffff) + { + binary::native_to_big(static_cast(0x99), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffff) + { + binary::native_to_big(static_cast(0x9a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffffffffffff) + { + binary::native_to_big(static_cast(0x9b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + return true; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().is_indefinite_length()) + { + sink_.push_back(0xff); + } + else + { + if (stack_.back().count() < stack_.back().length()) + { + ec = cbor_errc::too_few_items; + return false; + } + if (stack_.back().count() > stack_.back().length()) + { + ec = cbor_errc::too_many_items; + return false; + } + } + + stack_.pop_back(); + end_value(); + + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + write_string(name); + return true; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override + { + if (tag == semantic_tag::undefined) + { + sink_.push_back(0xf7); + } + else + { + sink_.push_back(0xf6); + } + + end_value(); + return true; + } + + void write_string(const string_view& sv) + { + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(cbor_errc::invalid_utf8_text_string)); + } + + if (options_.pack_strings() && sv.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + { + string_type s(sv.data(), sv.size(), alloc_); + auto it = stringref_map_.find(s); + if (it == stringref_map_.end()) + { + stringref_map_.emplace(std::make_pair(std::move(s), next_stringref_++)); + write_utf8_string(sv); + } + else + { + write_tag(25); + write_uint64_value(it->second); + } + } + else + { + write_utf8_string(sv); + } + } + + void write_utf8_string(const string_view& sv) + { + const size_t length = sv.size(); + + if (length <= 0x17) + { + // fixstr stores a byte array whose length is upto 31 bytes + binary::native_to_big(static_cast(0x60 + length), + std::back_inserter(sink_)); + } + else if (length <= 0xff) + { + binary::native_to_big(static_cast(0x78), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffff) + { + binary::native_to_big(static_cast(0x79), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffff) + { + binary::native_to_big(static_cast(0x7a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffffffffffff) + { + binary::native_to_big(static_cast(0x7b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + + for (auto c : sv) + { + sink_.push_back(c); + } + } + + void write_bignum(bigint& n) + { + bool is_neg = n < 0; + if (is_neg) + { + n = - n -1; + } + + int signum; + std::vector data; + n.write_bytes_be(signum, data); + std::size_t length = data.size(); + + if (is_neg) + { + write_tag(3); + } + else + { + write_tag(2); + } + + if (length <= 0x17) + { + // fixstr stores a byte array whose length is upto 31 bytes + binary::native_to_big(static_cast(0x40 + length), + std::back_inserter(sink_)); + } + else if (length <= 0xff) + { + binary::native_to_big(static_cast(0x58), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffff) + { + binary::native_to_big(static_cast(0x59), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffff) + { + binary::native_to_big(static_cast(0x5a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 0xffffffffffffffff) + { + binary::native_to_big(static_cast(0x5b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + + for (auto c : data) + { + sink_.push_back(c); + } + } + + bool write_decimal_value(const string_view_type& sv, const ser_context& context, std::error_code& ec) + { + bool more = true; + + decimal_parse_state state = decimal_parse_state::start; + std::basic_string s; + std::basic_string exponent; + int64_t scale = 0; + for (auto c : sv) + { + switch (state) + { + case decimal_parse_state::start: + { + switch (c) + { + case '-': + s.push_back(c); + state = decimal_parse_state::integer; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + s.push_back(c); + state = decimal_parse_state::integer; + break; + default: + { + ec = cbor_errc::invalid_decimal_fraction; + return false; + } + } + break; + } + case decimal_parse_state::integer: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + s.push_back(c); + break; + case 'e': case 'E': + state = decimal_parse_state::exp1; + break; + case '.': + state = decimal_parse_state::fraction1; + break; + default: + { + ec = cbor_errc::invalid_decimal_fraction; + return false; + } + } + break; + } + case decimal_parse_state::exp1: + { + switch (c) + { + case '+': + state = decimal_parse_state::exp2; + break; + case '-': + exponent.push_back(c); + state = decimal_parse_state::exp2; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + exponent.push_back(c); + state = decimal_parse_state::exp2; + break; + default: + { + ec = cbor_errc::invalid_decimal_fraction; + return false; + } + } + break; + } + case decimal_parse_state::exp2: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + exponent.push_back(c); + break; + default: + { + ec = cbor_errc::invalid_decimal_fraction; + return false; + } + } + break; + } + case decimal_parse_state::fraction1: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + s.push_back(c); + --scale; + break; + default: + { + ec = cbor_errc::invalid_decimal_fraction; + return false; + } + } + break; + } + } + } + + write_tag(4); + more = visit_begin_array((std::size_t)2, semantic_tag::none, context, ec); + if (!more) {return more;} + if (exponent.length() > 0) + { + int64_t val; + auto r = jsoncons::detail::to_integer(exponent.data(), exponent.length(), val); + if (!r) + { + ec = r.error_code(); + return false; + } + scale += val; + } + more = visit_int64(scale, semantic_tag::none, context, ec); + if (!more) {return more;} + + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(s.data(),s.length(), val); + if (r) + { + more = visit_int64(val, semantic_tag::none, context, ec); + if (!more) {return more;} + } + else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow) + { + bigint n = bigint::from_string(s.data(), s.length()); + write_bignum(n); + end_value(); + } + else + { + ec = r.error_code(); + return false; + } + more = visit_end_array(context, ec); + + return more; + } + + bool write_hexfloat_value(const string_view_type& sv, const ser_context& context, std::error_code& ec) + { + bool more = true; + + hexfloat_parse_state state = hexfloat_parse_state::start; + std::basic_string s; + std::basic_string exponent; + int64_t scale = 0; + + for (auto c : sv) + { + switch (state) + { + case hexfloat_parse_state::start: + { + switch (c) + { + case '-': + s.push_back(c); + state = hexfloat_parse_state::expect_0; + break; + case '0': + state = hexfloat_parse_state::expect_x; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::expect_0: + { + switch (c) + { + case '0': + state = hexfloat_parse_state::expect_x; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::expect_x: + { + switch (c) + { + case 'x': + case 'X': + state = hexfloat_parse_state::integer; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::integer: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + s.push_back(c); + break; + case 'p': case 'P': + state = hexfloat_parse_state::exp1; + break; + case '.': + state = hexfloat_parse_state::fraction1; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::exp1: + { + switch (c) + { + case '+': + state = hexfloat_parse_state::exp2; + break; + case '-': + exponent.push_back(c); + state = hexfloat_parse_state::exp2; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + exponent.push_back(c); + state = hexfloat_parse_state::exp2; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::exp2: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + exponent.push_back(c); + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + case hexfloat_parse_state::fraction1: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + s.push_back(c); + scale -= 4; + break; + default: + { + ec = cbor_errc::invalid_bigfloat; + return false; + } + } + break; + } + } + } + + write_tag(5); + more = visit_begin_array((std::size_t)2, semantic_tag::none, context, ec); + if (!more) return more; + + if (exponent.length() > 0) + { + int64_t val{ 0 }; + auto r = jsoncons::detail::base16_to_integer(exponent.data(), exponent.length(), val); + if (!r) + { + ec = r.error_code(); + return false; + } + scale += val; + } + more = visit_int64(scale, semantic_tag::none, context, ec); + if (!more) return more; + + int64_t val{ 0 }; + auto r = jsoncons::detail::base16_to_integer(s.data(),s.length(), val); + if (r) + { + more = visit_int64(val, semantic_tag::none, context, ec); + if (!more) return more; + } + else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow) + { + bigint n = bigint::from_string_radix(s.data(), s.length(), 16); + write_bignum(n); + end_value(); + } + else + { + JSONCONS_THROW(json_runtime_error(r.error_code().message())); + } + return visit_end_array(context, ec); + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::bigint: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + write_bignum(n); + end_value(); + break; + } + case semantic_tag::bigdec: + { + return write_decimal_value(sv, context, ec); + } + case semantic_tag::bigfloat: + { + return write_hexfloat_value(sv, context, ec); + } + case semantic_tag::datetime: + { + write_tag(0); + + write_string(sv); + end_value(); + break; + } + case semantic_tag::uri: + { + write_tag(32); + write_string(sv); + end_value(); + break; + } + case semantic_tag::base64url: + { + write_tag(33); + write_string(sv); + end_value(); + break; + } + case semantic_tag::base64: + { + write_tag(34); + write_string(sv); + end_value(); + break; + } + default: + { + write_string(sv); + end_value(); + break; + } + } + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + byte_string_chars_format encoding_hint; + switch (tag) + { + case semantic_tag::base16: + encoding_hint = byte_string_chars_format::base16; + break; + case semantic_tag::base64: + encoding_hint = byte_string_chars_format::base64; + break; + case semantic_tag::base64url: + encoding_hint = byte_string_chars_format::base64url; + break; + default: + encoding_hint = byte_string_chars_format::none; + break; + } + switch (encoding_hint) + { + case byte_string_chars_format::base64url: + write_tag(21); + break; + case byte_string_chars_format::base64: + write_tag(22); + break; + case byte_string_chars_format::base16: + write_tag(23); + break; + default: + break; + } + if (options_.pack_strings() && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + { + byte_string_type bs(b.data(), b.size(), alloc_); + auto it = bytestringref_map_.find(bs); + if (it == bytestringref_map_.end()) + { + bytestringref_map_.emplace(std::make_pair(bs, next_stringref_++)); + write_byte_string_value(bs); + } + else + { + write_tag(25); + write_uint64_value(it->second); + } + } + else + { + write_byte_string_value(b); + } + + end_value(); + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code&) override + { + if (options_.pack_strings() && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + { + byte_string_type bs(b.data(), b.size(), alloc_); + auto it = bytestringref_map_.find(bs); + if (it == bytestringref_map_.end()) + { + bytestringref_map_.emplace(std::make_pair(bs, next_stringref_++)); + write_tag(ext_tag); + write_byte_string_value(bs); + } + else + { + write_tag(25); + write_uint64_value(it->second); + } + } + else + { + write_tag(ext_tag); + write_byte_string_value(b); + } + + end_value(); + return true; + } + + void write_byte_string_value(const byte_string_view& b) + { + if (b.size() <= 0x17) + { + // fixstr stores a byte array whose length is upto 31 bytes + binary::native_to_big(static_cast(0x40 + b.size()), + std::back_inserter(sink_)); + } + else if (b.size() <= 0xff) + { + binary::native_to_big(static_cast(0x58), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(b.size()), + std::back_inserter(sink_)); + } + else if (b.size() <= 0xffff) + { + binary::native_to_big(static_cast(0x59), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(b.size()), + std::back_inserter(sink_)); + } + else if (b.size() <= 0xffffffff) + { + binary::native_to_big(static_cast(0x5a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(b.size()), + std::back_inserter(sink_)); + } + else // if (b.size() <= 0xffffffffffffffff) + { + binary::native_to_big(static_cast(0x5b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(b.size()), + std::back_inserter(sink_)); + } + + for (auto c : b) + { + sink_.push_back(c); + } + } + + bool visit_double(double val, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (tag) + { + case semantic_tag::epoch_second: + write_tag(1); + break; + case semantic_tag::epoch_milli: + write_tag(1); + if (val != 0) + { + val /= millis_in_second; + } + break; + case semantic_tag::epoch_nano: + write_tag(1); + if (val != 0) + { + val /= nanos_in_second; + } + break; + default: + break; + } + + float valf = (float)val; + if ((double)valf == val) + { + binary::native_to_big(static_cast(0xfa), + std::back_inserter(sink_)); + binary::native_to_big(valf, std::back_inserter(sink_)); + } + else + { + binary::native_to_big(static_cast(0xfb), + std::back_inserter(sink_)); + binary::native_to_big(val, std::back_inserter(sink_)); + } + + // write double + + end_value(); + return true; + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::epoch_milli: + case semantic_tag::epoch_nano: + return visit_double(static_cast(value), tag, context, ec); + case semantic_tag::epoch_second: + write_tag(1); + break; + default: + break; + } + if (value >= 0) + { + if (value <= 0x17) + { + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x18), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x19), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x1a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x1b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + } else + { + const auto posnum = -1 - value; + if (value >= -24) + { + binary::native_to_big(static_cast(0x20 + posnum), + std::back_inserter(sink_)); + } + else if (posnum <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x38), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(posnum), + std::back_inserter(sink_)); + } + else if (posnum <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x39), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(posnum), + std::back_inserter(sink_)); + } + else if (posnum <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x3a), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(posnum), + std::back_inserter(sink_)); + } + else if (posnum <= (std::numeric_limits::max)()) + { + binary::native_to_big(static_cast(0x3b), + std::back_inserter(sink_)); + binary::native_to_big(static_cast(posnum), + std::back_inserter(sink_)); + } + } + end_value(); + return true; + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::epoch_milli: + case semantic_tag::epoch_nano: + return visit_double(static_cast(value), tag, context, ec); + case semantic_tag::epoch_second: + write_tag(1); + break; + default: + break; + } + + write_uint64_value(value); + end_value(); + return true; + } + + void write_tag(uint64_t value) + { + if (value <= 0x17) + { + sink_.push_back(0xc0 | static_cast(value)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(0xd8); + sink_.push_back(static_cast(value)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(0xd9); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(0xda); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else + { + sink_.push_back(0xdb); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + } + + void write_uint64_value(uint64_t value) + { + if (value <= 0x17) + { + sink_.push_back(static_cast(value)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(static_cast(0x18)); + sink_.push_back(static_cast(value)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(static_cast(0x19)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(static_cast(0x1a)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + else if (value <=(std::numeric_limits::max)()) + { + sink_.push_back(static_cast(0x1b)); + binary::native_to_big(static_cast(value), + std::back_inserter(sink_)); + } + } + + bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override + { + if (value) + { + sink_.push_back(0xf5); + } + else + { + sink_.push_back(0xf4); + } + + end_value(); + return true; + } + + bool visit_typed_array(const jsoncons::span& v, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + switch (tag) + { + case semantic_tag::clamped: + write_tag(0x44); + break; + default: + write_tag(0x40); + break; + } + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(v.size(), semantic_tag::none, context, ec); + for (auto p = v.begin(); more && p != v.end(); ++p) + { + more = this->uint64_value(*p, tag, context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + uint16_t(), + tag); + std::vector v(data.size()*sizeof(uint16_t)); + std::memcpy(v.data(),data.data(),data.size()*sizeof(uint16_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none, context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->uint64_value(*p, tag, context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + uint32_t(), + tag); + std::vector v(data.size()*sizeof(uint32_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(uint32_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none, context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->uint64_value(*p, semantic_tag::none, context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + uint64_t(), + tag); + std::vector v(data.size()*sizeof(uint64_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(uint64_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none, context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->uint64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_tag(0x48); + std::vector v(data.size()*sizeof(int8_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(int8_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + int16_t(), + tag); + std::vector v(data.size()*sizeof(int16_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(int16_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + int32_t(), + tag); + std::vector v(data.size()*sizeof(int32_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(int32_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + int64_t(), + tag); + std::vector v(data.size()*sizeof(int64_t)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(int64_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->int64_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(half_arg_t, const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + half_arg, + tag); + std::vector v(data.size()*sizeof(uint16_t)); + std::memcpy(v.data(),data.data(),data.size()*sizeof(uint16_t)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none, context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->half_value(*p, tag, context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + float(), + tag); + std::vector v(data.size()*sizeof(float)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(float)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } + + bool visit_typed_array(const jsoncons::span& data, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + if (options_.use_typed_arrays()) + { + write_typed_array_tag(std::integral_constant(), + double(), + tag); + std::vector v(data.size()*sizeof(double)); + std::memcpy(v.data(), data.data(), data.size()*sizeof(double)); + write_byte_string_value(byte_string_view(v)); + return true; + } + else + { + bool more = this->begin_array(data.size(), semantic_tag::none,context, ec); + for (auto p = data.begin(); more && p != data.end(); ++p) + { + more = this->double_value(*p,semantic_tag::none,context, ec); + } + if (more) + { + more = this->end_array(context, ec); + } + return more; + } + } +/* + bool visit_typed_array(const jsoncons::span&, + semantic_tag, + const ser_context&, + std::error_code&) override + { + return true; + } +*/ + bool visit_begin_multi_dim(const jsoncons::span& shape, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::multi_dim_column_major: + write_tag(1040); + break; + default: + write_tag(40); + break; + } + bool more = visit_begin_array(2, semantic_tag::none, context, ec); + if (more) + more = visit_begin_array(shape.size(), semantic_tag::none, context, ec); + for (auto it = shape.begin(); more && it != shape.end(); ++it) + { + more = visit_uint64(*it, semantic_tag::none, context, ec); + } + if (more) + { + more = visit_end_array(context, ec); + } + return more; + } + + bool visit_end_multi_dim(const ser_context& context, + std::error_code& ec) override + { + bool more = visit_end_array(context, ec); + return more; + } + + void write_typed_array_tag(std::true_type, + uint16_t, + semantic_tag) + { + write_tag(0x41); // big endian + } + void write_typed_array_tag(std::false_type, + uint16_t, + semantic_tag) + { + write_tag(0x45); + } + + void write_typed_array_tag(std::true_type, + uint32_t, + semantic_tag) + { + write_tag(0x42); // big endian + } + void write_typed_array_tag(std::false_type, + uint32_t, + semantic_tag) + { + write_tag(0x46); // little endian + } + + void write_typed_array_tag(std::true_type, + uint64_t, + semantic_tag) + { + write_tag(0x43); // big endian + } + void write_typed_array_tag(std::false_type, + uint64_t, + semantic_tag) + { + write_tag(0x47); // little endian + } + + void write_typed_array_tag(std::true_type, + int16_t, + semantic_tag) + { + write_tag(0x49); // big endian + } + void write_typed_array_tag(std::false_type, + int16_t, + semantic_tag) + { + write_tag(0x4d); // little endian + } + + void write_typed_array_tag(std::true_type, + int32_t, + semantic_tag) + { + write_tag(0x4a); // big endian + } + void write_typed_array_tag(std::false_type, + int32_t, + semantic_tag) + { + write_tag(0x4e); // little endian + } + + void write_typed_array_tag(std::true_type, + int64_t, + semantic_tag) + { + write_tag(0x4b); // big endian + } + void write_typed_array_tag(std::false_type, + int64_t, + semantic_tag) + { + write_tag(0x4f); // little endian + } + + void write_typed_array_tag(std::true_type, + half_arg_t, + semantic_tag) + { + write_tag(0x50); + } + void write_typed_array_tag(std::false_type, + half_arg_t, + semantic_tag) + { + write_tag(0x54); + } + + void write_typed_array_tag(std::true_type, + float, + semantic_tag) + { + write_tag(0x51); // big endian + } + void write_typed_array_tag(std::false_type, + float, + semantic_tag) + { + write_tag(0x55); // little endian + } + + void write_typed_array_tag(std::true_type, + double, + semantic_tag) + { + write_tag(0x52); // big endian + } + void write_typed_array_tag(std::false_type, + double, + semantic_tag) + { + write_tag(0x56); // little endian + } + + void end_value() + { + if (!stack_.empty()) + { + ++stack_.back().count_; + } + } +}; + +using cbor_stream_encoder = basic_cbor_encoder; +using cbor_bytes_encoder = basic_cbor_encoder>>; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_encoder") typedef cbor_bytes_encoder cbor_bytes_serializer; + +template +using basic_cbor_serializer = basic_cbor_encoder; + +JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_encoder") typedef cbor_stream_encoder cbor_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_encoder") typedef cbor_stream_encoder cbor_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_encoder") typedef cbor_bytes_encoder cbor_buffer_serializer; +#endif + +}} +#endif diff --git a/include/jsoncons_ext/cbor/cbor_error.hpp b/include/jsoncons_ext/cbor/cbor_error.hpp new file mode 100644 index 0000000..a7a6626 --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_error.hpp @@ -0,0 +1,105 @@ +/// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_ERROR_HPP +#define JSONCONS_CBOR_CBOR_ERROR_HPP + +#include +#include +#include // jsoncons::ser_error + +namespace jsoncons { namespace cbor { + +enum class cbor_errc +{ + success = 0, + unexpected_eof, + source_error, + invalid_decimal_fraction, + invalid_bigfloat, + invalid_utf8_text_string, + too_many_items, + too_few_items, + number_too_large, + stringref_too_large, + max_nesting_depth_exceeded, + unknown_type, + illegal_chunked_string +}; + +class cbor_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/cbor"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case cbor_errc::unexpected_eof: + return "Unexpected end of file"; + case cbor_errc::source_error: + return "Source error"; + case cbor_errc::invalid_decimal_fraction: + return "Invalid decimal fraction"; + case cbor_errc::invalid_bigfloat: + return "Invalid bigfloat"; + case cbor_errc::invalid_utf8_text_string: + return "Illegal UTF-8 encoding in text string"; + case cbor_errc::too_many_items: + return "Too many items were added to a CBOR map or array of known length"; + case cbor_errc::too_few_items: + return "Too few items were added to a CBOR map or array of known length"; + case cbor_errc::number_too_large: + return "Number exceeds implementation limits"; + case cbor_errc::stringref_too_large: + return "stringref exceeds stringref map size"; + case cbor_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case cbor_errc::unknown_type: + return "An unknown type was found in the stream"; + case cbor_errc::illegal_chunked_string: + return "An illegal type was found while parsing an indefinite length string"; + default: + return "Unknown CBOR parser error"; + } + } +}; + +inline +const std::error_category& cbor_error_category() +{ + static cbor_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(cbor_errc e) +{ + return std::error_code(static_cast(e),cbor_error_category()); +} + + +#if !defined(JSONCONS_NO_DEPRECATED) + +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_error; +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_decode_error; +JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_reader_errc; +#endif + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/cbor/cbor_options.hpp b/include/jsoncons_ext/cbor/cbor_options.hpp new file mode 100644 index 0000000..1de4a4e --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_options.hpp @@ -0,0 +1,113 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_OPTIONS_HPP +#define JSONCONS_CBOR_CBOR_OPTIONS_HPP + +#include +#include // std::numeric_limits +#include +#include +#include + +namespace jsoncons { namespace cbor { + +class cbor_options; + +class cbor_options_common +{ + friend class cbor_options; + + int max_nesting_depth_; +protected: + virtual ~cbor_options_common() = default; + + cbor_options_common() + : max_nesting_depth_(1024) + { + } + + cbor_options_common(const cbor_options_common&) = default; + cbor_options_common& operator=(const cbor_options_common&) = default; + cbor_options_common(cbor_options_common&&) = default; + cbor_options_common& operator=(cbor_options_common&&) = default; +public: + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class cbor_decode_options : public virtual cbor_options_common +{ + friend class cbor_options; +public: + cbor_decode_options() + { + } +}; + +class cbor_encode_options : public virtual cbor_options_common +{ + friend class cbor_options; + + bool use_stringref_; + bool use_typed_arrays_; +public: + cbor_encode_options() + : use_stringref_(false), + use_typed_arrays_(false) + { + } + + bool pack_strings() const + { + return use_stringref_; + } + + bool use_typed_arrays() const + { + return use_typed_arrays_; + } +}; + +class cbor_options final : public cbor_decode_options, public cbor_encode_options +{ +public: + using cbor_options_common::max_nesting_depth; + using cbor_encode_options::pack_strings; + using cbor_encode_options::use_typed_arrays; + + cbor_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } + + cbor_options& pack_strings(bool value) + { + this->use_stringref_ = value; + return *this; + } + + cbor_options& use_typed_arrays(bool value) + { + this->use_typed_arrays_ = value; + return *this; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use use_typed_arrays(bool)") + cbor_options& enable_typed_arrays(bool value) + { + this->use_typed_arrays_ = value; + return *this; + } +#endif +}; + +}} +#endif diff --git a/include/jsoncons_ext/cbor/cbor_parser.hpp b/include/jsoncons_ext/cbor/cbor_parser.hpp new file mode 100644 index 0000000..f3d03bb --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_parser.hpp @@ -0,0 +1,1942 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_PARSER_HPP +#define JSONCONS_CBOR_CBOR_PARSER_HPP + +#include +#include +#include +#include // std::move +#include // std::bitset +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace cbor { + +enum class parse_mode {root,accept,array,indefinite_array,map_key,map_value,indefinite_map_key,indefinite_map_value,multi_dim}; + +struct mapped_string +{ + jsoncons::cbor::detail::cbor_major_type type; + std::string s; + std::vector bytes; + + mapped_string(const std::string& s) + : type(jsoncons::cbor::detail::cbor_major_type::text_string), s(s) + { + } + + mapped_string(std::string&& s) + : type(jsoncons::cbor::detail::cbor_major_type::text_string), s(std::move(s)) + { + } + + mapped_string(const std::vector& bytes) + : type(jsoncons::cbor::detail::cbor_major_type::byte_string), bytes(bytes) + { + } + + mapped_string(std::vector&& bytes) + : type(jsoncons::cbor::detail::cbor_major_type::byte_string), bytes(std::move(bytes)) + { + } + + mapped_string(const mapped_string&) = default; + + mapped_string(mapped_string&&) = default; + + mapped_string& operator=(const mapped_string&) = default; + + mapped_string& operator=(mapped_string&&) = default; +}; + +struct parse_state +{ + parse_mode mode; + std::size_t length; + std::size_t index; + bool pop_stringref_map_stack; + + parse_state(parse_mode mode, std::size_t length, bool pop_stringref_map_stack = false) noexcept + : mode(mode), length(length), index(0), pop_stringref_map_stack(pop_stringref_map_stack) + { + } + + parse_state(const parse_state&) = default; + parse_state(parse_state&&) = default; +}; + +template > +class basic_cbor_parser : public ser_context +{ + using char_type = char; + using char_traits_type = std::char_traits; + using allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using tag_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stringref_map = std::vector; + using stringref_map_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + using string_type = std::basic_string; + + enum {stringref_tag, // 25 + stringref_namespace_tag, // 256 + item_tag, + num_of_tags}; + + std::bitset other_tags_; + + allocator_type alloc_; + Source source_; + cbor_decode_options options_; + + bool more_; + bool done_; + string_type text_buffer_; + std::vector bytes_buffer_; + uint64_t item_tag_; + std::vector state_stack_; + std::vector typed_array_; + std::vector shape_; + std::size_t index_; // TODO: Never used! + std::vector stringref_map_stack_; + int nesting_depth_; + + struct read_byte_string_from_buffer + { + byte_string_view bytes; + + read_byte_string_from_buffer(const byte_string_view& b) + : bytes(b) + { + } + template + void operator()(Container& c, std::error_code&) + { + c.clear(); + c.reserve(bytes.size()); + for (auto b : bytes) + { + c.push_back(b); + } + } + }; + + struct read_byte_string_from_source + { + basic_cbor_parser* source; + + read_byte_string_from_source(basic_cbor_parser* source) + : source(source) + { + } + template + void operator()(Container& c, std::error_code& ec) + { + source->read_byte_string(c,ec); + } + }; + +public: + template + basic_cbor_parser(Sourceable&& source, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator alloc = Allocator()) + : alloc_(alloc), + source_(std::forward(source)), + options_(options), + more_(true), + done_(false), + text_buffer_(alloc), + bytes_buffer_(alloc), + item_tag_(0), + state_stack_(alloc), + typed_array_(alloc), + index_(0), + stringref_map_stack_(alloc), + nesting_depth_(0) + { + state_stack_.emplace_back(parse_mode::root,0); + } + + void restart() + { + more_ = true; + } + + void reset() + { + more_ = true; + done_ = false; + text_buffer_.clear(); + bytes_buffer_.clear(); + item_tag_ = 0; + state_stack_.clear(); + state_stack_.emplace_back(parse_mode::root,0); + typed_array_.clear(); + stringref_map_stack_.clear(); + nesting_depth_ = 0; + } + + template + void reset(Sourceable&& source) + { + source_ = std::forward(source); + reset(); + } + + bool done() const + { + return done_; + } + + bool stopped() const + { + return !more_; + } + + std::size_t line() const override + { + return 0; + } + + std::size_t column() const override + { + return source_.position(); + } + + void parse(json_visitor2& visitor, std::error_code& ec) + { + while (!done_ && more_) + { + switch (state_stack_.back().mode) + { + case parse_mode::multi_dim: + { + if (state_stack_.back().index == 0) + { + ++state_stack_.back().index; + read_item(visitor, ec); + } + else + { + produce_end_multi_dim(visitor, ec); + } + break; + } + case parse_mode::array: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_item(visitor, ec); + } + else + { + end_array(visitor, ec); + } + break; + } + case parse_mode::indefinite_array: + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == 0xff) + { + source_.ignore(1); + end_array(visitor, ec); + } + else + { + read_item(visitor, ec); + } + break; + } + case parse_mode::map_key: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + state_stack_.back().mode = parse_mode::map_value; + read_item(visitor, ec); + } + else + { + end_object(visitor, ec); + } + break; + } + case parse_mode::map_value: + { + state_stack_.back().mode = parse_mode::map_key; + read_item(visitor, ec); + break; + } + case parse_mode::indefinite_map_key: + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == 0xff) + { + source_.ignore(1); + end_object(visitor, ec); + } + else + { + state_stack_.back().mode = parse_mode::indefinite_map_value; + read_item(visitor, ec); + } + break; + } + case parse_mode::indefinite_map_value: + { + state_stack_.back().mode = parse_mode::indefinite_map_key; + read_item(visitor, ec); + break; + } + case parse_mode::root: + { + state_stack_.back().mode = parse_mode::accept; + read_item(visitor, ec); + break; + } + case parse_mode::accept: + { + JSONCONS_ASSERT(state_stack_.size() == 1); + state_stack_.clear(); + more_ = false; + done_ = true; + visitor.flush(); + break; + } + } + } + } +private: + void read_item(json_visitor2& visitor, std::error_code& ec) + { + read_tags(ec); + if (!more_) + { + return; + } + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value); + uint8_t info = get_additional_information_value(c.value); + + switch (major_type) + { + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + uint64_t val = get_uint64_value(ec); + if (ec) + { + return; + } + if (!stringref_map_stack_.empty() && other_tags_[stringref_tag]) + { + other_tags_[stringref_tag] = false; + if (val >= stringref_map_stack_.back().size()) + { + ec = cbor_errc::stringref_too_large; + more_ = false; + return; + } + stringref_map::size_type index = (stringref_map::size_type)val; + if (index != val) + { + ec = cbor_errc::number_too_large; + more_ = false; + return; + } + auto& str = stringref_map_stack_.back().at(index); + switch (str.type) + { + case jsoncons::cbor::detail::cbor_major_type::text_string: + { + handle_string(visitor, jsoncons::basic_string_view(str.s.data(),str.s.length()),ec); + if (ec) + { + return; + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::byte_string: + { + read_byte_string_from_buffer read(byte_string_view(str.bytes)); + write_byte_string(read, visitor, ec); + if (ec) + { + return; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + else + { + semantic_tag tag = semantic_tag::none; + if (other_tags_[item_tag]) + { + if (item_tag_ == 1) + { + tag = semantic_tag::epoch_second; + } + other_tags_[item_tag] = false; + } + more_ = visitor.uint64_value(val, tag, *this, ec); + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + { + int64_t val = get_int64_value(ec); + if (ec) + { + return; + } + semantic_tag tag = semantic_tag::none; + if (other_tags_[item_tag]) + { + if (item_tag_ == 1) + { + tag = semantic_tag::epoch_second; + } + other_tags_[item_tag] = false; + } + more_ = visitor.int64_value(val, tag, *this, ec); + break; + } + case jsoncons::cbor::detail::cbor_major_type::byte_string: + { + read_byte_string_from_source read(this); + write_byte_string(read, visitor, ec); + if (ec) + { + return; + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::text_string: + { + text_buffer_.clear(); + read_text_string(text_buffer_, ec); + if (ec) + { + return; + } + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = cbor_errc::invalid_utf8_text_string; + more_ = false; + return; + } + handle_string(visitor, jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()),ec); + if (ec) + { + return; + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::semantic_tag: + { + JSONCONS_UNREACHABLE(); + break; + } + case jsoncons::cbor::detail::cbor_major_type::simple: + { + switch (info) + { + case 0x14: + more_ = visitor.bool_value(false, semantic_tag::none, *this, ec); + source_.ignore(1); + break; + case 0x15: + more_ = visitor.bool_value(true, semantic_tag::none, *this, ec); + source_.ignore(1); + break; + case 0x16: + more_ = visitor.null_value(semantic_tag::none, *this, ec); + source_.ignore(1); + break; + case 0x17: + more_ = visitor.null_value(semantic_tag::undefined, *this, ec); + source_.ignore(1); + break; + case 0x19: // Half-Precision Float (two-byte IEEE 754) + { + uint64_t val = get_uint64_value(ec); + if (ec) + { + return; + } + more_ = visitor.half_value(static_cast(val), semantic_tag::none, *this, ec); + break; + } + case 0x1a: // Single-Precision Float (four-byte IEEE 754) + case 0x1b: // Double-Precision Float (eight-byte IEEE 754) + { + double val = get_double(ec); + if (ec) + { + return; + } + semantic_tag tag = semantic_tag::none; + if (other_tags_[item_tag]) + { + if (item_tag_ == 1) + { + tag = semantic_tag::epoch_second; + } + other_tags_[item_tag] = false; + } + more_ = visitor.double_value(val, tag, *this, ec); + break; + } + default: + { + ec = cbor_errc::unknown_type; + more_ = false; + return; + } + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::array: + { + if (other_tags_[item_tag]) + { + switch (item_tag_) + { + case 0x04: + text_buffer_.clear(); + read_decimal_fraction(text_buffer_, ec); + if (ec) + { + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::bigdec, *this, ec); + break; + case 0x05: + text_buffer_.clear(); + read_bigfloat(text_buffer_, ec); + if (ec) + { + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::bigfloat, *this, ec); + break; + case 40: // row major storage + produce_begin_multi_dim(visitor, semantic_tag::multi_dim_row_major, ec); + break; + case 1040: // column major storage + produce_begin_multi_dim(visitor, semantic_tag::multi_dim_column_major, ec); + break; + default: + begin_array(visitor, info, ec); + break; + } + other_tags_[item_tag] = false; + } + else + { + begin_array(visitor, info, ec); + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::map: + { + begin_object(visitor, info, ec); + break; + } + default: + break; + } + other_tags_[item_tag] = false; + } + + void begin_array(json_visitor2& visitor, uint8_t info, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + semantic_tag tag = semantic_tag::none; + bool pop_stringref_map_stack = false; + if (other_tags_[stringref_namespace_tag]) + { + stringref_map_stack_.emplace_back(alloc_); + other_tags_[stringref_namespace_tag] = false; + pop_stringref_map_stack = true; + } + switch (info) + { + case jsoncons::cbor::detail::additional_info::indefinite_length: + { + state_stack_.emplace_back(parse_mode::indefinite_array,0,pop_stringref_map_stack); + more_ = visitor.begin_array(tag, *this, ec); + source_.ignore(1); + break; + } + default: // definite length + { + std::size_t len = get_size(ec); + if (!more_) + { + return; + } + state_stack_.emplace_back(parse_mode::array,len,pop_stringref_map_stack); + more_ = visitor.begin_array(len, tag, *this, ec); + break; + } + } + } + + void end_array(json_visitor2& visitor, std::error_code& ec) + { + --nesting_depth_; + + more_ = visitor.end_array(*this, ec); + if (state_stack_.back().pop_stringref_map_stack) + { + stringref_map_stack_.pop_back(); + } + state_stack_.pop_back(); + } + + void begin_object(json_visitor2& visitor, uint8_t info, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = cbor_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + bool pop_stringref_map_stack = false; + if (other_tags_[stringref_namespace_tag]) + { + stringref_map_stack_.emplace_back(alloc_); + other_tags_[stringref_namespace_tag] = false; + pop_stringref_map_stack = true; + } + switch (info) + { + case jsoncons::cbor::detail::additional_info::indefinite_length: + { + state_stack_.emplace_back(parse_mode::indefinite_map_key,0,pop_stringref_map_stack); + more_ = visitor.begin_object(semantic_tag::none, *this, ec); + source_.ignore(1); + break; + } + default: // definite_length + { + std::size_t len = get_size(ec); + if (!more_) + { + return; + } + state_stack_.emplace_back(parse_mode::map_key,len,pop_stringref_map_stack); + more_ = visitor.begin_object(len, semantic_tag::none, *this, ec); + break; + } + } + } + + void end_object(json_visitor2& visitor, std::error_code& ec) + { + --nesting_depth_; + more_ = visitor.end_object(*this, ec); + if (state_stack_.back().pop_stringref_map_stack) + { + stringref_map_stack_.pop_back(); + } + state_stack_.pop_back(); + } + + void read_text_string(string_type& s, std::error_code& ec) + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value); + uint8_t info = get_additional_information_value(c.value); + + JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::text_string); + auto func = [&s](Source& source, std::size_t length, std::error_code& ec) -> bool + { + if (source_reader::read(source, s, length) != length) + { + ec = cbor_errc::unexpected_eof; + return false; + } + return true; + }; + iterate_string_chunks(func, major_type, ec); + if (!stringref_map_stack_.empty() && + info != jsoncons::cbor::detail::additional_info::indefinite_length && + s.length() >= jsoncons::cbor::detail::min_length_for_stringref(stringref_map_stack_.back().size())) + { + stringref_map_stack_.back().emplace_back(s); + } + } + + std::size_t get_size(std::error_code& ec) + { + uint64_t u = get_uint64_value(ec); + if (!more_) + { + return 0; + } + std::size_t len = static_cast(u); + if (len != u) + { + ec = cbor_errc::number_too_large; + more_ = false; + } + return len; + } + + bool read_byte_string(std::vector& v, std::error_code& ec) + { + bool more = true; + v.clear(); + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more = false; + return more; + } + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value); + uint8_t info = get_additional_information_value(c.value); + + JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::byte_string); + + switch(info) + { + case jsoncons::cbor::detail::additional_info::indefinite_length: + { + auto func = [&v,&more](Source& source, std::size_t length, std::error_code& ec) -> bool + { + if (source_reader::read(source, v, length) != length) + { + ec = cbor_errc::unexpected_eof; + more = false; + return more; + } + return true; + }; + iterate_string_chunks(func, major_type, ec); + break; + } + default: + { + std::size_t length = get_size(ec); + if (ec) + { + more = false; + return more; + } + if (source_reader::read(source_, v, length) != length) + { + ec = cbor_errc::unexpected_eof; + more = false; + return more; + } + if (!stringref_map_stack_.empty() && + v.size() >= jsoncons::cbor::detail::min_length_for_stringref(stringref_map_stack_.back().size())) + { + stringref_map_stack_.back().emplace_back(v); + } + break; + } + } + return more; + } + + template + void iterate_string_chunks(Function& func, jsoncons::cbor::detail::cbor_major_type type, std::error_code& ec) + { + int nesting_level = 0; + + bool done = false; + while (!done) + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + if (nesting_level > 0 && c.value == 0xff) + { + --nesting_level; + if (nesting_level == 0) + { + done = true; + } + source_.ignore(1); + continue; + } + + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value); + if (major_type != type) + { + ec = cbor_errc::illegal_chunked_string; + more_ = false; + return; + } + uint8_t info = get_additional_information_value(c.value); + + switch (info) + { + case jsoncons::cbor::detail::additional_info::indefinite_length: + { + ++nesting_level; + source_.ignore(1); + break; + } + default: // definite length + { + std::size_t length = get_size(ec); + if (!more_) + { + return; + } + more_ = func(source_, length, ec); + if (!more_) + { + return; + } + if (nesting_level == 0) + { + done = true; + } + break; + } + } + } + } + + uint64_t get_uint64_value(std::error_code& ec) + { + uint64_t val = 0; + + uint8_t initial_b; + if (source_.read(&initial_b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return 0; + } + uint8_t info = get_additional_information_value(initial_b); + switch (info) + { + case JSONCONS_CBOR_0x00_0x17: // Integer 0x00..0x17 (0..23) + { + val = info; + break; + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + val = b; + break; + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + uint8_t buf[sizeof(uint16_t)]; + source_.read(buf, sizeof(uint16_t)); + val = binary::big_to_native(buf, sizeof(buf)); + break; + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + uint8_t buf[sizeof(uint32_t)]; + source_.read(buf, sizeof(uint32_t)); + val = binary::big_to_native(buf, sizeof(buf)); + break; + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + uint8_t buf[sizeof(uint64_t)]; + source_.read(buf, sizeof(uint64_t)); + val = binary::big_to_native(buf, sizeof(buf)); + break; + } + default: + break; + } + return val; + } + + int64_t get_int64_value(std::error_code& ec) + { + int64_t val = 0; + + auto ch = source_.peek(); + if (ch.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(ch.value); + uint8_t info = get_additional_information_value(ch.value); + switch (major_type) + { + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + source_.ignore(1); + switch (info) + { + case JSONCONS_CBOR_0x00_0x17: // 0x00..0x17 (0..23) + { + val = static_cast(- 1 - info); + break; + } + case 0x18: // Negative integer (one-byte uint8_t follows) + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + val = static_cast(-1) - static_cast(b); + break; + } + + case 0x19: // Negative integer -1-n (two-byte uint16_t follows) + { + uint8_t buf[sizeof(uint16_t)]; + if (source_.read(buf, sizeof(uint16_t)) != sizeof(uint16_t)) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + auto x = binary::big_to_native(buf, sizeof(buf)); + val = static_cast(-1)- x; + break; + } + + case 0x1a: // Negative integer -1-n (four-byte uint32_t follows) + { + uint8_t buf[sizeof(uint32_t)]; + if (source_.read(buf, sizeof(uint32_t)) != sizeof(uint32_t)) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + auto x = binary::big_to_native(buf, sizeof(buf)); + val = static_cast(-1)- x; + break; + } + + case 0x1b: // Negative integer -1-n (eight-byte uint64_t follows) + { + uint8_t buf[sizeof(uint64_t)]; + if (source_.read(buf, sizeof(uint64_t)) != sizeof(uint64_t)) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return val; + } + auto x = binary::big_to_native(buf, sizeof(buf)); + val = static_cast(-1)- static_cast(x); + break; + } + } + break; + + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + uint64_t x = get_uint64_value(ec); + if (ec) + { + return 0; + } + if (x <= static_cast((std::numeric_limits::max)())) + { + val = x; + } + else + { + // error; + } + + break; + } + break; + default: + break; + } + + return val; + } + + double get_double(std::error_code& ec) + { + double val = 0; + + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return 0; + } + uint8_t info = get_additional_information_value(b); + switch (info) + { + case 0x1a: // Single-Precision Float (four-byte IEEE 754) + { + uint8_t buf[sizeof(float)]; + if (source_.read(buf, sizeof(float)) !=sizeof(float)) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return 0; + } + val = binary::big_to_native(buf, sizeof(buf)); + break; + } + + case 0x1b: // Double-Precision Float (eight-byte IEEE 754) + { + uint8_t buf[sizeof(double)]; + if (source_.read(buf, sizeof(double)) != sizeof(double)) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return 0; + } + val = binary::big_to_native(buf, sizeof(buf)); + break; + } + default: + break; + } + + return val; + } + + void read_decimal_fraction(string_type& result, std::error_code& ec) + { + std::size_t size = get_size(ec); + if (!more_) + { + return; + } + if (size != 2) + { + ec = cbor_errc::invalid_decimal_fraction; + more_ = false; + return; + } + + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + int64_t exponent = 0; + switch (get_major_type(c.value)) + { + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + exponent = get_uint64_value(ec); + if (ec) + { + return; + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + { + exponent = get_int64_value(ec); + if (ec) + { + return; + } + break; + } + default: + { + ec = cbor_errc::invalid_decimal_fraction; + more_ = false; + return; + } + } + + string_type s; + + c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + + switch (get_major_type(c.value)) + { + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + uint64_t val = get_uint64_value(ec); + if (ec) + { + return; + } + jsoncons::detail::from_integer(val, s); + break; + } + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + { + int64_t val = get_int64_value(ec); + if (ec) + { + return; + } + jsoncons::detail::from_integer(val, s); + break; + } + case jsoncons::cbor::detail::cbor_major_type::semantic_tag: + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + uint8_t tag = get_additional_information_value(b); + c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + + if (get_major_type(c.value) == jsoncons::cbor::detail::cbor_major_type::byte_string) + { + bytes_buffer_.clear(); + read_byte_string(bytes_buffer_, ec); + if (ec) + { + more_ = false; + return; + } + if (tag == 2) + { + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + n.write_string(s); + } + else if (tag == 3) + { + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + n = -1 - n; + n.write_string(s); + } + } + break; + } + default: + { + ec = cbor_errc::invalid_decimal_fraction; + more_ = false; + return; + } + } + + if (s.size() >= static_cast((std::numeric_limits::max)()) || + exponent >= (std::numeric_limits::max)() || + exponent <= (std::numeric_limits::min)()) + { + ec = cbor_errc::invalid_decimal_fraction; + more_ = false; + return; + } + else if (s.size() > 0) + { + if (s[0] == '-') + { + result.push_back('-'); + jsoncons::detail::prettify_string(s.c_str()+1, s.size()-1, (int)exponent, -4, 17, result); + } + else + { + jsoncons::detail::prettify_string(s.c_str(), s.size(), (int)exponent, -4, 17, result); + } + } + else + { + ec = cbor_errc::invalid_decimal_fraction; + more_ = false; + return; + } + } + + void read_bigfloat(string_type& s, std::error_code& ec) + { + std::size_t size = get_size(ec); + if (!more_) + { + return; + } + if (size != 2) + { + ec = cbor_errc::invalid_bigfloat; + more_ = false; + return; + } + + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + int64_t exponent = 0; + switch (get_major_type(c.value)) + { + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + exponent = get_uint64_value(ec); + if (ec) + { + return; + } + break; + } + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + { + exponent = get_int64_value(ec); + if (ec) + { + return; + } + break; + } + default: + { + ec = cbor_errc::invalid_bigfloat; + more_ = false; + return; + } + } + + c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + switch (get_major_type(c.value)) + { + case jsoncons::cbor::detail::cbor_major_type::unsigned_integer: + { + uint64_t val = get_uint64_value(ec); + if (ec) + { + return; + } + s.push_back('0'); + s.push_back('x'); + jsoncons::detail::integer_to_string_hex(val, s); + break; + } + case jsoncons::cbor::detail::cbor_major_type::negative_integer: + { + int64_t val = get_int64_value(ec); + if (ec) + { + return; + } + s.push_back('-'); + s.push_back('0'); + s.push_back('x'); + jsoncons::detail::integer_to_string_hex(static_cast(-val), s); + break; + } + case jsoncons::cbor::detail::cbor_major_type::semantic_tag: + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + uint8_t tag = get_additional_information_value(b); + + c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + + if (get_major_type(c.value) == jsoncons::cbor::detail::cbor_major_type::byte_string) + { + bytes_buffer_.clear(); + more_ = read_byte_string(bytes_buffer_, ec); + if (!more_) + { + return; + } + if (tag == 2) + { + s.push_back('0'); + s.push_back('x'); + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + n.write_string_hex(s); + } + else if (tag == 3) + { + s.push_back('-'); + s.push_back('0'); + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + n = -1 - n; + n.write_string_hex(s); + s[2] = 'x'; // overwrite minus + } + } + break; + } + default: + { + ec = cbor_errc::invalid_bigfloat; + more_ = false; + return; + } + } + + s.push_back('p'); + if (exponent >=0) + { + jsoncons::detail::integer_to_string_hex(static_cast(exponent), s); + } + else + { + s.push_back('-'); + jsoncons::detail::integer_to_string_hex(static_cast(-exponent), s); + } + } + + static jsoncons::cbor::detail::cbor_major_type get_major_type(uint8_t type) + { + static constexpr uint8_t major_type_shift = 0x05; + uint8_t value = type >> major_type_shift; + return static_cast(value); + } + + static uint8_t get_additional_information_value(uint8_t type) + { + static constexpr uint8_t additional_information_mask = (1U << 5) - 1; + uint8_t value = type & additional_information_mask; + return value; + } + + void read_tags(std::error_code& ec) + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value); + + while (major_type == jsoncons::cbor::detail::cbor_major_type::semantic_tag) + { + uint64_t val = get_uint64_value(ec); + if (!more_) + { + return; + } + switch(val) + { + case 25: // stringref + other_tags_[stringref_tag] = true; + break; + case 256: // stringref-namespace + other_tags_[stringref_namespace_tag] = true; + break; + default: + other_tags_[item_tag] = true; + item_tag_ = val; + break; + } + c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + major_type = get_major_type(c.value); + } + } + + void handle_string(json_visitor2& visitor, const jsoncons::basic_string_view& v, std::error_code& ec) + { + semantic_tag tag = semantic_tag::none; + if (other_tags_[item_tag]) + { + switch (item_tag_) + { + case 0: + tag = semantic_tag::datetime; + break; + case 32: + tag = semantic_tag::uri; + break; + case 33: + tag = semantic_tag::base64url; + break; + case 34: + tag = semantic_tag::base64; + break; + default: + break; + } + other_tags_[item_tag] = false; + } + more_ = visitor.string_value(v, tag, *this, ec); + } + + static jsoncons::endian get_typed_array_endianness(const uint8_t tag) + { + return ((tag & detail::cbor_array_tags_e_mask) >> detail::cbor_array_tags_e_shift) == 0 ? jsoncons::endian::big : jsoncons::endian::little; + } + + static std::size_t get_typed_array_bytes_per_element(const uint8_t tag) + { + const uint8_t f = (tag & detail::cbor_array_tags_f_mask) >> detail::cbor_array_tags_f_shift; + const uint8_t ll = (tag & detail::cbor_array_tags_ll_mask) >> detail::cbor_array_tags_ll_shift; + + return std::size_t(1) << (f + ll); + } + + template + void write_byte_string(Read read, json_visitor2& visitor, std::error_code& ec) + { + if (other_tags_[item_tag]) + { + switch (item_tag_) + { + case 0x2: + { + bytes_buffer_.clear(); + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + text_buffer_.clear(); + n.write_string(text_buffer_); + more_ = visitor.string_value(text_buffer_, semantic_tag::bigint, *this, ec); + break; + } + case 0x3: + { + bytes_buffer_.clear(); + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size()); + n = -1 - n; + text_buffer_.clear(); + n.write_string(text_buffer_); + more_ = visitor.string_value(text_buffer_, semantic_tag::bigint, *this, ec); + break; + } + case 0x15: + { + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base64url, *this, ec); + break; + } + case 0x16: + { + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base64, *this, ec); + break; + } + case 0x17: + { + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base16, *this, ec); + break; + } + case 0x40: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + uint8_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size(); + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x44: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + uint8_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size(); + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::clamped, *this, ec); + break; + } + case 0x41: + case 0x45: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + uint16_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x42: + case 0x46: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + uint32_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x43: + case 0x47: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + uint64_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x48: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + int8_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size(); + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x49: + case 0x4d: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + int16_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x4a: + case 0x4e: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + int32_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x4b: + case 0x4f: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + int64_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x50: + case 0x54: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + uint16_t* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(half_arg, jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x51: + case 0x55: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + float* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + case 0x52: + case 0x56: + { + typed_array_.clear(); + read(typed_array_,ec); + if (ec) + { + more_ = false; + return; + } + const uint8_t tag = (uint8_t)item_tag_; + jsoncons::endian e = get_typed_array_endianness(tag); + const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag); + + double* data = reinterpret_cast(typed_array_.data()); + std::size_t size = typed_array_.size()/bytes_per_elem; + + if (e != jsoncons::endian::native) + { + for (std::size_t i = 0; i < size; ++i) + { + data[i] = binary::byte_swap(data[i]); + } + } + more_ = visitor.typed_array(jsoncons::span(data,size), semantic_tag::none, *this, ec); + break; + } + default: + { + read(bytes_buffer_,ec); + if (ec) + { + more_ = false; + return; + } + more_ = visitor.byte_string_value(bytes_buffer_, item_tag_, *this, ec); + break; + } + } + other_tags_[item_tag] = false; + } + else + { + read(bytes_buffer_,ec); + if (ec) + { + return; + } + more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::none, *this, ec); + } + } + + void produce_begin_multi_dim(json_visitor2& visitor, + semantic_tag tag, + std::error_code& ec) + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(b); + JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::array); + uint8_t info = get_additional_information_value(b); + + read_shape(info, ec); + if (ec) + { + return; + } + + state_stack_.emplace_back(parse_mode::multi_dim, 0); + more_ = visitor.begin_multi_dim(shape_, tag, *this, ec); + } + + void produce_end_multi_dim(json_visitor2& visitor, std::error_code& ec) + { + more_ = visitor.end_multi_dim(*this, ec); + state_stack_.pop_back(); + } + + void read_shape(uint8_t info, std::error_code& ec) + { + shape_.clear(); + switch (info) + { + case jsoncons::cbor::detail::additional_info::indefinite_length: + { + while (true) + { + auto c = source_.peek(); + if (c.eof) + { + ec = cbor_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == 0xff) + { + source_.ignore(1); + } + else + { + std::size_t dim = get_size(ec); + if (!more_) + { + return; + } + shape_.push_back(dim); + } + } + break; + } + default: + { + std::size_t size = get_size(ec); + if (!more_) + { + return; + } + for (std::size_t i = 0; more_ && i < size; ++i) + { + std::size_t dim = get_size(ec); + if (!more_) + { + return; + } + shape_.push_back(dim); + } + break; + } + } + } +}; + +}} + +#endif diff --git a/include/jsoncons_ext/cbor/cbor_reader.hpp b/include/jsoncons_ext/cbor/cbor_reader.hpp new file mode 100644 index 0000000..a46a52e --- /dev/null +++ b/include/jsoncons_ext/cbor/cbor_reader.hpp @@ -0,0 +1,116 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_CBOR_READER_HPP +#define JSONCONS_CBOR_CBOR_READER_HPP + +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace cbor { + +template > +class basic_cbor_reader +{ + using char_type = char; + + basic_cbor_parser parser_; + basic_json_visitor2_to_visitor_adaptor adaptor_; + json_visitor2& visitor_; +public: + template + basic_cbor_reader(Sourceable&& source, + json_visitor& visitor, + const Allocator alloc) + : basic_cbor_reader(std::forward(source), + visitor, + cbor_decode_options(), + alloc) + { + } + + template + basic_cbor_reader(Sourceable&& source, + json_visitor& visitor, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + adaptor_(visitor, alloc), visitor_(adaptor_) + { + } + template + basic_cbor_reader(Sourceable&& source, + json_visitor2& visitor, + const Allocator alloc) + : basic_cbor_reader(std::forward(source), + visitor, + cbor_decode_options(), + alloc) + { + } + + template + basic_cbor_reader(Sourceable&& source, + json_visitor2& visitor, + const cbor_decode_options& options = cbor_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + visitor_(visitor) + { + } + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line(),column())); + } + } + + void read(std::error_code& ec) + { + parser_.reset(); + parser_.parse(visitor_, ec); + if (ec) + { + return; + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } +}; + +using cbor_stream_reader = basic_cbor_reader; + +using cbor_bytes_reader = basic_cbor_reader; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_reader") typedef cbor_stream_reader cbor_reader; +JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_reader") typedef cbor_bytes_reader cbor_buffer_reader; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/cbor/decode_cbor.hpp b/include/jsoncons_ext/cbor/decode_cbor.hpp new file mode 100644 index 0000000..ab5c913 --- /dev/null +++ b/include/jsoncons_ext/cbor/decode_cbor.hpp @@ -0,0 +1,203 @@ +// Copyright 2017 Daniel Parkerstd +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_DECODE_CBOR_HPP +#define JSONCONS_CBOR_DECODE_CBOR_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace cbor { + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_cbor(const Source& v, + const cbor_decode_options& options = cbor_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(v, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_cbor(const Source& v, + const cbor_decode_options& options = cbor_decode_options()) + { + basic_cbor_cursor cursor(v, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_cbor(std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + cbor_stream_reader reader(is, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_cbor(std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) + { + basic_cbor_cursor cursor(is, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_cbor(InputIt first, InputIt last, + const cbor_decode_options& options = cbor_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_cbor(InputIt first, InputIt last, + const cbor_decode_options& options = cbor_decode_options()) + { + basic_cbor_cursor> cursor(binary_iterator_source(first, last), options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const cbor_decode_options& options = cbor_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(v, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const cbor_decode_options& options = cbor_decode_options()) + { + basic_cbor_cursor cursor(v, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(is, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) + { + basic_cbor_cursor cursor(is, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // namespace cbor +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/cbor/encode_cbor.hpp b/include/jsoncons_ext/cbor/encode_cbor.hpp new file mode 100644 index 0000000..8576f1c --- /dev/null +++ b/include/jsoncons_ext/cbor/encode_cbor.hpp @@ -0,0 +1,151 @@ +// Copyright 2017 Daniel Parkerstd +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CBOR_ENCODE_CBOR_HPP +#define JSONCONS_CBOR_ENCODE_CBOR_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace cbor { + + // to bytes + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_cbor(const T& j, + Container& v, + const cbor_encode_options& options = cbor_encode_options()) + { + using char_type = typename T::char_type; + basic_cbor_encoder> encoder(v, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_cbor(const T& val, Container& v, + const cbor_encode_options& options = cbor_encode_options()) + { + basic_cbor_encoder> encoder(v, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // stream + + template + typename std::enable_if::value,void>::type + encode_cbor(const T& j, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) + { + using char_type = typename T::char_type; + cbor_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_cbor(const T& val, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) + { + cbor_stream_encoder encoder(os, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // temp_allocator_arg + + // to bytes + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + Container& v, + const cbor_encode_options& options = cbor_encode_options()) + { + using char_type = typename T::char_type; + basic_cbor_encoder,TempAllocator> encoder(v, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + Container& v, + const cbor_encode_options& options = cbor_encode_options()) + { + basic_cbor_encoder,TempAllocator> encoder(v, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // stream + + template + typename std::enable_if::value,void>::type + encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) + { + using char_type = typename T::char_type; + basic_cbor_encoder encoder(os, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) + { + std::error_code ec; + encode_cbor(temp_allocator_arg, temp_alloc, val, os, options, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // namespace cbor +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/csv/csv.hpp b/include/jsoncons_ext/csv/csv.hpp new file mode 100644 index 0000000..9f8a9c5 --- /dev/null +++ b/include/jsoncons_ext/csv/csv.hpp @@ -0,0 +1,17 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_HPP +#define JSONCONS_CSV_CSV_HPP + +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/jsoncons_ext/csv/csv_cursor.hpp b/include/jsoncons_ext/csv/csv_cursor.hpp new file mode 100644 index 0000000..67d55a6 --- /dev/null +++ b/include/jsoncons_ext/csv/csv_cursor.hpp @@ -0,0 +1,358 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_CURSOR_HPP +#define JSONCONS_CSV_CSV_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace csv { + +template,class Allocator=std::allocator> +class basic_csv_cursor : public basic_staj_cursor, private virtual ser_context +{ +public: + using source_type = Source; + using char_type = CharT; + using allocator_type = Allocator; +private: + static constexpr size_t default_max_buffer_size = 16384; + + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + text_source_adaptor source_; + basic_csv_parser parser_; + basic_staj_visitor cursor_visitor_; + + // Noncopyable and nonmoveable + basic_csv_cursor(const basic_csv_cursor&) = delete; + basic_csv_cursor& operator=(const basic_csv_cursor&) = delete; + +public: + using string_view_type = jsoncons::basic_string_view; + + // Constructors that throw parse exceptions + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options = basic_csv_decode_options(), + std::function err_handler = default_csv_parsing(), + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all) + { + if (!done()) + { + next(); + } + } + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options = basic_csv_decode_options(), + std::function err_handler = default_csv_parsing(), + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all) + { + jsoncons::basic_string_view sv(std::forward(source)); + initialize_with_string_view(sv); + } + + + // Constructors that set parse error codes + template + basic_csv_cursor(Sourceable&& source, + std::error_code& ec) + : basic_csv_cursor(std::allocator_arg, Allocator(), + std::forward(source), + basic_csv_decode_options(), + default_csv_parsing(), + ec) + { + } + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options, + std::error_code& ec) + : basic_csv_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + default_csv_parsing(), + ec) + { + } + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options, + std::function err_handler, + std::error_code& ec) + : basic_csv_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + err_handler, + ec) + { + } + + template + basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_csv_decode_options& options, + std::function err_handler, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all) + { + if (!done()) + { + next(ec); + } + } + + template + basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_csv_decode_options& options, + std::function err_handler, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options,err_handler,alloc), + cursor_visitor_(accept_all) + { + jsoncons::basic_string_view sv(std::forward(source)); + initialize_with_string_view(sv, ec); + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source) + { + source_ = std::forward(source); + parser_.reinitialize(); + cursor_visitor_.reset(); + if (!done()) + { + next(); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source) + { + source_ = {}; + parser_.reinitialize(); + cursor_visitor_.reset(); + initialize_with_string_view(std::forward(source)); + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source, std::error_code& ec) + { + source_ = std::forward(source); + parser_.reinitialize(); + cursor_visitor_.reset(); + if (!done()) + { + next(ec); + } + } + + template + typename std::enable_if,Sourceable>::value>::type + reset(Sourceable&& source, std::error_code& ec) + { + source_ = {}; + parser_.reinitialize(); + initialize_with_string_view(std::forward(source), ec); + } + + bool done() const override + { + return parser_.done(); + } + + const basic_staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + static bool accept_all(const basic_staj_event&, const ser_context&) + { + return true; + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + basic_staj_filter_view operator|(basic_csv_cursor& cursor, + std::function&, const ser_context&)> pred) + { + return basic_staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&)") + void read(basic_json_visitor& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&, std::error_code&)") + void read(basic_json_visitor& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + + void initialize_with_string_view(string_view_type sv) + { + auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column())); + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + if (!done()) + { + next(); + } + } + + void initialize_with_string_view(string_view_type sv, std::error_code& ec) + { + auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + ec = json_errc::illegal_unicode_character; + return; + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + if (!done()) + { + next(ec); + } + } + + void read_next(std::error_code& ec) + { + read_next(cursor_visitor_, ec); + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + parser_.parse_some(visitor, ec); + if (ec) return; + } + } +}; + +using csv_stream_cursor = basic_csv_cursor>; +using csv_string_cursor = basic_csv_cursor>; +using wcsv_stream_cursor = basic_csv_cursor>; +using wcsv_string_cursor = basic_csv_cursor>; + +using csv_cursor = basic_csv_cursor; +using wcsv_cursor = basic_csv_cursor; + +}} + +#endif + diff --git a/include/jsoncons_ext/csv/csv_encoder.hpp b/include/jsoncons_ext/csv/csv_encoder.hpp new file mode 100644 index 0000000..49c1a3d --- /dev/null +++ b/include/jsoncons_ext/csv/csv_encoder.hpp @@ -0,0 +1,954 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_ENCODER_HPP +#define JSONCONS_CSV_CSV_ENCODER_HPP + +#include // std::array +#include +#include +#include +#include // std::move +#include // std::unordered_map +#include // std::allocator +#include // std::numeric_limits +#include +#include +#include +#include +#include + +namespace jsoncons { namespace csv { + +template,class Allocator=std::allocator> +class basic_csv_encoder final : public basic_json_visitor +{ +public: + using char_type = CharT; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + + using allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using string_type = std::basic_string, char_allocator_type>; + using string_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using string_string_allocator_type = typename std::allocator_traits:: template rebind_alloc>; + +private: + static jsoncons::basic_string_view null_constant() + { + static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"null"); + return k; + } + static jsoncons::basic_string_view true_constant() + { + static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"true"); + return k; + } + static jsoncons::basic_string_view false_constant() + { + static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"false"); + return k; + } + + enum class stack_item_kind + { + row_mapping, + column_mapping, + object, + row, + column, + object_multi_valued_field, + row_multi_valued_field, + column_multi_valued_field + }; + + struct stack_item + { + stack_item_kind item_kind_; + std::size_t count_; + + stack_item(stack_item_kind item_kind) noexcept + : item_kind_(item_kind), count_(0) + { + } + + bool is_object() const + { + return item_kind_ == stack_item_kind::object; + } + + stack_item_kind item_kind() const + { + return item_kind_; + } + }; + + Sink sink_; + const basic_csv_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + jsoncons::detail::write_double fp_; + std::vector strings_buffer_; + + std::unordered_map,std::equal_to,string_string_allocator_type> buffered_line_; + string_type name_; + std::size_t column_index_; + std::vector row_counts_; + + // Noncopyable and nonmoveable + basic_csv_encoder(const basic_csv_encoder&) = delete; + basic_csv_encoder& operator=(const basic_csv_encoder&) = delete; +public: + basic_csv_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_csv_encoder(std::forward(sink), basic_csv_encode_options(), alloc) + { + } + + basic_csv_encoder(Sink&& sink, + const basic_csv_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + stack_(), + fp_(options.float_format(), options.precision()), + column_index_(0) + { + jsoncons::csv::detail::parse_column_names(options.column_names(), strings_buffer_); + } + + ~basic_csv_encoder() noexcept + { + JSONCONS_TRY + { + sink_.flush(); + } + JSONCONS_CATCH(...) + { + } + } + + void reset() + { + stack_.clear(); + strings_buffer_.clear(); + buffered_line_.clear(); + name_.clear(); + column_index_ = 0; + row_counts_.clear(); + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + +private: + + template + void escape_string(const CharT* s, + std::size_t length, + CharT quote_char, CharT quote_escape_char, + AnyWriter& sink) + { + const CharT* begin = s; + const CharT* end = s + length; + for (const CharT* it = begin; it != end; ++it) + { + CharT c = *it; + if (c == quote_char) + { + sink.push_back(quote_escape_char); + sink.push_back(quote_char); + } + else + { + sink.push_back(c); + } + } + } + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + stack_.emplace_back(stack_item_kind::column_mapping); + return true; + } + switch (stack_.back().item_kind_) + { + case stack_item_kind::row_mapping: + stack_.emplace_back(stack_item_kind::object); + return true; + default: // error + ec = csv_errc::source_error; + return false; + } + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + if (stack_[0].count_ == 0) + { + for (std::size_t i = 0; i < strings_buffer_.size(); ++i) + { + if (i > 0) + { + sink_.push_back(options_.field_delimiter()); + } + sink_.append(strings_buffer_[i].data(), + strings_buffer_[i].length()); + } + sink_.append(options_.line_delimiter().data(), + options_.line_delimiter().length()); + } + for (std::size_t i = 0; i < strings_buffer_.size(); ++i) + { + if (i > 0) + { + sink_.push_back(options_.field_delimiter()); + } + auto it = buffered_line_.find(strings_buffer_[i]); + if (it != buffered_line_.end()) + { + sink_.append(it->second.data(),it->second.length()); + it->second.clear(); + } + } + sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length()); + break; + case stack_item_kind::column_mapping: + { + for (const auto& item : strings_buffer_) + { + sink_.append(item.data(), item.size()); + sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length()); + } + break; + } + default: + break; + } + stack_.pop_back(); + if (!stack_.empty()) + { + end_value(); + } + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + stack_.emplace_back(stack_item_kind::row_mapping); + return true; + } + switch (stack_.back().item_kind_) + { + case stack_item_kind::row_mapping: + stack_.emplace_back(stack_item_kind::row); + if (stack_[0].count_ == 0) + { + for (std::size_t i = 0; i < strings_buffer_.size(); ++i) + { + if (i > 0) + { + sink_.push_back(options_.field_delimiter()); + } + sink_.append(strings_buffer_[i].data(),strings_buffer_[i].length()); + } + if (strings_buffer_.size() > 0) + { + sink_.append(options_.line_delimiter().data(), + options_.line_delimiter().length()); + } + } + return true; + case stack_item_kind::object: + stack_.emplace_back(stack_item_kind::object_multi_valued_field); + return true; + case stack_item_kind::column_mapping: + stack_.emplace_back(stack_item_kind::column); + row_counts_.push_back(1); + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + return true; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + begin_value(bo); + stack_.emplace_back(stack_item_kind::column_multi_valued_field); + return true; + } + case stack_item_kind::row: + begin_value(sink_); + stack_.emplace_back(stack_item_kind::row_multi_valued_field); + return true; + default: // error + ec = csv_errc::source_error; + return false; + } + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::row: + sink_.append(options_.line_delimiter().data(), + options_.line_delimiter().length()); + break; + case stack_item_kind::column: + ++column_index_; + break; + default: + break; + } + stack_.pop_back(); + + if (!stack_.empty()) + { + end_value(); + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + { + name_ = string_type(name); + buffered_line_[string_type(name)] = std::basic_string(); + if (stack_[0].count_ == 0 && options_.column_names().size() == 0) + { + strings_buffer_.emplace_back(name); + } + break; + } + case stack_item_kind::column_mapping: + { + if (strings_buffer_.empty()) + { + strings_buffer_.emplace_back(name); + } + else + { + strings_buffer_[0].push_back(options_.field_delimiter()); + strings_buffer_[0].append(string_type(name)); + } + break; + } + default: + break; + } + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_null_value(bo); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_null_value(sink_); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_null_value(bo); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_null_value(bo); + break; + } + default: + break; + } + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag, const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_string_value(sv,bo); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_string_value(sv,sink_); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_string_value(sv,bo); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_string_value(sv,bo); + break; + } + default: + break; + } + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag tag, + const ser_context& context, + std::error_code& ec) override + { + byte_string_chars_format encoding_hint; + switch (tag) + { + case semantic_tag::base16: + encoding_hint = byte_string_chars_format::base16; + break; + case semantic_tag::base64: + encoding_hint = byte_string_chars_format::base64; + break; + case semantic_tag::base64url: + encoding_hint = byte_string_chars_format::base64url; + break; + default: + encoding_hint = byte_string_chars_format::none; + break; + } + byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(encoding_hint,byte_string_chars_format::none,byte_string_chars_format::base64url); + + std::basic_string s; + switch (format) + { + case byte_string_chars_format::base16: + { + encode_base16(b.begin(),b.end(),s); + visit_string(s, semantic_tag::none, context, ec); + break; + } + case byte_string_chars_format::base64: + { + encode_base64(b.begin(),b.end(),s); + visit_string(s, semantic_tag::none, context, ec); + break; + } + case byte_string_chars_format::base64url: + { + encode_base64url(b.begin(),b.end(),s); + visit_string(s, semantic_tag::none, context, ec); + break; + } + default: + { + JSONCONS_UNREACHABLE(); + } + } + + return true; + } + + bool visit_double(double val, + semantic_tag, + const ser_context& context, + std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_double_value(val, context, bo, ec); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_double_value(val, context, sink_, ec); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_double_value(val, context, bo, ec); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_double_value(val, context, bo, ec); + break; + } + default: + break; + } + return true; + } + + bool visit_int64(int64_t val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_int64_value(val,bo); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_int64_value(val,sink_); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_int64_value(val, bo); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_int64_value(val, bo); + break; + } + default: + break; + } + return true; + } + + bool visit_uint64(uint64_t val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_uint64_value(val, bo); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_uint64_value(val,sink_); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_uint64_value(val, bo); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_uint64_value(val, bo); + break; + } + default: + break; + } + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::object: + case stack_item_kind::object_multi_valued_field: + { + auto it = buffered_line_.find(name_); + if (it != buffered_line_.end()) + { + std::basic_string s; + jsoncons::string_sink> bo(s); + write_bool_value(val,bo); + bo.flush(); + if (!it->second.empty() && options_.subfield_delimiter() != char_type()) + { + it->second.push_back(options_.subfield_delimiter()); + } + it->second.append(s); + } + break; + } + case stack_item_kind::row: + case stack_item_kind::row_multi_valued_field: + write_bool_value(val,sink_); + break; + case stack_item_kind::column: + { + if (strings_buffer_.size() <= row_counts_.back()) + { + strings_buffer_.emplace_back(); + } + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_bool_value(val, bo); + break; + } + case stack_item_kind::column_multi_valued_field: + { + jsoncons::string_sink> bo(strings_buffer_[row_counts_.back()]); + write_bool_value(val, bo); + break; + } + default: + break; + } + return true; + } + + template + bool do_string_value(const CharT* s, std::size_t length, AnyWriter& sink) + { + bool quote = false; + if (options_.quote_style() == quote_style_kind::all || options_.quote_style() == quote_style_kind::nonnumeric || + (options_.quote_style() == quote_style_kind::minimal && + (std::char_traits::find(s, length, options_.field_delimiter()) != nullptr || std::char_traits::find(s, length, options_.quote_char()) != nullptr))) + { + quote = true; + sink.push_back(options_.quote_char()); + } + escape_string(s, length, options_.quote_char(), options_.quote_escape_char(), sink); + if (quote) + { + sink.push_back(options_.quote_char()); + } + + return true; + } + + template + void write_string_value(const string_view_type& value, AnyWriter& sink) + { + begin_value(sink); + do_string_value(value.data(),value.length(),sink); + end_value(); + } + + template + void write_double_value(double val, const ser_context& context, AnyWriter& sink, std::error_code& ec) + { + begin_value(sink); + + if (!std::isfinite(val)) + { + if ((std::isnan)(val)) + { + if (options_.enable_nan_to_num()) + { + sink.append(options_.nan_to_num().data(), options_.nan_to_num().length()); + } + else if (options_.enable_nan_to_str()) + { + visit_string(options_.nan_to_str(), semantic_tag::none, context, ec); + } + else + { + sink.append(null_constant().data(), null_constant().size()); + } + } + else if (val == std::numeric_limits::infinity()) + { + if (options_.enable_inf_to_num()) + { + sink.append(options_.inf_to_num().data(), options_.inf_to_num().length()); + } + else if (options_.enable_inf_to_str()) + { + visit_string(options_.inf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink.append(null_constant().data(), null_constant().size()); + } + } + else + { + if (options_.enable_neginf_to_num()) + { + sink.append(options_.neginf_to_num().data(), options_.neginf_to_num().length()); + } + else if (options_.enable_neginf_to_str()) + { + visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec); + } + else + { + sink.append(null_constant().data(), null_constant().size()); + } + } + } + else + { + fp_(val, sink); + } + + end_value(); + + } + + template + void write_int64_value(int64_t val, AnyWriter& sink) + { + begin_value(sink); + + jsoncons::detail::from_integer(val,sink); + + end_value(); + } + + template + void write_uint64_value(uint64_t val, AnyWriter& sink) + { + begin_value(sink); + + jsoncons::detail::from_integer(val,sink); + + end_value(); + } + + template + void write_bool_value(bool val, AnyWriter& sink) + { + begin_value(sink); + + if (val) + { + sink.append(true_constant().data(), true_constant().size()); + } + else + { + sink.append(false_constant().data(), false_constant().size()); + } + + end_value(); + } + + template + bool write_null_value(AnyWriter& sink) + { + begin_value(sink); + sink.append(null_constant().data(), null_constant().size()); + end_value(); + return true; + } + + template + void begin_value(AnyWriter& sink) + { + JSONCONS_ASSERT(!stack_.empty()); + switch (stack_.back().item_kind_) + { + case stack_item_kind::row: + if (stack_.back().count_ > 0) + { + sink.push_back(options_.field_delimiter()); + } + break; + case stack_item_kind::column: + { + if (row_counts_.size() >= 3) + { + for (std::size_t i = row_counts_.size()-2; i-- > 0;) + { + if (row_counts_[i] <= row_counts_.back()) + { + sink.push_back(options_.field_delimiter()); + } + else + { + break; + } + } + } + if (column_index_ > 0) + { + sink.push_back(options_.field_delimiter()); + } + break; + } + case stack_item_kind::row_multi_valued_field: + case stack_item_kind::column_multi_valued_field: + if (stack_.back().count_ > 0 && options_.subfield_delimiter() != char_type()) + { + sink.push_back(options_.subfield_delimiter()); + } + break; + default: + break; + } + } + + void end_value() + { + JSONCONS_ASSERT(!stack_.empty()); + switch(stack_.back().item_kind_) + { + case stack_item_kind::row: + { + ++stack_.back().count_; + break; + } + case stack_item_kind::column: + { + ++row_counts_.back(); + break; + } + default: + ++stack_.back().count_; + break; + } + } +}; + +using csv_stream_encoder = basic_csv_encoder; +using csv_string_encoder = basic_csv_encoder>; +using csv_wstream_encoder = basic_csv_encoder; +using wcsv_string_encoder = basic_csv_encoder>; + +#if !defined(JSONCONS_NO_DEPRECATED) +template, class Allocator = std::allocator> +using basic_csv_serializer = basic_csv_encoder; + +JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use wcsv_stream_encoder") typedef csv_stream_encoder wcsv_encoder; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/csv/csv_error.hpp b/include/jsoncons_ext/csv/csv_error.hpp new file mode 100644 index 0000000..30255dd --- /dev/null +++ b/include/jsoncons_ext/csv/csv_error.hpp @@ -0,0 +1,85 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_ERROR_HPP +#define JSONCONS_CSV_CSV_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace csv { + + enum class csv_errc : int + { + success = 0, + unexpected_eof = 1, + source_error, + expected_quote, + syntax_error, + invalid_parse_state, + invalid_escaped_char, + unexpected_char_between_fields + }; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use csv_errc") typedef csv_errc csv_parser_errc; +#endif + +class csv_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/csv"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case csv_errc::unexpected_eof: + return "Unexpected end of file"; + case csv_errc::source_error: + return "Source error"; + case csv_errc::expected_quote: + return "Expected quote character"; + case csv_errc::syntax_error: + return "CSV syntax error"; + case csv_errc::invalid_parse_state: + return "Invalid CSV parser state"; + case csv_errc::invalid_escaped_char: + return "Invalid character following quote escape character"; + case csv_errc::unexpected_char_between_fields: + return "Unexpected character between fields"; + default: + return "Unknown CSV parser error"; + } + } +}; + +inline +const std::error_category& csv_error_category() +{ + static csv_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(csv_errc result) +{ + return std::error_code(static_cast(result),csv_error_category()); +} + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/csv/csv_options.hpp b/include/jsoncons_ext/csv/csv_options.hpp new file mode 100644 index 0000000..8bd2e22 --- /dev/null +++ b/include/jsoncons_ext/csv/csv_options.hpp @@ -0,0 +1,973 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_OPTIONS_HPP +#define JSONCONS_CSV_CSV_OPTIONS_HPP + +#include +#include +#include // std::pair +#include // std::unordered_map +#include +#include // std::numeric_limits +#include +#include + +namespace jsoncons { namespace csv { + +enum class csv_column_type : uint8_t +{ + string_t,integer_t,float_t,boolean_t,repeat_t +}; + +enum class quote_style_kind : uint8_t +{ + minimal,all,nonnumeric,none +}; + +enum class csv_mapping_kind : uint8_t +{ + n_rows = 1, + n_objects, + m_columns +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +using mapping_kind = csv_mapping_kind; +JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_styles; +JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_style_type; +JSONCONS_DEPRECATED_MSG("Instead, use csv_mapping_kind") typedef csv_mapping_kind mapping_type; +#endif + +enum class column_state {sequence,label}; + +struct csv_type_info +{ + csv_type_info() = default; + csv_type_info(const csv_type_info&) = default; + csv_type_info(csv_type_info&&) = default; + + csv_type_info(csv_column_type ctype, std::size_t lev, std::size_t repcount = 0) noexcept + { + col_type = ctype; + level = lev; + rep_count = repcount; + } + + csv_column_type col_type; + std::size_t level; + std::size_t rep_count; +}; + +namespace detail { + +template +void parse_column_names(const std::basic_string& names, + Container& cont) +{ + column_state state = column_state::sequence; + typename Container::value_type buffer(cont.get_allocator()); + + auto p = names.begin(); + while (p != names.end()) + { + switch (state) + { + case column_state::sequence: + { + switch (*p) + { + case ' ': case '\t':case '\r': case '\n': + ++p; + break; + default: + buffer.clear(); + state = column_state::label; + break; + } + break; + } + case column_state::label: + { + switch (*p) + { + case ',': + cont.push_back(buffer); + buffer.clear(); + ++p; + state = column_state::sequence; + break; + default: + buffer.push_back(*p); + ++p; + break; + } + break; + } + } + } + if (state == column_state::label) + { + cont.push_back(buffer); + buffer.clear(); + } +} + +template +void parse_column_types(const std::basic_string& types, + Container& column_types) +{ + const std::map,csv_column_type> type_dictionary = + { + + {JSONCONS_STRING_VIEW_CONSTANT(CharT,"string"),csv_column_type::string_t}, + {JSONCONS_STRING_VIEW_CONSTANT(CharT,"integer"),csv_column_type::integer_t}, + {JSONCONS_STRING_VIEW_CONSTANT(CharT,"float"),csv_column_type::float_t}, + {JSONCONS_STRING_VIEW_CONSTANT(CharT,"boolean"),csv_column_type::boolean_t} + }; + + column_state state = column_state::sequence; + int depth = 0; + std::basic_string buffer; + + auto p = types.begin(); + while (p != types.end()) + { + switch (state) + { + case column_state::sequence: + { + switch (*p) + { + case ' ': case '\t':case '\r': case '\n': + ++p; + break; + case '[': + ++depth; + ++p; + break; + case ']': + JSONCONS_ASSERT(depth > 0); + --depth; + ++p; + break; + case '*': + { + JSONCONS_ASSERT(column_types.size() != 0); + std::size_t offset = 0; + std::size_t level = column_types.size() > 0 ? column_types.back().level: 0; + if (level > 0) + { + for (auto it = column_types.rbegin(); + it != column_types.rend() && level == it->level; + ++it) + { + ++offset; + } + } + else + { + offset = 1; + } + column_types.emplace_back(csv_column_type::repeat_t,depth,offset); + ++p; + break; + } + default: + buffer.clear(); + state = column_state::label; + break; + } + break; + } + case column_state::label: + { + switch (*p) + { + case '*': + { + auto it = type_dictionary.find(buffer); + if (it != type_dictionary.end()) + { + column_types.emplace_back(it->second,depth); + buffer.clear(); + } + else + { + JSONCONS_ASSERT(false); + } + state = column_state::sequence; + break; + } + case ',': + { + auto it = type_dictionary.find(buffer); + if (it != type_dictionary.end()) + { + column_types.emplace_back(it->second,depth); + buffer.clear(); + } + else + { + JSONCONS_ASSERT(false); + } + ++p; + state = column_state::sequence; + break; + } + case ']': + { + JSONCONS_ASSERT(depth > 0); + auto it = type_dictionary.find(buffer); + if (it != type_dictionary.end()) + { + column_types.emplace_back(it->second,depth); + buffer.clear(); + } + else + { + JSONCONS_ASSERT(false); + } + --depth; + ++p; + state = column_state::sequence; + break; + } + default: + { + buffer.push_back(*p); + ++p; + break; + } + } + break; + } + } + } + if (state == column_state::label) + { + auto it = type_dictionary.find(buffer); + if (it != type_dictionary.end()) + { + column_types.emplace_back(it->second,depth); + buffer.clear(); + } + else + { + JSONCONS_ASSERT(false); + } + } +} + +} // detail + +template +class basic_csv_options; + +template +class basic_csv_options_common +{ + friend class basic_csv_options; +public: + using char_type = CharT; + using string_type = std::basic_string; +private: + char_type field_delimiter_; + char_type quote_char_; + char_type quote_escape_char_; + char_type subfield_delimiter_; + + bool enable_nan_to_num_:1; + bool enable_inf_to_num_:1; + bool enable_neginf_to_num_:1; + bool enable_nan_to_str_:1; + bool enable_inf_to_str_:1; + bool enable_neginf_to_str_:1; + bool enable_str_to_nan_:1; + bool enable_str_to_inf_:1; + bool enable_str_to_neginf_:1; + + string_type nan_to_num_; + string_type inf_to_num_; + string_type neginf_to_num_; + string_type nan_to_str_; + string_type inf_to_str_; + string_type neginf_to_str_; + string_type column_names_; + +protected: + basic_csv_options_common() + : field_delimiter_(','), + quote_char_('\"'), + quote_escape_char_('\"'), + subfield_delimiter_(char_type()), + enable_nan_to_num_(false), + enable_inf_to_num_(false), + enable_neginf_to_num_(false), + enable_nan_to_str_(false), + enable_inf_to_str_(false), + enable_neginf_to_str_(false), + enable_str_to_nan_(false), + enable_str_to_inf_(false), + enable_str_to_neginf_(false) + { + } + + basic_csv_options_common(const basic_csv_options_common&) = default; + basic_csv_options_common& operator=(const basic_csv_options_common&) = default; + + virtual ~basic_csv_options_common() noexcept = default; +public: + + char_type field_delimiter() const + { + return field_delimiter_; + } + + const char_type subfield_delimiter() const + { + return subfield_delimiter_; + } + + char_type quote_char() const + { + return quote_char_; + } + + char_type quote_escape_char() const + { + return quote_escape_char_; + } + + string_type column_names() const + { + return column_names_; + } + + bool enable_nan_to_num() const + { + return enable_nan_to_num_; + } + + bool enable_inf_to_num() const + { + return enable_inf_to_num_; + } + + bool enable_neginf_to_num() const + { + return enable_neginf_to_num_ || enable_inf_to_num_; + } + + bool enable_nan_to_str() const + { + return enable_nan_to_str_; + } + + bool enable_str_to_nan() const + { + return enable_str_to_nan_; + } + + bool enable_inf_to_str() const + { + return enable_inf_to_str_; + } + + bool enable_str_to_inf() const + { + return enable_str_to_inf_; + } + + bool enable_neginf_to_str() const + { + return enable_neginf_to_str_ || enable_inf_to_str_; + } + + bool enable_str_to_neginf() const + { + return enable_str_to_neginf_ || enable_str_to_inf_; + } + + string_type nan_to_num() const + { + return nan_to_num_; + } + + string_type inf_to_num() const + { + return inf_to_num_; + } + + string_type neginf_to_num() const + { + if (enable_neginf_to_num_) + { + return neginf_to_num_; + } + else if (enable_inf_to_num_) + { + string_type s; + s.push_back('-'); + s.append(inf_to_num_); + return s; + } + else + { + return neginf_to_num_; + } + } + + string_type nan_to_str() const + { + return nan_to_str_; + } + + string_type inf_to_str() const + { + return inf_to_str_; + } + + string_type neginf_to_str() const + { + if (enable_neginf_to_str_) + { + return neginf_to_str_; + } + else if (enable_inf_to_str_) + { + string_type s; + s.push_back('-'); + s.append(inf_to_str_); + return s; + } + else + { + return neginf_to_str_; // empty string + } + } +}; + +template +class basic_csv_decode_options : public virtual basic_csv_options_common +{ + friend class basic_csv_options; + using super_type = basic_csv_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; + +private: + bool assume_header_:1; + bool ignore_empty_values_:1; + bool ignore_empty_lines_:1; + bool trim_leading_:1; + bool trim_trailing_:1; + bool trim_leading_inside_quotes_:1; + bool trim_trailing_inside_quotes_:1; + bool unquoted_empty_value_is_null_:1; + bool infer_types_:1; + bool lossless_number_:1; + char_type comment_starter_; + csv_mapping_kind mapping_; + std::size_t header_lines_; + std::size_t max_lines_; + string_type column_types_; + string_type column_defaults_; +public: + basic_csv_decode_options() + : assume_header_(false), + ignore_empty_values_(false), + ignore_empty_lines_(true), + trim_leading_(false), + trim_trailing_(false), + trim_leading_inside_quotes_(false), + trim_trailing_inside_quotes_(false), + unquoted_empty_value_is_null_(false), + infer_types_(true), + lossless_number_(false), + comment_starter_('\0'), + mapping_(), + header_lines_(0), + max_lines_((std::numeric_limits::max)()) + {} + + basic_csv_decode_options(const basic_csv_decode_options& other) = default; + + basic_csv_decode_options(basic_csv_decode_options&& other) + : super_type(std::forward(other)), + assume_header_(other.assume_header_), + ignore_empty_values_(other.ignore_empty_values_), + ignore_empty_lines_(other.ignore_empty_lines_), + trim_leading_(other.trim_leading_), + trim_trailing_(other.trim_trailing_), + trim_leading_inside_quotes_(other.trim_leading_inside_quotes_), + trim_trailing_inside_quotes_(other.trim_trailing_inside_quotes_), + unquoted_empty_value_is_null_(other.unquoted_empty_value_is_null_), + infer_types_(other.infer_types_), + lossless_number_(other.lossless_number_), + comment_starter_(other.comment_starter_), + mapping_(other.mapping_), + header_lines_(other.header_lines_), + max_lines_(other.max_lines_), + column_types_(std::move(other.column_types_)), + column_defaults_(std::move(other.column_defaults_)) + {} + + std::size_t header_lines() const + { + return (assume_header_ && header_lines_ <= 1) ? 1 : header_lines_; + } + + bool assume_header() const + { + return assume_header_; + } + + bool ignore_empty_values() const + { + return ignore_empty_values_; + } + + bool ignore_empty_lines() const + { + return ignore_empty_lines_; + } + + bool trim_leading() const + { + return trim_leading_; + } + + bool trim_trailing() const + { + return trim_trailing_; + } + + bool trim_leading_inside_quotes() const + { + return trim_leading_inside_quotes_; + } + + bool trim_trailing_inside_quotes() const + { + return trim_trailing_inside_quotes_; + } + + bool trim() const + { + return trim_leading_ && trim_trailing_; + } + + bool trim_inside_quotes() const + { + return trim_leading_inside_quotes_ && trim_trailing_inside_quotes_; + } + + bool unquoted_empty_value_is_null() const + { + return unquoted_empty_value_is_null_; + } + + bool infer_types() const + { + return infer_types_; + } + + bool lossless_number() const + { + return lossless_number_; + } + + char_type comment_starter() const + { + return comment_starter_; + } + + csv_mapping_kind mapping_kind() const + { + return mapping_ != csv_mapping_kind() ? mapping_ : (assume_header() || this->column_names().size() > 0 ? csv_mapping_kind::n_objects : csv_mapping_kind::n_rows); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + csv_mapping_kind mapping() const + { + return mapping_kind(); + } +#endif + + std::size_t max_lines() const + { + return max_lines_; + } + + string_type column_types() const + { + return column_types_; + } + + string_type column_defaults() const + { + return column_defaults_; + } +}; + +template +class basic_csv_encode_options : public virtual basic_csv_options_common +{ + friend class basic_csv_options; + using super_type = basic_csv_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; +private: + quote_style_kind quote_style_; + float_chars_format float_format_; + int8_t precision_; + string_type line_delimiter_; +public: + basic_csv_encode_options() + : quote_style_(quote_style_kind::minimal), + float_format_(float_chars_format::general), + precision_(0) + { + line_delimiter_.push_back('\n'); + } + + basic_csv_encode_options(const basic_csv_encode_options& other) = default; + + basic_csv_encode_options(basic_csv_encode_options&& other) + : super_type(std::forward(other)), + quote_style_(other.quote_style_), + float_format_(other.float_format_), + precision_(other.precision_), + line_delimiter_(std::move(other.line_delimiter_)) + { + } + + quote_style_kind quote_style() const + { + return quote_style_; + } + + float_chars_format float_format() const + { + return float_format_; + } + + int8_t precision() const + { + return precision_; + } + + string_type line_delimiter() const + { + return line_delimiter_; + } +}; + +template +class basic_csv_options final : public basic_csv_decode_options, public basic_csv_encode_options +{ + using char_type = CharT; + using string_type = std::basic_string; + +public: + using basic_csv_decode_options::enable_str_to_nan; + using basic_csv_decode_options::enable_str_to_inf; + using basic_csv_decode_options::enable_str_to_neginf; + using basic_csv_decode_options::nan_to_str; + using basic_csv_decode_options::inf_to_str; + using basic_csv_decode_options::neginf_to_str; + using basic_csv_decode_options::nan_to_num; + using basic_csv_decode_options::inf_to_num; + using basic_csv_decode_options::neginf_to_num; + using basic_csv_decode_options::field_delimiter; + using basic_csv_decode_options::subfield_delimiter; + using basic_csv_decode_options::quote_char; + using basic_csv_decode_options::quote_escape_char; + using basic_csv_decode_options::column_names; + using basic_csv_decode_options::header_lines; + using basic_csv_decode_options::assume_header; + using basic_csv_decode_options::ignore_empty_values; + using basic_csv_decode_options::ignore_empty_lines; + using basic_csv_decode_options::trim_leading; + using basic_csv_decode_options::trim_trailing; + using basic_csv_decode_options::trim_leading_inside_quotes; + using basic_csv_decode_options::trim_trailing_inside_quotes; + using basic_csv_decode_options::trim; + using basic_csv_decode_options::trim_inside_quotes; + using basic_csv_decode_options::unquoted_empty_value_is_null; + using basic_csv_decode_options::infer_types; + using basic_csv_decode_options::lossless_number; + using basic_csv_decode_options::comment_starter; + using basic_csv_decode_options::mapping; + using basic_csv_decode_options::max_lines; + using basic_csv_decode_options::column_types; + using basic_csv_decode_options::column_defaults; + using basic_csv_encode_options::float_format; + using basic_csv_encode_options::precision; + using basic_csv_encode_options::line_delimiter; + using basic_csv_encode_options::quote_style; + + static constexpr size_t default_indent = 4; + +// Constructors + + basic_csv_options() = default; + basic_csv_options(const basic_csv_options&) = default; + basic_csv_options(basic_csv_options&&) = default; + basic_csv_options& operator=(const basic_csv_options&) = default; + basic_csv_options& operator=(basic_csv_options&&) = default; + + basic_csv_options& float_format(float_chars_format value) + { + this->float_format_ = value; + return *this; + } + + basic_csv_options& precision(int8_t value) + { + this->precision_ = value; + return *this; + } + + basic_csv_options& header_lines(std::size_t value) + { + this->header_lines_ = value; + return *this; + } + + basic_csv_options& assume_header(bool value) + { + this->assume_header_ = value; + return *this; + } + + basic_csv_options& ignore_empty_values(bool value) + { + this->ignore_empty_values_ = value; + return *this; + } + + basic_csv_options& ignore_empty_lines(bool value) + { + this->ignore_empty_lines_ = value; + return *this; + } + + basic_csv_options& trim_leading(bool value) + { + this->trim_leading_ = value; + return *this; + } + + basic_csv_options& trim_trailing(bool value) + { + this->trim_trailing_ = value; + return *this; + } + + basic_csv_options& trim_leading_inside_quotes(bool value) + { + this->trim_leading_inside_quotes_ = value; + return *this; + } + + basic_csv_options& trim_trailing_inside_quotes(bool value) + { + this->trim_trailing_inside_quotes_ = value; + return *this; + } + + basic_csv_options& trim(bool value) + { + this->trim_leading_ = value; + this->trim_trailing_ = value; + return *this; + } + + basic_csv_options& trim_inside_quotes(bool value) + { + this->trim_leading_inside_quotes_ = value; + this->trim_trailing_inside_quotes_ = value; + return *this; + } + + basic_csv_options& unquoted_empty_value_is_null(bool value) + { + this->unquoted_empty_value_is_null_ = value; + return *this; + } + + basic_csv_options& column_names(const string_type& value) + { + this->column_names_ = value; + return *this; + } + + basic_csv_options& column_types(const string_type& value) + { + this->column_types_ = value; + return *this; + } + + basic_csv_options& column_defaults(const string_type& value) + { + this->column_defaults_ = value; + return *this; + } + + basic_csv_options& field_delimiter(char_type value) + { + this->field_delimiter_ = value; + return *this; + } + + basic_csv_options& subfield_delimiter(char_type value) + { + this->subfield_delimiter_ = value; + return *this; + } + + basic_csv_options& line_delimiter(const string_type& value) + { + this->line_delimiter_ = value; + return *this; + } + + basic_csv_options& quote_char(char_type value) + { + this->quote_char_ = value; + return *this; + } + + basic_csv_options& infer_types(bool value) + { + this->infer_types_ = value; + return *this; + } + + basic_csv_options& lossless_number(bool value) + { + this->lossless_number_ = value; + return *this; + } + + basic_csv_options& quote_escape_char(char_type value) + { + this->quote_escape_char_ = value; + return *this; + } + + basic_csv_options& comment_starter(char_type value) + { + this->comment_starter_ = value; + return *this; + } + + basic_csv_options& quote_style(quote_style_kind value) + { + this->quote_style_ = value; + return *this; + } + +//#if !defined(JSONCONS_NO_DEPRECATED) + basic_csv_options& mapping(csv_mapping_kind value) + { + this->mapping_ = value; + return *this; + } +//#endif + + basic_csv_options& mapping_kind(csv_mapping_kind value) + { + this->mapping_ = value; + return *this; + } + + basic_csv_options& max_lines(std::size_t value) + { + this->max_lines_ = value; + return *this; + } + + basic_csv_options& nan_to_num(const string_type& value) + { + this->enable_nan_to_num_ = true; + this->nan_to_str_.clear(); + this->nan_to_num_ = value; + return *this; + } + + basic_csv_options& inf_to_num(const string_type& value) + { + this->enable_inf_to_num_ = true; + this->inf_to_str_.clear(); + this->inf_to_num_ = value; + return *this; + } + + basic_csv_options& neginf_to_num(const string_type& value) + { + this->enable_neginf_to_num_ = true; + this->neginf_to_str_.clear(); + this->neginf_to_num_ = value; + return *this; + } + + basic_csv_options& nan_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_nan_to_str_ = true; + this->enable_str_to_nan_ = enable_inverse; + this->nan_to_num_.clear(); + this->nan_to_str_ = value; + return *this; + } + + basic_csv_options& inf_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_inf_to_str_ = true; + this->enable_inf_to_str_ = enable_inverse; + this->inf_to_num_.clear(); + this->inf_to_str_ = value; + return *this; + } + + basic_csv_options& neginf_to_str(const string_type& value, bool enable_inverse = true) + { + this->enable_neginf_to_str_ = true; + this->enable_neginf_to_str_ = enable_inverse; + this->neginf_to_num_.clear(); + this->neginf_to_str_ = value; + return *this; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use float_format(float_chars_format)") + basic_csv_options& floating_point_format(float_chars_format value) + { + this->float_format_ = value; + return *this; + } +#endif + +}; + +using csv_options = basic_csv_options; +using wcsv_options = basic_csv_options; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_parameters; +JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_parameters; +JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_serializing_options; +JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_serializing_options; +#endif + + +}} +#endif diff --git a/include/jsoncons_ext/csv/csv_parser.hpp b/include/jsoncons_ext/csv/csv_parser.hpp new file mode 100644 index 0000000..37887e2 --- /dev/null +++ b/include/jsoncons_ext/csv/csv_parser.hpp @@ -0,0 +1,2097 @@ +// Copyright 2015 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_PARSER_HPP +#define JSONCONS_CSV_CSV_PARSER_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace csv { + +enum class csv_mode +{ + initial, + header, + data, + subfields +}; + +enum class csv_parse_state +{ + start, + cr, + column_labels, + expect_comment_or_record, + expect_record, + end_record, + no_more_records, + comment, + between_values, + quoted_string, + unquoted_string, + before_unquoted_string, + escaped_value, + minus, + zero, + integer, + fraction, + exp1, + exp2, + exp3, + accept, + before_unquoted_field, + before_unquoted_field_tail, + before_unquoted_field_tail1, + before_last_unquoted_field, + before_last_unquoted_field_tail, + before_unquoted_subfield, + before_unquoted_subfield_tail, + before_quoted_subfield, + before_quoted_subfield_tail, + before_quoted_field, + before_quoted_field_tail, + before_last_quoted_field, + before_last_quoted_field_tail, + done +}; + +enum class cached_state +{ + begin_object, + end_object, + begin_array, + end_array, + name, + item, + done +}; + +struct default_csv_parsing +{ + bool operator()(csv_errc, const ser_context&) noexcept + { + return false; + } +}; + +namespace detail { + + template + class parse_event + { + using temp_allocator_type = TempAllocator; + using string_view_type = typename basic_json_visitor::string_view_type; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using string_type = std::basic_string,char_allocator_type>; + using byte_string_type = basic_byte_string; + + staj_event_type event_type; + string_type string_value; + byte_string_type byte_string_value; + union + { + bool bool_value; + int64_t int64_value; + uint64_t uint64_value; + double double_value; + }; + semantic_tag tag; + public: + parse_event(staj_event_type event_type, semantic_tag tag, const TempAllocator& alloc) + : event_type(event_type), + string_value(alloc), + byte_string_value(alloc), + tag(tag) + { + } + + parse_event(const string_view_type& value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::string_value), + string_value(value.data(),value.length(),alloc), + byte_string_value(alloc), + tag(tag) + { + } + + parse_event(const byte_string_view& value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::byte_string_value), + string_value(alloc), + byte_string_value(value.data(),value.size(),alloc), + tag(tag) + { + } + + parse_event(bool value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::bool_value), + string_value(alloc), + byte_string_value(alloc), + bool_value(value), + tag(tag) + { + } + + parse_event(int64_t value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::int64_value), + string_value(alloc), + byte_string_value(alloc), + int64_value(value), + tag(tag) + { + } + + parse_event(uint64_t value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::uint64_value), + string_value(alloc), + byte_string_value(alloc), + uint64_value(value), + tag(tag) + { + } + + parse_event(double value, semantic_tag tag, const TempAllocator& alloc) + : event_type(staj_event_type::double_value), + string_value(alloc), + byte_string_value(alloc), + double_value(value), + tag(tag) + { + } + + parse_event(const parse_event&) = default; + parse_event(parse_event&&) = default; + parse_event& operator=(const parse_event&) = default; + parse_event& operator=(parse_event&&) = default; + + bool replay(basic_json_visitor& visitor) const + { + switch (event_type) + { + case staj_event_type::begin_array: + return visitor.begin_array(tag, ser_context()); + case staj_event_type::end_array: + return visitor.end_array(ser_context()); + case staj_event_type::string_value: + return visitor.string_value(string_value, tag, ser_context()); + case staj_event_type::byte_string_value: + case staj_event_type::null_value: + return visitor.null_value(tag, ser_context()); + case staj_event_type::bool_value: + return visitor.bool_value(bool_value, tag, ser_context()); + case staj_event_type::int64_value: + return visitor.int64_value(int64_value, tag, ser_context()); + case staj_event_type::uint64_value: + return visitor.uint64_value(uint64_value, tag, ser_context()); + case staj_event_type::double_value: + return visitor.double_value(double_value, tag, ser_context()); + default: + return false; + } + } + }; + + template + class m_columns_filter : public basic_json_visitor + { + public: + using string_view_type = typename basic_json_visitor::string_view_type; + using char_type = CharT; + using temp_allocator_type = TempAllocator; + + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using string_type = std::basic_string,char_allocator_type>; + + using string_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_event_allocator_type = typename std::allocator_traits:: template rebind_alloc>; + using parse_event_vector_type = std::vector, parse_event_allocator_type>; + using parse_event_vector_allocator_type = typename std::allocator_traits:: template rebind_alloc; + private: + TempAllocator alloc_; + std::size_t name_index_; + int level_; + cached_state state_; + std::size_t column_index_; + std::size_t row_index_; + + std::vector column_names_; + std::vector cached_events_; + public: + + m_columns_filter(const TempAllocator& alloc) + : alloc_(alloc), + name_index_(0), + level_(0), + state_(cached_state::begin_object), + column_index_(0), + row_index_(0), + column_names_(alloc), + cached_events_(alloc) + { + } + + void reset() + { + name_index_ = 0; + level_ = 0; + state_ = cached_state::begin_object; + column_index_ = 0; + row_index_ = 0; + column_names_.clear(); + cached_events_.clear(); + } + + bool done() const + { + return state_ == cached_state::done; + } + + void initialize(const std::vector& column_names) + { + for (const auto& name : column_names) + { + column_names_.push_back(name); + cached_events_.emplace_back(alloc_); + } + name_index_ = 0; + level_ = 0; + column_index_ = 0; + row_index_ = 0; + state_ = cached_state::begin_object; + } + + void skip_column() + { + ++name_index_; + } + + bool replay_parse_events(basic_json_visitor& visitor) + { + bool more = true; + while (more) + { + switch (state_) + { + case cached_state::begin_object: + more = visitor.begin_object(semantic_tag::none, ser_context()); + column_index_ = 0; + state_ = cached_state::name; + break; + case cached_state::end_object: + more = visitor.end_object(ser_context()); + state_ = cached_state::done; + break; + case cached_state::name: + if (column_index_ < column_names_.size()) + { + more = visitor.key(column_names_[column_index_], ser_context()); + state_ = cached_state::begin_array; + } + else + { + state_ = cached_state::end_object; + } + break; + case cached_state::begin_array: + more = visitor.begin_array(semantic_tag::none, ser_context()); + row_index_ = 0; + state_ = cached_state::item; + break; + case cached_state::end_array: + more = visitor.end_array(ser_context()); + ++column_index_; + state_ = cached_state::name; + break; + case cached_state::item: + if (row_index_ < cached_events_[column_index_].size()) + { + more = cached_events_[column_index_][row_index_].replay(visitor); + ++row_index_; + } + else + { + state_ = cached_state::end_array; + } + break; + default: + more = false; + break; + } + } + return more; + } + + void visit_flush() override + { + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + ec = csv_errc::invalid_parse_state; + return false; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + ec = csv_errc::invalid_parse_state; + return false; + } + + bool visit_begin_array(semantic_tag tag, const ser_context&, std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(staj_event_type::begin_array, tag, alloc_); + + ++level_; + } + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + if (level_ > 0) + { + cached_events_[name_index_].emplace_back(staj_event_type::end_array, semantic_tag::none, alloc_); + ++name_index_; + --level_; + } + else + { + name_index_ = 0; + } + return true; + } + + bool visit_key(const string_view_type&, const ser_context&, std::error_code& ec) override + { + ec = csv_errc::invalid_parse_state; + return false; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(staj_event_type::null_value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_string(const string_view_type& value, semantic_tag tag, const ser_context&, std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_byte_string(const byte_string_view& value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_double(double value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_int64(int64_t value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_uint64(uint64_t value, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + + bool visit_bool(bool value, semantic_tag tag, const ser_context&, std::error_code&) override + { + if (name_index_ < column_names_.size()) + { + cached_events_[name_index_].emplace_back(value, tag, alloc_); + if (level_ == 0) + { + ++name_index_; + } + } + return true; + } + }; + +} // namespace detail + +template> +class basic_csv_parser : public ser_context +{ +public: + using string_view_type = jsoncons::basic_string_view; + using char_type = CharT; +private: + struct string_maps_to_double + { + string_view_type s; + + bool operator()(const std::pair& val) const + { + return val.first == s; + } + }; + + using temp_allocator_type = TempAllocator; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + using string_type = std::basic_string,char_allocator_type>; + typedef typename std::allocator_traits:: template rebind_alloc string_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc csv_mode_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc csv_type_info_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc> string_vector_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc csv_parse_state_allocator_type; + + static constexpr int default_depth = 3; + + temp_allocator_type alloc_; + csv_parse_state state_; + basic_json_visitor* visitor_; + std::function err_handler_; + std::size_t column_; + std::size_t line_; + int depth_; + const basic_csv_decode_options options_; + std::size_t column_index_; + std::size_t level_; + std::size_t offset_; + jsoncons::detail::chars_to to_double_; + const CharT* begin_input_; + const CharT* input_end_; + const CharT* input_ptr_; + bool more_; + std::size_t header_line_; + + detail::m_columns_filter m_columns_filter_; + std::vector stack_; + std::vector column_names_; + std::vector column_types_; + std::vector column_defaults_; + std::vector state_stack_; + string_type buffer_; + std::vector> string_double_map_; + +public: + basic_csv_parser(const TempAllocator& alloc = TempAllocator()) + : basic_csv_parser(basic_csv_decode_options(), + default_csv_parsing(), + alloc) + { + } + + basic_csv_parser(const basic_csv_decode_options& options, + const TempAllocator& alloc = TempAllocator()) + : basic_csv_parser(options, + default_csv_parsing(), + alloc) + { + } + + basic_csv_parser(std::function err_handler, + const TempAllocator& alloc = TempAllocator()) + : basic_csv_parser(basic_csv_decode_options(), + err_handler, + alloc) + { + } + + basic_csv_parser(const basic_csv_decode_options& options, + std::function err_handler, + const TempAllocator& alloc = TempAllocator()) + : alloc_(alloc), + state_(csv_parse_state::start), + visitor_(nullptr), + err_handler_(err_handler), + column_(1), + line_(1), + depth_(default_depth), + options_(options), + column_index_(0), + level_(0), + offset_(0), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + more_(true), + header_line_(1), + m_columns_filter_(alloc), + stack_(alloc), + column_names_(alloc), + column_types_(alloc), + column_defaults_(alloc), + state_stack_(alloc), + buffer_(alloc) + { + if (options_.enable_str_to_nan()) + { + string_double_map_.emplace_back(options_.nan_to_str(),std::nan("")); + } + if (options_.enable_str_to_inf()) + { + string_double_map_.emplace_back(options_.inf_to_str(),std::numeric_limits::infinity()); + } + if (options_.enable_str_to_neginf()) + { + string_double_map_.emplace_back(options_.neginf_to_str(),-std::numeric_limits::infinity()); + } + + initialize(); + } + + ~basic_csv_parser() noexcept + { + } + + bool done() const + { + return state_ == csv_parse_state::done; + } + + bool accept() const + { + return state_ == csv_parse_state::accept || state_ == csv_parse_state::done; + } + + bool stopped() const + { + return !more_; + } + + bool source_exhausted() const + { + return input_ptr_ == input_end_; + } + + const std::vector& column_labels() const + { + return column_names_; + } + + void reinitialize() + { + state_ = csv_parse_state::start; + visitor_ = nullptr; + column_ = 1; + line_ = 1; + depth_ = default_depth; + column_index_ = 0; + level_ = 0; + offset_ = 0; + begin_input_ = nullptr; + input_end_ = nullptr; + input_ptr_ = nullptr; + more_ = true; + header_line_ = 1; + m_columns_filter_.reset(); + stack_.clear(); + column_names_.clear(); + column_types_.clear(); + column_defaults_.clear(); + state_stack_.clear(); + buffer_.clear(); + + initialize(); + } + + void restart() + { + more_ = true; + } + + void parse_some(basic_json_visitor& visitor) + { + std::error_code ec; + parse_some(visitor,ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column_)); + } + } + + void parse_some(basic_json_visitor& visitor, std::error_code& ec) + { + switch (options_.mapping_kind()) + { + case csv_mapping_kind::m_columns: + visitor_ = &m_columns_filter_; + break; + default: + visitor_ = std::addressof(visitor); + break; + } + + const CharT* local_input_end = input_end_; + + if (input_ptr_ == local_input_end && more_) + { + switch (state_) + { + case csv_parse_state::start: + ec = csv_errc::source_error; + more_ = false; + return; + case csv_parse_state::before_unquoted_field: + case csv_parse_state::before_last_unquoted_field: + end_unquoted_string_value(ec); + state_ = csv_parse_state::before_last_unquoted_field_tail; + break; + case csv_parse_state::before_last_unquoted_field_tail: + if (stack_.back() == csv_mode::subfields) + { + stack_.pop_back(); + more_ = visitor_->end_array(*this, ec); + } + ++column_index_; + state_ = csv_parse_state::end_record; + break; + case csv_parse_state::before_unquoted_string: + buffer_.clear(); + JSONCONS_FALLTHROUGH; + case csv_parse_state::unquoted_string: + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + if (options_.ignore_empty_values() && buffer_.empty()) + { + state_ = csv_parse_state::end_record; + } + else + { + before_value(ec); + state_ = csv_parse_state::before_unquoted_field; + } + break; + case csv_parse_state::before_last_quoted_field: + end_quoted_string_value(ec); + ++column_index_; + state_ = csv_parse_state::end_record; + break; + case csv_parse_state::escaped_value: + if (options_.quote_escape_char() == options_.quote_char()) + { + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + before_value(ec); + ++column_; + state_ = csv_parse_state::before_last_quoted_field; + } + else + { + state_ = csv_parse_state::end_record; + } + } + else + { + ec = csv_errc::invalid_escaped_char; + more_ = false; + return; + } + break; + case csv_parse_state::end_record: + if (column_index_ > 0) + { + after_record(ec); + } + state_ = csv_parse_state::no_more_records; + break; + case csv_parse_state::no_more_records: + switch (stack_.back()) + { + case csv_mode::header: + stack_.pop_back(); + break; + case csv_mode::data: + stack_.pop_back(); + break; + default: + break; + } + more_ = visitor_->end_array(*this, ec); + if (options_.mapping_kind() == csv_mapping_kind::m_columns) + { + if (!m_columns_filter_.done()) + { + more_ = m_columns_filter_.replay_parse_events(visitor); + } + else + { + state_ = csv_parse_state::accept; + } + } + else + { + state_ = csv_parse_state::accept; + } + break; + case csv_parse_state::accept: + if (!(stack_.size() == 1 && stack_.back() == csv_mode::initial)) + { + err_handler_(csv_errc::unexpected_eof, *this); + ec = csv_errc::unexpected_eof; + more_ = false; + return; + } + stack_.pop_back(); + visitor_->flush(); + state_ = csv_parse_state::done; + more_ = false; + return; + default: + state_ = csv_parse_state::end_record; + break; + } + } + + for (; (input_ptr_ < local_input_end) && more_;) + { + CharT curr_char = *input_ptr_; + + switch (state_) + { + case csv_parse_state::cr: + ++line_; + column_ = 1; + switch (*input_ptr_) + { + case '\n': + ++input_ptr_; + state_ = pop_state(); + break; + default: + state_ = pop_state(); + break; + } + break; + case csv_parse_state::start: + if (options_.mapping_kind() != csv_mapping_kind::m_columns) + { + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + } + if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows && options_.column_names().size() > 0) + { + column_index_ = 0; + state_ = csv_parse_state::column_labels; + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + state_ = csv_parse_state::expect_comment_or_record; + } + else + { + state_ = csv_parse_state::expect_comment_or_record; + } + break; + case csv_parse_state::column_labels: + if (column_index_ < column_names_.size()) + { + more_ = visitor_->string_value(column_names_[column_index_], semantic_tag::none, *this, ec); + ++column_index_; + } + else + { + more_ = visitor_->end_array(*this, ec); + state_ = csv_parse_state::expect_comment_or_record; + //stack_.back() = csv_mode::data; + column_index_ = 0; + } + break; + case csv_parse_state::comment: + switch (curr_char) + { + case '\n': + { + ++line_; + if (stack_.back() == csv_mode::header) + { + ++header_line_; + } + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + break; + } + case '\r': + ++line_; + if (stack_.back() == csv_mode::header) + { + ++header_line_; + } + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + push_state(state_); + state_ = csv_parse_state::cr; + break; + default: + ++column_; + break; + } + ++input_ptr_; + break; + + case csv_parse_state::expect_comment_or_record: + buffer_.clear(); + if (curr_char == options_.comment_starter()) + { + state_ = csv_parse_state::comment; + ++column_; + ++input_ptr_; + } + else + { + state_ = csv_parse_state::expect_record; + } + break; + case csv_parse_state::quoted_string: + { + if (curr_char == options_.quote_escape_char()) + { + state_ = csv_parse_state::escaped_value; + } + else if (curr_char == options_.quote_char()) + { + state_ = csv_parse_state::between_values; + } + else + { + buffer_.push_back(static_cast(curr_char)); + } + } + ++column_; + ++input_ptr_; + break; + case csv_parse_state::escaped_value: + { + if (curr_char == options_.quote_char()) + { + buffer_.push_back(static_cast(curr_char)); + state_ = csv_parse_state::quoted_string; + ++column_; + ++input_ptr_; + } + else if (options_.quote_escape_char() == options_.quote_char()) + { + state_ = csv_parse_state::between_values; + } + else + { + ec = csv_errc::invalid_escaped_char; + more_ = false; + return; + } + } + break; + case csv_parse_state::between_values: + switch (curr_char) + { + case '\r': + case '\n': + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + before_value(ec); + state_ = csv_parse_state::before_last_quoted_field; + } + else + { + state_ = csv_parse_state::end_record; + } + break; + } + default: + if (curr_char == options_.field_delimiter()) + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + before_value(ec); + state_ = csv_parse_state::before_quoted_field; + } + else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter()) + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + before_value(ec); + state_ = csv_parse_state::before_quoted_subfield; + } + else if (curr_char == ' ' || curr_char == '\t') + { + ++column_; + ++input_ptr_; + } + else + { + ec = csv_errc::unexpected_char_between_fields; + more_ = false; + return; + } + break; + } + break; + case csv_parse_state::before_unquoted_string: + { + buffer_.clear(); + state_ = csv_parse_state::unquoted_string; + break; + } + case csv_parse_state::before_unquoted_field: + end_unquoted_string_value(ec); + state_ = csv_parse_state::before_unquoted_field_tail; + break; + case csv_parse_state::before_unquoted_field_tail: + { + if (stack_.back() == csv_mode::subfields) + { + stack_.pop_back(); + more_ = visitor_->end_array(*this, ec); + } + ++column_index_; + state_ = csv_parse_state::before_unquoted_string; + ++column_; + ++input_ptr_; + break; + } + case csv_parse_state::before_unquoted_field_tail1: + { + if (stack_.back() == csv_mode::subfields) + { + stack_.pop_back(); + more_ = visitor_->end_array(*this, ec); + } + state_ = csv_parse_state::end_record; + ++column_; + ++input_ptr_; + break; + } + + case csv_parse_state::before_last_unquoted_field: + end_unquoted_string_value(ec); + state_ = csv_parse_state::before_last_unquoted_field_tail; + break; + + case csv_parse_state::before_last_unquoted_field_tail: + if (stack_.back() == csv_mode::subfields) + { + stack_.pop_back(); + more_ = visitor_->end_array(*this, ec); + } + ++column_index_; + state_ = csv_parse_state::end_record; + break; + + case csv_parse_state::before_unquoted_subfield: + if (stack_.back() == csv_mode::data) + { + stack_.push_back(csv_mode::subfields); + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + } + state_ = csv_parse_state::before_unquoted_subfield_tail; + break; + case csv_parse_state::before_unquoted_subfield_tail: + end_unquoted_string_value(ec); + state_ = csv_parse_state::before_unquoted_string; + ++column_; + ++input_ptr_; + break; + case csv_parse_state::before_quoted_field: + end_quoted_string_value(ec); + state_ = csv_parse_state::before_unquoted_field_tail; // return to unquoted + break; + case csv_parse_state::before_quoted_subfield: + if (stack_.back() == csv_mode::data) + { + stack_.push_back(csv_mode::subfields); + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + } + state_ = csv_parse_state::before_quoted_subfield_tail; + break; + case csv_parse_state::before_quoted_subfield_tail: + end_quoted_string_value(ec); + state_ = csv_parse_state::before_unquoted_string; + ++column_; + ++input_ptr_; + break; + case csv_parse_state::before_last_quoted_field: + end_quoted_string_value(ec); + state_ = csv_parse_state::before_last_quoted_field_tail; + break; + case csv_parse_state::before_last_quoted_field_tail: + if (stack_.back() == csv_mode::subfields) + { + stack_.pop_back(); + more_ = visitor_->end_array(*this, ec); + } + ++column_index_; + state_ = csv_parse_state::end_record; + break; + case csv_parse_state::unquoted_string: + { + switch (curr_char) + { + case '\n': + case '\r': + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + before_value(ec); + state_ = csv_parse_state::before_last_unquoted_field; + } + else + { + state_ = csv_parse_state::end_record; + } + break; + } + default: + if (curr_char == options_.field_delimiter()) + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + before_value(ec); + state_ = csv_parse_state::before_unquoted_field; + } + else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter()) + { + if (options_.trim_leading() || options_.trim_trailing()) + { + trim_string_buffer(options_.trim_leading(),options_.trim_trailing()); + } + before_value(ec); + state_ = csv_parse_state::before_unquoted_subfield; + } + else if (curr_char == options_.quote_char()) + { + buffer_.clear(); + state_ = csv_parse_state::quoted_string; + ++column_; + ++input_ptr_; + } + else + { + buffer_.push_back(static_cast(curr_char)); + ++column_; + ++input_ptr_; + } + break; + } + break; + } + case csv_parse_state::expect_record: + { + switch (curr_char) + { + case '\n': + { + if (!options_.ignore_empty_lines()) + { + before_record(ec); + state_ = csv_parse_state::end_record; + } + else + { + ++line_; + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + ++input_ptr_; + } + break; + } + case '\r': + if (!options_.ignore_empty_lines()) + { + before_record(ec); + state_ = csv_parse_state::end_record; + } + else + { + ++line_; + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + ++input_ptr_; + push_state(state_); + state_ = csv_parse_state::cr; + } + break; + case ' ': + case '\t': + if (!options_.trim_leading()) + { + buffer_.push_back(static_cast(curr_char)); + before_record(ec); + state_ = csv_parse_state::unquoted_string; + } + ++column_; + ++input_ptr_; + break; + default: + before_record(ec); + if (curr_char == options_.quote_char()) + { + buffer_.clear(); + state_ = csv_parse_state::quoted_string; + ++column_; + ++input_ptr_; + } + else + { + state_ = csv_parse_state::unquoted_string; + } + break; + } + break; + } + case csv_parse_state::end_record: + { + switch (curr_char) + { + case '\n': + { + ++line_; + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + after_record(ec); + ++input_ptr_; + break; + } + case '\r': + ++line_; + column_ = 1; + state_ = csv_parse_state::expect_comment_or_record; + after_record(ec); + push_state(state_); + state_ = csv_parse_state::cr; + ++input_ptr_; + break; + case ' ': + case '\t': + ++column_; + ++input_ptr_; + break; + default: + err_handler_(csv_errc::syntax_error, *this); + ec = csv_errc::syntax_error; + more_ = false; + return; + } + break; + } + default: + err_handler_(csv_errc::invalid_parse_state, *this); + ec = csv_errc::invalid_parse_state; + more_ = false; + return; + } + if (line_ > options_.max_lines()) + { + state_ = csv_parse_state::done; + more_ = false; + } + } + } + + void finish_parse() + { + std::error_code ec; + finish_parse(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line_,column_)); + } + } + + void finish_parse(std::error_code& ec) + { + while (more_) + { + parse_some(ec); + } + } + + csv_parse_state state() const + { + return state_; + } + + void update(const string_view_type sv) + { + update(sv.data(),sv.length()); + } + + void update(const CharT* data, std::size_t length) + { + begin_input_ = data; + input_end_ = data + length; + input_ptr_ = begin_input_; + } + + std::size_t line() const override + { + return line_; + } + + std::size_t column() const override + { + return column_; + } + +private: + void initialize() + { + jsoncons::csv::detail::parse_column_names(options_.column_names(), column_names_); + jsoncons::csv::detail::parse_column_types(options_.column_types(), column_types_); + jsoncons::csv::detail::parse_column_names(options_.column_defaults(), column_defaults_); + + stack_.reserve(default_depth); + stack_.push_back(csv_mode::initial); + stack_.push_back((options_.header_lines() > 0) ? csv_mode::header + : csv_mode::data); + } + + // name + void before_value(std::error_code& ec) + { + switch (stack_.back()) + { + case csv_mode::header: + if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes()) + { + trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes()); + } + if (line_ == header_line_) + { + column_names_.push_back(buffer_); + if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows) + { + more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec); + } + } + break; + case csv_mode::data: + if (options_.mapping_kind() == csv_mapping_kind::n_objects) + { + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + if (column_index_ < column_names_.size() + offset_) + { + more_ = visitor_->key(column_names_[column_index_ - offset_], *this, ec); + } + } + } + break; + default: + break; + } + } + + // begin_array or begin_record + void before_record(std::error_code& ec) + { + offset_ = 0; + + switch (stack_.back()) + { + case csv_mode::header: + if (options_.assume_header() && line_ == header_line_) + { + if (options_.mapping_kind() == csv_mapping_kind::n_rows) + { + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + } + } + break; + case csv_mode::data: + switch (options_.mapping_kind()) + { + case csv_mapping_kind::n_rows: + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + break; + case csv_mapping_kind::n_objects: + more_ = visitor_->begin_object(semantic_tag::none, *this, ec); + break; + case csv_mapping_kind::m_columns: + break; + default: + break; + } + break; + default: + break; + } + } + + // end_array, begin_array, string_value (headers) + void after_record(std::error_code& ec) + { + if (column_types_.size() > 0) + { + if (level_ > 0) + { + more_ = visitor_->end_array(*this, ec); + level_ = 0; + } + } + switch (stack_.back()) + { + case csv_mode::header: + if (line_ >= options_.header_lines()) + { + stack_.back() = csv_mode::data; + } + switch (options_.mapping_kind()) + { + case csv_mapping_kind::n_rows: + if (options_.assume_header()) + { + more_ = visitor_->end_array(*this, ec); + } + break; + case csv_mapping_kind::m_columns: + m_columns_filter_.initialize(column_names_); + break; + default: + break; + } + break; + case csv_mode::data: + case csv_mode::subfields: + { + switch (options_.mapping_kind()) + { + case csv_mapping_kind::n_rows: + more_ = visitor_->end_array(*this, ec); + break; + case csv_mapping_kind::n_objects: + more_ = visitor_->end_object(*this, ec); + break; + case csv_mapping_kind::m_columns: + more_ = visitor_->end_array(*this, ec); + break; + } + break; + } + default: + break; + } + column_index_ = 0; + } + + void trim_string_buffer(bool trim_leading, bool trim_trailing) + { + std::size_t start = 0; + std::size_t length = buffer_.length(); + if (trim_leading) + { + bool done = false; + while (!done && start < buffer_.length()) + { + if ((buffer_[start] < 256) && std::isspace(buffer_[start])) + { + ++start; + } + else + { + done = true; + } + } + } + if (trim_trailing) + { + bool done = false; + while (!done && length > 0) + { + if ((buffer_[length-1] < 256) && std::isspace(buffer_[length-1])) + { + --length; + } + else + { + done = true; + } + } + } + if (start != 0 || length != buffer_.size()) + { + buffer_ = buffer_.substr(start,length-start); + } + } + + /* + end_array, begin_array, xxx_value (end_value) + */ + void end_unquoted_string_value(std::error_code& ec) + { + switch (stack_.back()) + { + case csv_mode::data: + case csv_mode::subfields: + switch (options_.mapping_kind()) + { + case csv_mapping_kind::n_rows: + if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0) + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + else + { + end_value(options_.infer_types(), ec); + } + break; + case csv_mapping_kind::n_objects: + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + if (column_index_ < column_names_.size() + offset_) + { + if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0) + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + else + { + end_value(options_.infer_types(), ec); + } + } + else if (level_ > 0) + { + if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0) + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + else + { + end_value(options_.infer_types(), ec); + } + } + } + break; + case csv_mapping_kind::m_columns: + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + end_value(options_.infer_types(), ec); + } + else + { + m_columns_filter_.skip_column(); + } + break; + } + break; + default: + break; + } + } + + void end_quoted_string_value(std::error_code& ec) + { + switch (stack_.back()) + { + case csv_mode::data: + case csv_mode::subfields: + if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes()) + { + trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes()); + } + switch (options_.mapping_kind()) + { + case csv_mapping_kind::n_rows: + end_value(false, ec); + break; + case csv_mapping_kind::n_objects: + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + if (column_index_ < column_names_.size() + offset_) + { + if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0) + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + else + { + end_value(false, ec); + } + } + else if (level_ > 0) + { + if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0) + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + else + { + end_value(false, ec); + } + } + } + break; + case csv_mapping_kind::m_columns: + if (!(options_.ignore_empty_values() && buffer_.empty())) + { + end_value(false, ec); + } + else + { + m_columns_filter_.skip_column(); + } + break; + } + break; + default: + break; + } + } + + void end_value(bool infer_types, std::error_code& ec) + { + auto it = std::find_if(string_double_map_.begin(), string_double_map_.end(), string_maps_to_double{ buffer_ }); + if (it != string_double_map_.end()) + { + more_ = visitor_->double_value(it->second, semantic_tag::none, *this, ec); + } + else if (column_index_ < column_types_.size() + offset_) + { + if (column_types_[column_index_ - offset_].col_type == csv_column_type::repeat_t) + { + offset_ = offset_ + column_types_[column_index_ - offset_].rep_count; + if (column_index_ - offset_ + 1 < column_types_.size()) + { + if (column_index_ == offset_ || level_ > column_types_[column_index_-offset_].level) + { + more_ = visitor_->end_array(*this, ec); + } + level_ = column_index_ == offset_ ? 0 : column_types_[column_index_ - offset_].level; + } + } + if (level_ < column_types_[column_index_ - offset_].level) + { + more_ = visitor_->begin_array(semantic_tag::none, *this, ec); + level_ = column_types_[column_index_ - offset_].level; + } + else if (level_ > column_types_[column_index_ - offset_].level) + { + more_ = visitor_->end_array(*this, ec); + level_ = column_types_[column_index_ - offset_].level; + } + switch (column_types_[column_index_ - offset_].col_type) + { + case csv_column_type::integer_t: + { + std::basic_istringstream,char_allocator_type> iss{buffer_}; + int64_t val; + iss >> val; + if (!iss.fail()) + { + more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec); + } + else + { + if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0) + { + basic_json_parser parser(alloc_); + parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length()); + parser.parse_some(*visitor_); + parser.finish_parse(*visitor_); + } + else + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + } + } + break; + case csv_column_type::float_t: + { + if (options_.lossless_number()) + { + more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec); + } + else + { + std::basic_istringstream, char_allocator_type> iss{ buffer_ }; + double val; + iss >> val; + if (!iss.fail()) + { + more_ = visitor_->double_value(val, semantic_tag::none, *this, ec); + } + else + { + if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0) + { + basic_json_parser parser(alloc_); + parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length()); + parser.parse_some(*visitor_); + parser.finish_parse(*visitor_); + } + else + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + } + } + } + break; + case csv_column_type::boolean_t: + { + if (buffer_.length() == 1 && buffer_[0] == '0') + { + more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec); + } + else if (buffer_.length() == 1 && buffer_[0] == '1') + { + more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec); + } + else if (buffer_.length() == 5 && ((buffer_[0] == 'f' || buffer_[0] == 'F') && (buffer_[1] == 'a' || buffer_[1] == 'A') && (buffer_[2] == 'l' || buffer_[2] == 'L') && (buffer_[3] == 's' || buffer_[3] == 'S') && (buffer_[4] == 'e' || buffer_[4] == 'E'))) + { + more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec); + } + else if (buffer_.length() == 4 && ((buffer_[0] == 't' || buffer_[0] == 'T') && (buffer_[1] == 'r' || buffer_[1] == 'R') && (buffer_[2] == 'u' || buffer_[2] == 'U') && (buffer_[3] == 'e' || buffer_[3] == 'E'))) + { + more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec); + } + else + { + if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0) + { + basic_json_parser parser(alloc_); + parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length()); + parser.parse_some(*visitor_); + parser.finish_parse(*visitor_); + } + else + { + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + } + } + } + break; + default: + if (buffer_.length() > 0) + { + more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec); + } + else + { + if (column_index_ < column_defaults_.size() + offset_ && column_defaults_[column_index_ - offset_].length() > 0) + { + basic_json_parser parser(alloc_); + parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length()); + parser.parse_some(*visitor_); + parser.finish_parse(*visitor_); + } + else + { + more_ = visitor_->string_value(string_view_type(), semantic_tag::none, *this, ec); + } + } + break; + } + } + else + { + if (infer_types) + { + end_value_with_numeric_check(ec); + } + else + { + more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec); + } + } + } + + enum class numeric_check_state + { + initial, + null, + boolean_true, + boolean_false, + minus, + zero, + integer, + fraction1, + fraction, + exp1, + exp, + not_a_number + }; + + /* + xxx_value + */ + void end_value_with_numeric_check(std::error_code& ec) + { + numeric_check_state state = numeric_check_state::initial; + bool is_negative = false; + int precision = 0; + uint8_t decimal_places = 0; + + auto last = buffer_.end(); + + std::string buffer; + for (auto p = buffer_.begin(); state != numeric_check_state::not_a_number && p != last; ++p) + { + switch (state) + { + case numeric_check_state::initial: + { + switch (*p) + { + case 'n':case 'N': + if ((last-p) == 4 && (p[1] == 'u' || p[1] == 'U') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 'l' || p[3] == 'L')) + { + state = numeric_check_state::null; + } + else + { + state = numeric_check_state::not_a_number; + } + break; + case 't':case 'T': + if ((last-p) == 4 && (p[1] == 'r' || p[1] == 'R') && (p[2] == 'u' || p[2] == 'U') && (p[3] == 'e' || p[3] == 'U')) + { + state = numeric_check_state::boolean_true; + } + else + { + state = numeric_check_state::not_a_number; + } + break; + case 'f':case 'F': + if ((last-p) == 5 && (p[1] == 'a' || p[1] == 'A') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 's' || p[3] == 'S') && (p[4] == 'e' || p[4] == 'E')) + { + state = numeric_check_state::boolean_false; + } + else + { + state = numeric_check_state::not_a_number; + } + break; + case '-': + is_negative = true; + buffer.push_back(*p); + state = numeric_check_state::minus; + break; + case '0': + ++precision; + buffer.push_back(*p); + state = numeric_check_state::zero; + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ++precision; + buffer.push_back(*p); + state = numeric_check_state::integer; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::zero: + { + switch (*p) + { + case '.': + buffer.push_back(to_double_.get_decimal_point()); + state = numeric_check_state::fraction1; + break; + case 'e':case 'E': + buffer.push_back(*p); + state = numeric_check_state::exp1; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::integer: + { + switch (*p) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ++precision; + buffer.push_back(*p); + break; + case '.': + buffer.push_back(to_double_.get_decimal_point()); + state = numeric_check_state::fraction1; + break; + case 'e':case 'E': + buffer.push_back(*p); + state = numeric_check_state::exp1; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::minus: + { + switch (*p) + { + case '0': + ++precision; + buffer.push_back(*p); + state = numeric_check_state::zero; + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ++precision; + buffer.push_back(*p); + state = numeric_check_state::integer; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::fraction1: + { + switch (*p) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ++precision; + ++decimal_places; + buffer.push_back(*p); + state = numeric_check_state::fraction; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::fraction: + { + switch (*p) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ++precision; + ++decimal_places; + buffer.push_back(*p); + break; + case 'e':case 'E': + buffer.push_back(*p); + state = numeric_check_state::exp1; + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::exp1: + { + switch (*p) + { + case '-': + buffer.push_back(*p); + break; + case '+': + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state = numeric_check_state::exp; + buffer.push_back(*p); + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + case numeric_check_state::exp: + { + switch (*p) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.push_back(*p); + break; + default: + state = numeric_check_state::not_a_number; + break; + } + break; + } + default: + break; + } + } + + switch (state) + { + case numeric_check_state::null: + more_ = visitor_->null_value(semantic_tag::none, *this, ec); + break; + case numeric_check_state::boolean_true: + more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec); + break; + case numeric_check_state::boolean_false: + more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec); + break; + case numeric_check_state::zero: + case numeric_check_state::integer: + { + if (is_negative) + { + int64_t val{ 0 }; + auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val); + if (result) + { + more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec); + } + else // Must be overflow + { + more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec); + } + } + else + { + uint64_t val{ 0 }; + auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val); + if (result) + { + more_ = visitor_->uint64_value(val, semantic_tag::none, *this, ec); + } + else if (result.ec == jsoncons::detail::to_integer_errc::overflow) + { + more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec); + } + else + { + ec = result.ec; + more_ = false; + return; + } + } + break; + } + case numeric_check_state::fraction: + case numeric_check_state::exp: + { + if (options_.lossless_number()) + { + more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec); + } + else + { + double d = to_double_(buffer.c_str(), buffer.length()); + more_ = visitor_->double_value(d, semantic_tag::none, *this, ec); + } + break; + } + default: + { + more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec); + break; + } + } + } + + void push_state(csv_parse_state state) + { + state_stack_.push_back(state); + } + + csv_parse_state pop_state() + { + JSONCONS_ASSERT(!state_stack_.empty()) + csv_parse_state state = state_stack_.back(); + state_stack_.pop_back(); + return state; + } +}; + +using csv_parser = basic_csv_parser; +using wcsv_parser = basic_csv_parser; + +}} + +#endif + diff --git a/include/jsoncons_ext/csv/csv_reader.hpp b/include/jsoncons_ext/csv/csv_reader.hpp new file mode 100644 index 0000000..f10211a --- /dev/null +++ b/include/jsoncons_ext/csv/csv_reader.hpp @@ -0,0 +1,348 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_READER_HPP +#define JSONCONS_CSV_CSV_READER_HPP + +#include +#include +#include +#include // std::allocator +#include // std::move +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace csv { + + template,class Allocator=std::allocator> + class basic_csv_reader + { + struct stack_item + { + stack_item() noexcept + : array_begun_(false) + { + } + + bool array_begun_; + }; + using char_type = CharT; + using temp_allocator_type = Allocator; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + basic_csv_reader(const basic_csv_reader&) = delete; + basic_csv_reader& operator = (const basic_csv_reader&) = delete; + + basic_default_json_visitor default_visitor_; + text_source_adaptor source_; + basic_json_visitor& visitor_; + basic_csv_parser parser_; + + public: + // Structural characters + static constexpr size_t default_max_buffer_size = 16384; + //! Parse an input stream of CSV text into a json object + /*! + \param is The input stream to read from + */ + + template + basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) + + : basic_csv_reader(std::forward(source), + visitor, + basic_csv_decode_options(), + default_csv_parsing(), + alloc) + { + } + + template + basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + const Allocator& alloc = Allocator()) + + : basic_csv_reader(std::forward(source), + visitor, + options, + default_csv_parsing(), + alloc) + { + } + + template + basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + std::function err_handler, + const Allocator& alloc = Allocator()) + : basic_csv_reader(std::forward(source), + visitor, + basic_csv_decode_options(), + err_handler, + alloc) + { + } + + template + basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator()) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options, err_handler, alloc) + + { + } + + ~basic_csv_reader() noexcept = default; + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read(std::error_code& ec) + { + read_internal(ec); + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + private: + + void read_internal(std::error_code& ec) + { + if (source_.is_error()) + { + ec = csv_errc::source_error; + return; + } + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + parser_.parse_some(visitor_, ec); + if (ec) return; + } + } + }; + + template,class Allocator=std::allocator> + class legacy_basic_csv_reader + { + struct stack_item + { + stack_item() noexcept + : array_begun_(false) + { + } + + bool array_begun_; + }; + using char_type = CharT; + using temp_allocator_type = Allocator; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + legacy_basic_csv_reader(const legacy_basic_csv_reader&) = delete; + legacy_basic_csv_reader& operator = (const legacy_basic_csv_reader&) = delete; + + basic_default_json_visitor default_visitor_; + text_source_adaptor source_; + basic_json_visitor& visitor_; + basic_csv_parser parser_; + + public: + // Structural characters + static constexpr size_t default_max_buffer_size = 16384; + //! Parse an input stream of CSV text into a json object + /*! + \param is The input stream to read from + */ + + template + legacy_basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) + + : legacy_basic_csv_reader(std::forward(source), + visitor, + basic_csv_decode_options(), + default_csv_parsing(), + alloc) + { + } + + template + legacy_basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + const Allocator& alloc = Allocator()) + + : legacy_basic_csv_reader(std::forward(source), + visitor, + options, + default_csv_parsing(), + alloc) + { + } + + template + legacy_basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + std::function err_handler, + const Allocator& alloc = Allocator()) + : legacy_basic_csv_reader(std::forward(source), + visitor, + basic_csv_decode_options(), + err_handler, + alloc) + { + } + + template + legacy_basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options, err_handler, alloc) + { + } + + template + legacy_basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + visitor_(visitor), + parser_(options, err_handler, alloc) + { + jsoncons::basic_string_view sv(std::forward(source)); + auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size()); + if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) + { + JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column())); + } + std::size_t offset = (r.ptr - sv.data()); + parser_.update(sv.data()+offset,sv.size()-offset); + } + + ~legacy_basic_csv_reader() noexcept = default; + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read(std::error_code& ec) + { + read_internal(ec); + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } + + bool eof() const + { + return parser_.source_exhausted() && source_.eof(); + } + + private: + + void read_internal(std::error_code& ec) + { + if (source_.is_error()) + { + ec = csv_errc::source_error; + return; + } + while (!parser_.stopped()) + { + if (parser_.source_exhausted()) + { + auto s = source_.read_buffer(ec); + if (ec) return; + if (s.size() > 0) + { + parser_.update(s.data(),s.size()); + } + } + parser_.parse_some(visitor_, ec); + if (ec) return; + } + } + }; + +#if !defined(JSONCONS_NO_DEPRECATED) + using csv_reader = legacy_basic_csv_reader; + using wcsv_reader = legacy_basic_csv_reader; +#endif + + using csv_string_reader = basic_csv_reader>; + using wcsv_string_reader = basic_csv_reader>; + using csv_stream_reader = basic_csv_reader>; + using wcsv_stream_reader = basic_csv_reader>; + +}} + +#endif diff --git a/include/jsoncons_ext/csv/csv_serializer.hpp b/include/jsoncons_ext/csv/csv_serializer.hpp new file mode 100644 index 0000000..ec73510 --- /dev/null +++ b/include/jsoncons_ext/csv/csv_serializer.hpp @@ -0,0 +1,12 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_CSV_SERIALIZER_HPP +#define JSONCONS_CSV_CSV_SERIALIZER_HPP + +#include + +#endif diff --git a/include/jsoncons_ext/csv/decode_csv.hpp b/include/jsoncons_ext/csv/decode_csv.hpp new file mode 100644 index 0000000..b91c58b --- /dev/null +++ b/include/jsoncons_ext/csv/decode_csv.hpp @@ -0,0 +1,208 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_DECODE_CSV_HPP +#define JSONCONS_CSV_DECODE_CSV_HPP + +#include +#include +#include +#include + +namespace jsoncons { +namespace csv { + + template + typename std::enable_if::value && + type_traits::is_sequence_of::value,T>::type + decode_csv(const Source& s, const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = typename Source::value_type; + + json_decoder decoder; + + basic_csv_reader> reader(s,decoder,options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_char_sequence::value,T>::type + decode_csv(const Source& s, const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = typename Source::value_type; + + basic_csv_cursor cursor(s, options); + jsoncons::json_decoder> decoder; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = CharT; + + json_decoder decoder; + + basic_csv_reader> reader(is,decoder,options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) + { + basic_csv_cursor cursor(is, options); + jsoncons::json_decoder> decoder; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_csv(InputIt first, InputIt last, + const basic_csv_decode_options::value_type>& options = + basic_csv_decode_options::value_type>()) + { + using char_type = typename std::iterator_traits::value_type; + + jsoncons::json_decoder decoder; + basic_csv_reader> reader(iterator_source(first,last), decoder, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_csv(InputIt first, InputIt last, + const basic_csv_decode_options::value_type>& options = + basic_csv_decode_options::value_type>()) + { + using char_type = typename std::iterator_traits::value_type; + + basic_csv_cursor> cursor(iterator_source(first, last), options); + jsoncons::json_decoder> decoder; + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_sequence_of::value,T>::type + decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& s, + const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = typename Source::value_type; + + json_decoder decoder(temp_alloc); + + basic_csv_reader,TempAllocator> reader(s,decoder,options,temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_char_sequence::value,T>::type + decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& s, + const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = typename Source::value_type; + + basic_csv_cursor,TempAllocator> cursor(s, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::basic_istream& is, + const basic_csv_decode_options& options = basic_csv_decode_options()) + { + using char_type = CharT; + + json_decoder decoder(temp_alloc); + + basic_csv_reader,TempAllocator> reader(is,decoder,options,temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::basic_istream& is, + const basic_csv_decode_options& options = basic_csv_decode_options()) + { + basic_csv_cursor,TempAllocator> cursor(is, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // namespace csv +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/csv/encode_csv.hpp b/include/jsoncons_ext/csv/encode_csv.hpp new file mode 100644 index 0000000..d919253 --- /dev/null +++ b/include/jsoncons_ext/csv/encode_csv.hpp @@ -0,0 +1,122 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_CSV_ENCODE_CSV_HPP +#define JSONCONS_CSV_ENCODE_CSV_HPP + +#include +#include +#include + +namespace jsoncons { +namespace csv { + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_csv(const T& j, Container& s, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = typename Container::value_type; + basic_csv_encoder>> encoder(s,options); + j.dump(encoder); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_csv(const T& val, Container& s, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = typename Container::value_type; + basic_csv_encoder>> encoder(s,options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_csv(const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = CharT; + basic_csv_encoder> encoder(os,options); + j.dump(encoder); + } + + template + typename std::enable_if::value,void>::type + encode_csv(const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = CharT; + basic_csv_encoder> encoder(os,options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // with temp_allocator_arg_t + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, Container& s, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = typename Container::value_type; + basic_csv_encoder>,TempAllocator> encoder(s, options, temp_alloc); + j.dump(encoder); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_char_container::value>::type + encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, Container& s, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = typename Container::value_type; + basic_csv_encoder>,TempAllocator> encoder(s, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = CharT; + basic_csv_encoder,TempAllocator> encoder(os, options, temp_alloc); + j.dump(encoder); + } + + template + typename std::enable_if::value,void>::type + encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) + { + using char_type = CharT; + basic_csv_encoder,TempAllocator> encoder(os, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // namespace csv +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jmespath/jmespath.hpp b/include/jsoncons_ext/jmespath/jmespath.hpp new file mode 100644 index 0000000..69458cd --- /dev/null +++ b/include/jsoncons_ext/jmespath/jmespath.hpp @@ -0,0 +1,5215 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JMESPATH_JMESPATH_HPP +#define JSONCONS_JMESPATH_JMESPATH_HPP + +#include +#include +#include // std::unordered_map +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include // +#include // std::stable_sort, std::reverse +#include // std::abs +#include +#include + +namespace jsoncons { +namespace jmespath { + + enum class operator_kind + { + default_op, // Identifier, CurrentNode, Index, MultiSelectList, MultiSelectHash, FunctionExpression + projection_op, + flatten_projection_op, // FlattenProjection + or_op, + and_op, + eq_op, + ne_op, + lt_op, + lte_op, + gt_op, + gte_op, + not_op + }; + + struct operator_table final + { + static int precedence_level(operator_kind oper) + { + switch (oper) + { + case operator_kind::projection_op: + return 11; + case operator_kind::flatten_projection_op: + return 11; + case operator_kind::or_op: + return 9; + case operator_kind::and_op: + return 8; + case operator_kind::eq_op: + case operator_kind::ne_op: + return 6; + case operator_kind::lt_op: + case operator_kind::lte_op: + case operator_kind::gt_op: + case operator_kind::gte_op: + return 5; + case operator_kind::not_op: + return 1; + default: + return 1; + } + } + + static bool is_right_associative(operator_kind oper) + { + switch (oper) + { + case operator_kind::not_op: + return true; + case operator_kind::projection_op: + return true; + case operator_kind::flatten_projection_op: + return false; + case operator_kind::or_op: + case operator_kind::and_op: + case operator_kind::eq_op: + case operator_kind::ne_op: + case operator_kind::lt_op: + case operator_kind::lte_op: + case operator_kind::gt_op: + case operator_kind::gte_op: + return false; + default: + return false; + } + } + }; + + enum class token_kind + { + current_node, + lparen, + rparen, + begin_multi_select_hash, + end_multi_select_hash, + begin_multi_select_list, + end_multi_select_list, + begin_filter, + end_filter, + pipe, + separator, + key, + literal, + expression, + binary_operator, + unary_operator, + function, + end_function, + argument, + begin_expression_type, + end_expression_type, + end_of_expression + }; + + struct literal_arg_t + { + explicit literal_arg_t() = default; + }; + constexpr literal_arg_t literal_arg{}; + + struct begin_expression_type_arg_t + { + explicit begin_expression_type_arg_t() = default; + }; + constexpr begin_expression_type_arg_t begin_expression_type_arg{}; + + struct end_expression_type_arg_t + { + explicit end_expression_type_arg_t() = default; + }; + constexpr end_expression_type_arg_t end_expression_type_arg{}; + + struct end_of_expression_arg_t + { + explicit end_of_expression_arg_t() = default; + }; + constexpr end_of_expression_arg_t end_of_expression_arg{}; + + struct separator_arg_t + { + explicit separator_arg_t() = default; + }; + constexpr separator_arg_t separator_arg{}; + + struct key_arg_t + { + explicit key_arg_t() = default; + }; + constexpr key_arg_t key_arg{}; + + struct lparen_arg_t + { + explicit lparen_arg_t() = default; + }; + constexpr lparen_arg_t lparen_arg{}; + + struct rparen_arg_t + { + explicit rparen_arg_t() = default; + }; + constexpr rparen_arg_t rparen_arg{}; + + struct begin_multi_select_hash_arg_t + { + explicit begin_multi_select_hash_arg_t() = default; + }; + constexpr begin_multi_select_hash_arg_t begin_multi_select_hash_arg{}; + + struct end_multi_select_hash_arg_t + { + explicit end_multi_select_hash_arg_t() = default; + }; + constexpr end_multi_select_hash_arg_t end_multi_select_hash_arg{}; + + struct begin_multi_select_list_arg_t + { + explicit begin_multi_select_list_arg_t() = default; + }; + constexpr begin_multi_select_list_arg_t begin_multi_select_list_arg{}; + + struct end_multi_select_list_arg_t + { + explicit end_multi_select_list_arg_t() = default; + }; + constexpr end_multi_select_list_arg_t end_multi_select_list_arg{}; + + struct begin_filter_arg_t + { + explicit begin_filter_arg_t() = default; + }; + constexpr begin_filter_arg_t begin_filter_arg{}; + + struct end_filter_arg_t + { + explicit end_filter_arg_t() = default; + }; + constexpr end_filter_arg_t end_filter_arg{}; + + struct pipe_arg_t + { + explicit pipe_arg_t() = default; + }; + constexpr pipe_arg_t pipe_arg{}; + + struct current_node_arg_t + { + explicit current_node_arg_t() = default; + }; + constexpr current_node_arg_t current_node_arg{}; + + struct end_function_arg_t + { + explicit end_function_arg_t() = default; + }; + constexpr end_function_arg_t end_function_arg{}; + + struct argument_arg_t + { + explicit argument_arg_t() = default; + }; + constexpr argument_arg_t argument_arg{}; + + struct slice + { + jsoncons::optional start_; + jsoncons::optional stop_; + int64_t step_; + + slice() + : start_(), stop_(), step_(1) + { + } + + slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) + : start_(start), stop_(end), step_(step) + { + } + + slice(const slice& other) + : start_(other.start_), stop_(other.stop_), step_(other.step_) + { + } + + slice& operator=(const slice& rhs) + { + if (this != &rhs) + { + if (rhs.start_) + { + start_ = rhs.start_; + } + else + { + start_.reset(); + } + if (rhs.stop_) + { + stop_ = rhs.stop_; + } + else + { + stop_.reset(); + } + step_ = rhs.step_; + } + return *this; + } + + int64_t get_start(std::size_t size) const + { + if (start_) + { + auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + if (step_ >= 0) + { + return 0; + } + else + { + return static_cast(size); + } + } + } + + int64_t get_stop(std::size_t size) const + { + if (stop_) + { + auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + return step_ >= 0 ? static_cast(size) : -1; + } + } + + int64_t step() const + { + return step_; // Allow negative + } + }; + + namespace detail { + + enum class path_state + { + start, + lhs_expression, + rhs_expression, + sub_expression, + expression_type, + comparator_expression, + function_expression, + argument, + expression_or_expression_type, + quoted_string, + raw_string, + raw_string_escape_char, + quoted_string_escape_char, + escape_u1, + escape_u2, + escape_u3, + escape_u4, + escape_expect_surrogate_pair1, + escape_expect_surrogate_pair2, + escape_u5, + escape_u6, + escape_u7, + escape_u8, + literal, + key_expr, + val_expr, + identifier_or_function_expr, + unquoted_string, + key_val_expr, + number, + digit, + index_or_slice_expression, + bracket_specifier, + bracket_specifier_or_multi_select_list, + filter, + multi_select_list, + multi_select_hash, + rhs_slice_expression_stop, + rhs_slice_expression_step, + expect_rbracket, + expect_rparen, + expect_dot, + expect_rbrace, + expect_colon, + expect_multi_select_list, + cmp_lt_or_lte, + cmp_eq, + cmp_gt_or_gte, + cmp_ne, + expect_pipe_or_or, + expect_and + }; + + // dynamic_resources + + template + class dynamic_resources + { + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + std::vector> temp_storage_; + + public: + ~dynamic_resources() + { + } + + reference number_type_name() + { + static Json number_type_name(JSONCONS_STRING_CONSTANT(char_type, "number")); + + return number_type_name; + } + + reference boolean_type_name() + { + static Json boolean_type_name(JSONCONS_STRING_CONSTANT(char_type, "boolean")); + + return boolean_type_name; + } + + reference string_type_name() + { + static Json string_type_name(JSONCONS_STRING_CONSTANT(char_type, "string")); + + return string_type_name; + } + + reference object_type_name() + { + static Json object_type_name(JSONCONS_STRING_CONSTANT(char_type, "object")); + + return object_type_name; + } + + reference array_type_name() + { + static Json array_type_name(JSONCONS_STRING_CONSTANT(char_type, "array")); + + return array_type_name; + } + + reference null_type_name() + { + static Json null_type_name(JSONCONS_STRING_CONSTANT(char_type, "null")); + + return null_type_name; + } + + reference true_value() const + { + static const Json true_value(true, semantic_tag::none); + return true_value; + } + + reference false_value() const + { + static const Json false_value(false, semantic_tag::none); + return false_value; + } + + reference null_value() const + { + static const Json null_value(null_type(), semantic_tag::none); + return null_value; + } + + template + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + Json* ptr = temp.get(); + temp_storage_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template + class jmespath_evaluator + { + public: + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + static bool is_false(reference ref) + { + return (ref.is_array() && ref.empty()) || + (ref.is_object() && ref.empty()) || + (ref.is_string() && ref.as_string_view().size() == 0) || + (ref.is_bool() && !ref.as_bool()) || + ref.is_null(); + } + + static bool is_true(reference ref) + { + return !is_false(ref); + } + + class unary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + + protected: + ~unary_operator() = default; // virtual destructor not needed + public: + unary_operator(operator_kind oper) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)) + { + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual reference evaluate(reference val, dynamic_resources&, std::error_code& ec) const = 0; + }; + + class not_expression final : public unary_operator + { + public: + not_expression() + : unary_operator(operator_kind::not_op) + {} + + reference evaluate(reference val, dynamic_resources& resources, std::error_code&) const override + { + return is_false(val) ? resources.true_value() : resources.false_value(); + } + }; + + class binary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + protected: + ~binary_operator() = default; // virtual destructor not needed + public: + binary_operator(operator_kind oper) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)) + { + } + + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual reference evaluate(reference lhs, reference rhs, dynamic_resources&, std::error_code& ec) const = 0; + + virtual std::string to_string(std::size_t indent = 0) const + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("to_string not implemented\n"); + return s; + } + }; + + // expression_base + class expression_base + { + std::size_t precedence_level_; + bool is_right_associative_; + bool is_projection_; + public: + expression_base(operator_kind oper, bool is_projection) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)), + is_projection_(is_projection) + { + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + + bool is_right_associative() const + { + return is_right_associative_; + } + + bool is_projection() const + { + return is_projection_; + } + + virtual ~expression_base() = default; + + virtual reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const = 0; + + virtual void add_expression(std::unique_ptr&& expressions) = 0; + + virtual std::string to_string(std::size_t = 0) const + { + return std::string("to_string not implemented"); + } + }; + + // parameter + + enum class parameter_kind{value, expression}; + + class parameter + { + parameter_kind type_; + + union + { + expression_base* expression_; + pointer value_; + }; + + public: + + parameter(const parameter& other) noexcept + : type_(other.type_) + { + switch (type_) + { + case parameter_kind::expression: + expression_ = other.expression_; + break; + case parameter_kind::value: + value_ = other.value_; + break; + default: + break; + } + } + + parameter(reference value) noexcept + : type_(parameter_kind::value), value_(std::addressof(value)) + { + } + + parameter(expression_base* expression) noexcept + : type_(parameter_kind::expression), expression_(expression) + { + } + + parameter& operator=(const parameter& other) + { + if (&other != this) + { + type_ = other.type_; + switch (type_) + { + case parameter_kind::expression: + expression_ = other.expression_; + break; + case parameter_kind::value: + value_ = other.value_; + break; + default: + break; + } + } + return *this; + } + + bool is_value() const + { + return type_ == parameter_kind::value; + } + + bool is_expression() const + { + return type_ == parameter_kind::expression; + } + + const Json& value() const + { + return *value_; + } + + const expression_base& expression() const + { + return *expression_; + } + }; + + // function_base + class function_base + { + jsoncons::optional arg_count_; + public: + function_base(jsoncons::optional arg_count) + : arg_count_(arg_count) + { + } + + jsoncons::optional arity() const + { + return arg_count_; + } + + virtual ~function_base() = default; + + virtual reference evaluate(std::vector& args, dynamic_resources&, std::error_code& ec) const = 0; + + virtual std::string to_string(std::size_t = 0) const + { + return std::string("to_string not implemented"); + } + }; + + class abs_function : public function_base + { + public: + abs_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + return arg0; + case json_type::int64_value: + { + return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); + } + case json_type::double_value: + { + return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class avg_function : public function_base + { + public: + avg_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as(); + } + + return sum == 0 ? resources.null_value() : *resources.create_json(sum/arg0.size()); + } + }; + + class ceil_function : public function_base + { + public: + ceil_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return *resources.create_json(arg0.template as()); + } + case json_type::double_value: + { + return *resources.create_json(std::ceil(arg0.template as())); + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class contains_function : public function_base + { + public: + contains_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + switch (arg0.type()) + { + case json_type::array_value: + for (auto& j : arg0.array_range()) + { + if (j == arg1) + { + return resources.true_value(); + } + } + return resources.false_value(); + case json_type::string_value: + { + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class ends_with_function : public function_base + { + public: + ends_with_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + if (sv1.length() <= sv0.length() && sv1 == sv0.substr(sv0.length() - sv1.length())) + { + return resources.true_value(); + } + else + { + return resources.false_value(); + } + } + }; + + class floor_function : public function_base + { + public: + floor_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return *resources.create_json(arg0.template as()); + } + case json_type::double_value: + { + return *resources.create_json(std::floor(arg0.template as())); + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class join_function : public function_base + { + public: + join_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (!arg1.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + string_type sep = arg0.template as(); + string_type buf; + for (auto& j : arg1.array_range()) + { + if (!j.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (!buf.empty()) + { + buf.append(sep); + } + auto sv = j.template as(); + buf.append(sv.begin(), sv.end()); + } + return *resources.create_json(buf); + } + }; + + class length_function : public function_base + { + public: + length_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case json_type::object_value: + case json_type::array_value: + return *resources.create_json(arg0.size()); + case json_type::string_value: + { + auto sv0 = arg0.template as(); + auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size()); + return *resources.create_json(length); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class max_function : public function_base + { + public: + max_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.at(i) > arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + }; + + class max_by_function : public function_base + { + public: + max_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + const auto& expr = args[1].expression(); + + std::error_code ec2; + Json key1 = expr.evaluate(arg0.at(0), resources, ec2); + + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + reference key2 = expr.evaluate(arg0.at(i), resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (key2 > key1) + { + key1 = key2; + index = i; + } + } + + return arg0.at(index); + } + }; + + class map_function : public function_base + { + public: + map_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_expression() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + const auto& expr = args[0].expression(); + + reference arg0 = args[1].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + + for (auto& item : arg0.array_range()) + { + auto& j = expr.evaluate(item, resources, ec); + if (ec) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + + return *result; + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("map_function\n"); + } + }; + + class min_function : public function_base + { + public: + min_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.at(i) < arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + }; + + class min_by_function : public function_base + { + public: + min_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + const auto& expr = args[1].expression(); + + std::error_code ec2; + Json key1 = expr.evaluate(arg0.at(0), resources, ec2); + + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + reference key2 = expr.evaluate(arg0.at(i), resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (key2 < key1) + { + key1 = key2; + index = i; + } + } + + return arg0.at(index); + } + }; + + class merge_function : public function_base + { + public: + merge_function() + : function_base(jsoncons::optional()) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + if (args.empty()) + { + ec = jmespath_errc::invalid_arity; + return resources.null_value(); + } + + for (auto& param : args) + { + if (!param.is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (args.size() == 1) + { + return arg0; + } + + auto result = resources.create_json(arg0); + for (std::size_t i = 1; i < args.size(); ++i) + { + reference argi = args[i].value(); + if (!argi.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + for (auto& item : argi.object_range()) + { + result->insert_or_assign(item.key(),item.value()); + } + } + + return *result; + } + }; + + class type_function : public function_base + { + public: + type_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case json_type::int64_value: + case json_type::uint64_value: + case json_type::double_value: + return resources.number_type_name(); + case json_type::bool_value: + return resources.boolean_type_name(); + case json_type::string_value: + return resources.string_type_name(); + case json_type::object_value: + return resources.object_type_name(); + case json_type::array_value: + return resources.array_type_name(); + default: + return resources.null_type_name(); + break; + + } + } + }; + + class sort_function : public function_base + { + public: + sort_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.size() <= 1) + { + return arg0; + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + for (std::size_t i = 1; i < arg0.size(); ++i) + { + if (arg0.at(i).is_number() != is_number || arg0.at(i).is_string() != is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + + auto v = resources.create_json(arg0); + std::stable_sort((v->array_range()).begin(), (v->array_range()).end()); + return *v; + } + }; + + class sort_by_function : public function_base + { + public: + sort_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.size() <= 1) + { + return arg0; + } + + const auto& expr = args[1].expression(); + + auto v = resources.create_json(arg0); + std::stable_sort((v->array_range()).begin(), (v->array_range()).end(), + [&expr,&resources,&ec](reference lhs, reference rhs) -> bool + { + std::error_code ec2; + reference key1 = expr.evaluate(lhs, resources, ec2); + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + } + + reference key2 = expr.evaluate(rhs, resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + } + + return key1 < key2; + }); + return ec ? resources.null_value() : *v; + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("sort_by_function\n"); + } + }; + + class keys_function final : public function_base + { + public: + keys_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + result->reserve(args.size()); + + for (auto& item : arg0.object_range()) + { + result->emplace_back(item.key()); + } + return *result; + } + }; + + class values_function final : public function_base + { + public: + values_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + result->reserve(args.size()); + + for (auto& item : arg0.object_range()) + { + result->emplace_back(item.value()); + } + return *result; + } + }; + + class reverse_function final : public function_base + { + public: + reverse_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::string_value: + { + string_view_type sv = arg0.as_string_view(); + std::basic_string buf; + unicode_traits::convert(sv.data(), sv.size(), buf); + std::reverse(buf.begin(), buf.end()); + string_type s; + unicode_traits::convert(buf.data(), buf.size(), s); + return *resources.create_json(s); + } + case json_type::array_value: + { + auto result = resources.create_json(arg0); + std::reverse(result->array_range().begin(),result->array_range().end()); + return *result; + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class starts_with_function : public function_base + { + public: + starts_with_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + if (sv1.length() <= sv0.length() && sv1 == sv0.substr(0, sv1.length())) + { + return resources.true_value(); + } + else + { + return resources.false_value(); + } + } + }; + + class sum_function : public function_base + { + public: + sum_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as(); + } + + return *resources.create_json(sum); + } + }; + + class to_array_function final : public function_base + { + public: + to_array_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (arg0.is_array()) + { + return arg0; + } + else + { + auto result = resources.create_json(json_array_arg); + result->push_back(arg0); + return *result; + } + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_array_function\n"); + } + }; + + class to_number_function final : public function_base + { + public: + to_number_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::int64_value: + case json_type::uint64_value: + case json_type::double_value: + return arg0; + case json_type::string_value: + { + auto sv = arg0.as_string_view(); + uint64_t uval{ 0 }; + auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), uval); + if (result1) + { + return *resources.create_json(uval); + } + int64_t sval{ 0 }; + auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sval); + if (result2) + { + return *resources.create_json(sval); + } + jsoncons::detail::chars_to to_double; + try + { + auto s = arg0.as_string(); + double d = to_double(s.c_str(), s.length()); + return *resources.create_json(d); + } + catch (const std::exception&) + { + return resources.null_value(); + } + } + default: + return resources.null_value(); + } + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_number_function\n"); + } + }; + + class to_string_function final : public function_base + { + public: + to_string_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + return *resources.create_json(arg0.template as()); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_string_function\n"); + } + }; + + class not_null_function final : public function_base + { + public: + not_null_function() + : function_base(jsoncons::optional()) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code&) const override + { + for (auto& param : args) + { + if (param.is_value() && !param.value().is_null()) + { + return param.value(); + } + } + return resources.null_value(); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_string_function\n"); + } + }; + + // token + + class token + { + public: + token_kind type_; + + union + { + std::unique_ptr expression_; + const unary_operator* unary_operator_; + const binary_operator* binary_operator_; + const function_base* function_; + Json value_; + string_type key_; + }; + public: + + token(current_node_arg_t) noexcept + : type_(token_kind::current_node) + { + } + + token(end_function_arg_t) noexcept + : type_(token_kind::end_function) + { + } + + token(separator_arg_t) noexcept + : type_(token_kind::separator) + { + } + + token(lparen_arg_t) noexcept + : type_(token_kind::lparen) + { + } + + token(rparen_arg_t) noexcept + : type_(token_kind::rparen) + { + } + + token(end_of_expression_arg_t) noexcept + : type_(token_kind::end_of_expression) + { + } + + token(begin_multi_select_hash_arg_t) noexcept + : type_(token_kind::begin_multi_select_hash) + { + } + + token(end_multi_select_hash_arg_t) noexcept + : type_(token_kind::end_multi_select_hash) + { + } + + token(begin_multi_select_list_arg_t) noexcept + : type_(token_kind::begin_multi_select_list) + { + } + + token(end_multi_select_list_arg_t) noexcept + : type_(token_kind::end_multi_select_list) + { + } + + token(begin_filter_arg_t) noexcept + : type_(token_kind::begin_filter) + { + } + + token(end_filter_arg_t) noexcept + : type_(token_kind::end_filter) + { + } + + token(pipe_arg_t) noexcept + : type_(token_kind::pipe) + { + } + + token(key_arg_t, const string_type& key) + : type_(token_kind::key) + { + new (&key_) string_type(key); + } + + token(std::unique_ptr&& expression) + : type_(token_kind::expression) + { + new (&expression_) std::unique_ptr(std::move(expression)); + } + + token(const unary_operator* expression) noexcept + : type_(token_kind::unary_operator), + unary_operator_(expression) + { + } + + token(const binary_operator* expression) noexcept + : type_(token_kind::binary_operator), + binary_operator_(expression) + { + } + + token(const function_base* function) noexcept + : type_(token_kind::function), + function_(function) + { + } + + token(argument_arg_t) noexcept + : type_(token_kind::argument) + { + } + + token(begin_expression_type_arg_t) noexcept + : type_(token_kind::begin_expression_type) + { + } + + token(end_expression_type_arg_t) noexcept + : type_(token_kind::end_expression_type) + { + } + + token(literal_arg_t, Json&& value) noexcept + : type_(token_kind::literal), value_(std::move(value)) + { + } + + token(token&& other) noexcept + { + construct(std::forward(other)); + } + + token& operator=(token&& other) + { + if (&other != this) + { + if (type_ == other.type_) + { + switch (type_) + { + case token_kind::expression: + expression_ = std::move(other.expression_); + break; + case token_kind::key: + key_ = std::move(other.key_); + break; + case token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case token_kind::function: + function_ = other.function_; + break; + case token_kind::literal: + value_ = std::move(other.value_); + break; + default: + break; + } + } + else + { + destroy(); + construct(std::forward(other)); + } + } + return *this; + } + + ~token() noexcept + { + destroy(); + } + + token_kind type() const + { + return type_; + } + + bool is_lparen() const + { + return type_ == token_kind::lparen; + } + + bool is_lbrace() const + { + return type_ == token_kind::begin_multi_select_hash; + } + + bool is_key() const + { + return type_ == token_kind::key; + } + + bool is_rparen() const + { + return type_ == token_kind::rparen; + } + + bool is_current_node() const + { + return type_ == token_kind::current_node; + } + + bool is_projection() const + { + return type_ == token_kind::expression && expression_->is_projection(); + } + + bool is_expression() const + { + return type_ == token_kind::expression; + } + + bool is_operator() const + { + return type_ == token_kind::unary_operator || + type_ == token_kind::binary_operator; + } + + std::size_t precedence_level() const + { + switch(type_) + { + case token_kind::unary_operator: + return unary_operator_->precedence_level(); + case token_kind::binary_operator: + return binary_operator_->precedence_level(); + case token_kind::expression: + return expression_->precedence_level(); + default: + return 0; + } + } + + jsoncons::optional arity() const + { + return type_ == token_kind::function ? function_->arity() : jsoncons::optional(); + } + + bool is_right_associative() const + { + switch(type_) + { + case token_kind::unary_operator: + return unary_operator_->is_right_associative(); + case token_kind::binary_operator: + return binary_operator_->is_right_associative(); + case token_kind::expression: + return expression_->is_right_associative(); + default: + return false; + } + } + + void construct(token&& other) + { + type_ = other.type_; + switch (type_) + { + case token_kind::expression: + new (&expression_) std::unique_ptr(std::move(other.expression_)); + break; + case token_kind::key: + new (&key_) string_type(std::move(other.key_)); + break; + case token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case token_kind::function: + function_ = other.function_; + break; + case token_kind::literal: + new (&value_) Json(std::move(other.value_)); + break; + default: + break; + } + } + + void destroy() noexcept + { + switch(type_) + { + case token_kind::expression: + expression_.~unique_ptr(); + break; + case token_kind::key: + key_.~basic_string(); + break; + case token_kind::literal: + value_.~Json(); + break; + default: + break; + } + } + + std::string to_string(std::size_t indent = 0) const + { + switch(type_) + { + case token_kind::expression: + return expression_->to_string(indent); + break; + case token_kind::unary_operator: + return std::string("unary_operator"); + break; + case token_kind::binary_operator: + return binary_operator_->to_string(indent); + break; + case token_kind::current_node: + return std::string("current_node"); + break; + case token_kind::end_function: + return std::string("end_function"); + break; + case token_kind::separator: + return std::string("separator"); + break; + case token_kind::literal: + return std::string("literal"); + break; + case token_kind::key: + return std::string("key") + key_; + break; + case token_kind::begin_multi_select_hash: + return std::string("begin_multi_select_hash"); + break; + case token_kind::begin_multi_select_list: + return std::string("begin_multi_select_list"); + break; + case token_kind::begin_filter: + return std::string("begin_filter"); + break; + case token_kind::pipe: + return std::string("pipe"); + break; + case token_kind::lparen: + return std::string("lparen"); + break; + case token_kind::function: + return function_->to_string(); + case token_kind::argument: + return std::string("argument"); + break; + case token_kind::begin_expression_type: + return std::string("begin_expression_type"); + break; + case token_kind::end_expression_type: + return std::string("end_expression_type"); + break; + default: + return std::string("default"); + break; + } + } + }; + + static pointer evaluate_tokens(reference doc, const std::vector& output_stack, dynamic_resources& resources, std::error_code& ec) + { + pointer root_ptr = std::addressof(doc); + std::vector stack; + std::vector arg_stack; + for (std::size_t i = 0; i < output_stack.size(); ++i) + { + auto& t = output_stack[i]; + switch (t.type()) + { + case token_kind::literal: + { + stack.emplace_back(t.value_); + break; + } + case token_kind::begin_expression_type: + { + JSONCONS_ASSERT(i+1 < output_stack.size()); + ++i; + JSONCONS_ASSERT(output_stack[i].is_expression()); + JSONCONS_ASSERT(!stack.empty()); + stack.pop_back(); + stack.emplace_back(output_stack[i].expression_.get()); + break; + } + case token_kind::pipe: + { + JSONCONS_ASSERT(!stack.empty()); + root_ptr = std::addressof(stack.back().value()); + break; + } + case token_kind::current_node: + stack.emplace_back(*root_ptr); + break; + case token_kind::expression: + { + JSONCONS_ASSERT(!stack.empty()); + pointer ptr = std::addressof(stack.back().value()); + stack.pop_back(); + auto& ref = t.expression_->evaluate(*ptr, resources, ec); + stack.emplace_back(ref); + break; + } + case token_kind::unary_operator: + { + JSONCONS_ASSERT(stack.size() >= 1); + pointer ptr = std::addressof(stack.back().value()); + stack.pop_back(); + reference r = t.unary_operator_->evaluate(*ptr, resources, ec); + stack.emplace_back(r); + break; + } + case token_kind::binary_operator: + { + JSONCONS_ASSERT(stack.size() >= 2); + pointer rhs = std::addressof(stack.back().value()); + stack.pop_back(); + pointer lhs = std::addressof(stack.back().value()); + stack.pop_back(); + reference r = t.binary_operator_->evaluate(*lhs,*rhs, resources, ec); + stack.emplace_back(r); + break; + } + case token_kind::argument: + { + JSONCONS_ASSERT(!stack.empty()); + arg_stack.push_back(std::move(stack.back())); + stack.pop_back(); + break; + } + case token_kind::function: + { + if (t.function_->arity() && *(t.function_->arity()) != arg_stack.size()) + { + ec = jmespath_errc::invalid_arity; + return std::addressof(resources.null_value()); + } + + reference r = t.function_->evaluate(arg_stack, resources, ec); + if (ec) + { + return std::addressof(resources.null_value()); + } + arg_stack.clear(); + stack.emplace_back(r); + break; + } + default: + break; + } + } + JSONCONS_ASSERT(stack.size() == 1); + return std::addressof(stack.back().value()); + } + + // Implementations + + class or_operator final : public binary_operator + { + public: + or_operator() + : binary_operator(operator_kind::or_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if (lhs.is_null() && rhs.is_null()) + { + return resources.null_value(); + } + if (!is_false(lhs)) + { + return lhs; + } + else + { + return rhs; + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("or_operator\n"); + return s; + } + }; + + class and_operator final : public binary_operator + { + public: + and_operator() + : binary_operator(operator_kind::and_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources&, std::error_code&) const override + { + if (is_true(lhs)) + { + return rhs; + } + else + { + return lhs; + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("and_operator\n"); + return s; + } + }; + + class eq_operator final : public binary_operator + { + public: + eq_operator() + : binary_operator(operator_kind::eq_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + return lhs == rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("eq_operator\n"); + return s; + } + }; + + class ne_operator final : public binary_operator + { + public: + ne_operator() + : binary_operator(operator_kind::ne_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + return lhs != rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("ne_operator\n"); + return s; + } + }; + + class lt_operator final : public binary_operator + { + public: + lt_operator() + : binary_operator(operator_kind::lt_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs < rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("lt_operator\n"); + return s; + } + }; + + class lte_operator final : public binary_operator + { + public: + lte_operator() + : binary_operator(operator_kind::lte_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs <= rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("lte_operator\n"); + return s; + } + }; + + class gt_operator final : public binary_operator + { + public: + gt_operator() + : binary_operator(operator_kind::gt_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs > rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("gt_operator\n"); + return s; + } + }; + + class gte_operator final : public binary_operator + { + public: + gte_operator() + : binary_operator(operator_kind::gte_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs >= rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("gte_operator\n"); + return s; + } + }; + + // basic_expression + class basic_expression : public expression_base + { + public: + basic_expression() + : expression_base(operator_kind::default_op, false) + { + } + + void add_expression(std::unique_ptr&&) override + { + } + }; + + class identifier_selector final : public basic_expression + { + private: + string_type identifier_; + public: + identifier_selector(const string_view_type& name) + : identifier_(name) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code&) const override + { + //std::cout << "(identifier_selector " << identifier_ << " ) " << pretty_print(val) << "\n"; + if (val.is_object() && val.contains(identifier_)) + { + return val.at(identifier_); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("identifier_selector "); + s.append(identifier_); + return s; + } + }; + + class current_node final : public basic_expression + { + public: + current_node() + { + } + + reference evaluate(reference val, dynamic_resources&, std::error_code&) const override + { + return val; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("current_node "); + return s; + } + }; + + class index_selector final : public basic_expression + { + int64_t index_; + public: + index_selector(int64_t index) + : index_(index) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code&) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + int64_t slen = static_cast(val.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t index = static_cast(index_); + return val.at(index); + } + else if ((slen + index_) >= 0 && (slen+index_) < slen) + { + std::size_t index = static_cast(slen + index_); + return val.at(index); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("index_selector "); + s.append(std::to_string(index_)); + return s; + } + }; + + // projection_base + class projection_base : public expression_base + { + protected: + std::vector> expressions_; + public: + projection_base(operator_kind oper) + : expression_base(oper, true) + { + } + + void add_expression(std::unique_ptr&& expr) override + { + if (!expressions_.empty() && expressions_.back()->is_projection() && + (expr->precedence_level() < expressions_.back()->precedence_level() || + (expr->precedence_level() == expressions_.back()->precedence_level() && expr->is_right_associative()))) + { + expressions_.back()->add_expression(std::move(expr)); + } + else + { + expressions_.emplace_back(std::move(expr)); + } + } + + reference apply_expressions(reference val, dynamic_resources& resources, std::error_code& ec) const + { + pointer ptr = std::addressof(val); + for (auto& expression : expressions_) + { + ptr = std::addressof(expression->evaluate(*ptr, resources, ec)); + } + return *ptr; + } + }; + + class object_projection final : public projection_base + { + public: + object_projection() + : projection_base(operator_kind::projection_op) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_object()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (auto& item : val.object_range()) + { + if (!item.value().is_null()) + { + reference j = this->apply_expressions(item.value(), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("object_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class list_projection final : public projection_base + { + public: + list_projection() + : projection_base(operator_kind::projection_op) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (reference item : val.array_range()) + { + if (!item.is_null()) + { + reference j = this->apply_expressions(item, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("list_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class slice_projection final : public projection_base + { + slice slice_; + public: + slice_projection(const slice& s) + : projection_base(operator_kind::projection_op), slice_(s) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto start = slice_.get_start(val.size()); + auto end = slice_.get_stop(val.size()); + auto step = slice_.step(); + + if (step == 0) + { + ec = jmespath_errc::step_cannot_be_zero; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > static_cast(val.size())) + { + end = val.size(); + } + for (int64_t i = start; i < end; i += step) + { + reference j = this->apply_expressions(val.at(static_cast(i)), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + else + { + if (start >= static_cast(val.size())) + { + start = static_cast(val.size()) - 1; + } + if (end < -1) + { + end = -1; + } + for (int64_t i = start; i > end; i += step) + { + reference j = this->apply_expressions(val.at(static_cast(i)), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("slice_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class filter_expression final : public projection_base + { + std::vector token_list_; + public: + filter_expression(std::vector&& token_list) + : projection_base(operator_kind::projection_op), token_list_(std::move(token_list)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + auto result = resources.create_json(json_array_arg); + + for (auto& item : val.array_range()) + { + Json j(json_const_pointer_arg, evaluate_tokens(item, token_list_, resources, ec)); + if (is_true(j)) + { + reference jj = this->apply_expressions(item, resources, ec); + if (!jj.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(jj)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("filter_expression\n"); + for (auto& item : token_list_) + { + std::string sss = item.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class flatten_projection final : public projection_base + { + public: + flatten_projection() + : projection_base(operator_kind::flatten_projection_op) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (reference current_elem : val.array_range()) + { + if (current_elem.is_array()) + { + for (reference elem : current_elem.array_range()) + { + if (!elem.is_null()) + { + reference j = this->apply_expressions(elem, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + } + else + { + if (!current_elem.is_null()) + { + reference j = this->apply_expressions(current_elem, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("flatten_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class multi_select_list final : public basic_expression + { + std::vector> token_lists_; + public: + multi_select_list(std::vector>&& token_lists) + : token_lists_(std::move(token_lists)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (val.is_null()) + { + return val; + } + auto result = resources.create_json(json_array_arg); + result->reserve(token_lists_.size()); + + for (auto& list : token_lists_) + { + result->emplace_back(json_const_pointer_arg, evaluate_tokens(val, list, resources, ec)); + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("multi_select_list\n"); + for (auto& list : token_lists_) + { + for (auto& item : list) + { + std::string sss = item.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + s.append("---\n"); + } + return s; + } + }; + + struct key_tokens + { + string_type key; + std::vector tokens; + + key_tokens(string_type&& key, std::vector&& tokens) noexcept + : key(std::move(key)), tokens(std::move(tokens)) + { + } + }; + + class multi_select_hash final : public basic_expression + { + public: + std::vector key_toks_; + + multi_select_hash(std::vector&& key_toks) + : key_toks_(std::move(key_toks)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (val.is_null()) + { + return val; + } + auto resultp = resources.create_json(json_object_arg); + resultp->reserve(key_toks_.size()); + for (auto& item : key_toks_) + { + resultp->try_emplace(item.key, json_const_pointer_arg, evaluate_tokens(val, item.tokens, resources, ec)); + } + + return *resultp; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("multi_select_list\n"); + return s; + } + }; + + class function_expression final : public basic_expression + { + public: + std::vector toks_; + + function_expression(std::vector&& toks) + : toks_(std::move(toks)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + return *evaluate_tokens(val, toks_, resources, ec); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("function_expression\n"); + for (auto& tok : toks_) + { + for (std::size_t i = 0; i <= indent+2; ++i) + { + s.push_back(' '); + } + std::string sss = tok.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class static_resources + { + std::vector> temp_storage_; + + public: + + static_resources() = default; + static_resources(const static_resources& expr) = delete; + static_resources& operator=(const static_resources& expr) = delete; + static_resources(static_resources&& expr) = default; + static_resources& operator=(static_resources&& expr) = default; + + const function_base* get_function(const string_type& name, std::error_code& ec) const + { + static abs_function abs_func; + static avg_function avg_func; + static ceil_function ceil_func; + static contains_function contains_func; + static ends_with_function ends_with_func; + static floor_function floor_func; + static join_function join_func; + static length_function length_func; + static max_function max_func; + static max_by_function max_by_func; + static map_function map_func; + static merge_function merge_func; + static min_function min_func; + static min_by_function min_by_func; + static type_function type_func; + static sort_function sort_func; + static sort_by_function sort_by_func; + static keys_function keys_func; + static values_function values_func; + static reverse_function reverse_func; + static starts_with_function starts_with_func; + static const sum_function sum_func; + static to_array_function to_array_func; + static to_number_function to_number_func; + static to_string_function to_string_func; + static not_null_function not_null_func; + + using function_dictionary = std::unordered_map; + static const function_dictionary functions_ = + { + {string_type{'a','b','s'}, &abs_func}, + {string_type{'a','v','g'}, &avg_func}, + {string_type{'c','e','i', 'l'}, &ceil_func}, + {string_type{'c','o','n', 't', 'a', 'i', 'n', 's'}, &contains_func}, + {string_type{'e','n','d', 's', '_', 'w', 'i', 't', 'h'}, &ends_with_func}, + {string_type{'f','l','o', 'o', 'r'}, &floor_func}, + {string_type{'j','o','i', 'n'}, &join_func}, + {string_type{'l','e','n', 'g', 't', 'h'}, &length_func}, + {string_type{'m','a','x'}, &max_func}, + {string_type{'m','a','x','_','b','y'}, &max_by_func}, + {string_type{'m','a','p'}, &map_func}, + {string_type{'m','i','n'}, &min_func}, + {string_type{'m','i','n','_','b','y'}, &min_by_func}, + {string_type{'m','e','r', 'g', 'e'}, &merge_func}, + {string_type{'t','y','p', 'e'}, &type_func}, + {string_type{'s','o','r', 't'}, &sort_func}, + {string_type{'s','o','r', 't','_','b','y'}, &sort_by_func}, + {string_type{'k','e','y', 's'}, &keys_func}, + {string_type{'v','a','l', 'u','e','s'}, &values_func}, + {string_type{'r','e','v', 'e', 'r', 's','e'}, &reverse_func}, + {string_type{'s','t','a', 'r','t','s','_','w','i','t','h'}, &starts_with_func}, + {string_type{'s','u','m'}, &sum_func}, + {string_type{'t','o','_','a','r','r','a','y',}, &to_array_func}, + {string_type{'t','o','_', 'n', 'u', 'm','b','e','r'}, &to_number_func}, + {string_type{'t','o','_', 's', 't', 'r','i','n','g'}, &to_string_func}, + {string_type{'n','o','t', '_', 'n', 'u','l','l'}, ¬_null_func} + }; + auto it = functions_.find(name); + if (it == functions_.end()) + { + ec = jmespath_errc::unknown_function; + return nullptr; + } + return it->second; + } + + const unary_operator* get_not_operator() const + { + static const not_expression not_oper; + + return ¬_oper; + } + + const binary_operator* get_or_operator() const + { + static const or_operator or_oper; + + return &or_oper; + } + + const binary_operator* get_and_operator() const + { + static const and_operator and_oper; + + return &and_oper; + } + + const binary_operator* get_eq_operator() const + { + static const eq_operator eq_oper; + return &eq_oper; + } + + const binary_operator* get_ne_operator() const + { + static const ne_operator ne_oper; + return &ne_oper; + } + + const binary_operator* get_lt_operator() const + { + static const lt_operator lt_oper; + return <_oper; + } + + const binary_operator* get_lte_operator() const + { + static const lte_operator lte_oper; + return <e_oper; + } + + const binary_operator* get_gt_operator() const + { + static const gt_operator gt_oper; + return >_oper; + } + + const binary_operator* get_gte_operator() const + { + static const gte_operator gte_oper; + return >e_oper; + } + }; + + class jmespath_expression + { + static_resources resources_; + std::vector output_stack_; + public: + jmespath_expression() + { + } + + jmespath_expression(const jmespath_expression& expr) = delete; + jmespath_expression& operator=(const jmespath_expression& expr) = delete; + + jmespath_expression(jmespath_expression&& expr) + : resources_(std::move(expr.resources_)), + output_stack_(std::move(expr.output_stack_)) + { + } + + jmespath_expression(static_resources&& resources, + std::vector&& output_stack) + : resources_(std::move(resources)), output_stack_(std::move(output_stack)) + { + } + + Json evaluate(reference doc) + { + if (output_stack_.empty()) + { + return Json::null(); + } + std::error_code ec; + Json result = evaluate(doc, ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec)); + } + return result; + } + + Json evaluate(reference doc, std::error_code& ec) + { + if (output_stack_.empty()) + { + return Json::null(); + } + dynamic_resources dynamic_storage; + return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec)); + } + + static jmespath_expression compile(const string_view_type& expr) + { + jsoncons::jmespath::detail::jmespath_evaluator evaluator; + std::error_code ec; + jmespath_expression result = evaluator.compile(expr.data(), expr.size(), ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column())); + } + return result; + } + + static jmespath_expression compile(const string_view_type& expr, + std::error_code& ec) + { + jsoncons::jmespath::detail::jmespath_evaluator evaluator; + return evaluator.compile(expr.data(), expr.size(), ec); + } + }; + private: + std::size_t line_; + std::size_t column_; + const char_type* begin_input_; + const char_type* end_input_; + const char_type* p_; + + static_resources resources_; + std::vector state_stack_; + + std::vector output_stack_; + std::vector operator_stack_; + + public: + jmespath_evaluator() + : line_(1), column_(1), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr) + { + } + + std::size_t line() const + { + return line_; + } + + std::size_t column() const + { + return column_; + } + + jmespath_expression compile(const char_type* path, + std::size_t length, + std::error_code& ec) + { + push_token(current_node_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::start); + + string_type buffer; + uint32_t cp = 0; + uint32_t cp2 = 0; + + begin_input_ = path; + end_input_ = path + length; + p_ = begin_input_; + + slice slic{}; + + while (p_ < end_input_) + { + switch (state_stack_.back()) + { + case path_state::start: + { + state_stack_.back() = path_state::rhs_expression; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + case path_state::rhs_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '.': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::sub_expression); + break; + case '|': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_pipe_or_or); + break; + case '&': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_and); + break; + case '<': + case '>': + case '=': + { + state_stack_.emplace_back(path_state::comparator_expression); + break; + } + case '!': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::cmp_ne); + break; + } + case ')': + { + state_stack_.pop_back(); + break; + } + case '[': + state_stack_.emplace_back(path_state::bracket_specifier); + ++p_; + ++column_; + break; + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + } + break; + case path_state::comparator_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '<': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_lt_or_lte); + break; + case '>': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_gt_or_gte); + break; + case '=': + { + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_eq); + break; + } + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + } + break; + case path_state::lhs_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::raw_string; + ++p_; + ++column_; + break; + case '`': + state_stack_.back() = path_state::literal; + ++p_; + ++column_; + break; + case '{': + push_token(begin_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': // wildcard + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '(': + { + ++p_; + ++column_; + push_token(lparen_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rparen; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + case '!': + { + ++p_; + ++column_; + push_token(token(resources_.get_not_operator()), ec); + if (ec) {return jmespath_expression();} + break; + } + case '@': + ++p_; + ++column_; + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case '[': + state_stack_.back() = path_state::bracket_specifier_or_multi_select_list; + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_identifier; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::sub_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '{': + push_token(begin_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '[': + state_stack_.back() = path_state::expect_multi_select_list; + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_identifier; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::key_expr: + push_token(token(key_arg, buffer), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::expression_or_expression_type: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '&': + push_token(token(begin_expression_type_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expression_type; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + default: + state_stack_.back() = path_state::argument; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + case path_state::identifier_or_function_expr: + switch(*p_) + { + case '(': + { + auto f = resources_.get_function(buffer, ec); + if (ec) + { + return jmespath_expression(); + } + buffer.clear(); + push_token(token(f), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::function_expression; + state_stack_.emplace_back(path_state::expression_or_expression_type); + ++p_; + ++column_; + break; + } + default: + { + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + } + break; + + case path_state::function_expression: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::expression_or_expression_type); + ++p_; + ++column_; + break; + case ')': + { + push_token(token(end_function_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + break; + } + break; + + case path_state::argument: + push_token(argument_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + + case path_state::expression_type: + push_token(end_expression_type_arg, ec); + push_token(argument_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + + case path_state::quoted_string: + switch (*p_) + { + case '\"': + state_stack_.pop_back(); // quoted_string + ++p_; + ++column_; + break; + case '\\': + state_stack_.emplace_back(path_state::quoted_string_escape_char); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + + case path_state::unquoted_string: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + state_stack_.pop_back(); // unquoted_string + advance_past_space_character(ec); + break; + default: + if ((*p_ >= '0' && *p_ <= '9') || (*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + state_stack_.pop_back(); // unquoted_string + } + break; + }; + break; + case path_state::raw_string_escape_char: + switch (*p_) + { + case '\'': + buffer.push_back(*p_); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + buffer.push_back('\\'); + buffer.push_back(*p_); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + break; + case path_state::quoted_string_escape_char: + switch (*p_) + { + case '\"': + buffer.push_back('\"'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '\\': + buffer.push_back('\\'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '/': + buffer.push_back('/'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'b': + buffer.push_back('\b'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'f': + buffer.push_back('\f'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'n': + buffer.push_back('\n'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'r': + buffer.push_back('\r'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 't': + buffer.push_back('\t'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'u': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u1; + break; + default: + ec = jmespath_errc::illegal_escaped_character; + return jmespath_expression(); + } + break; + case path_state::escape_u1: + cp = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u2; + break; + case path_state::escape_u2: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u3; + break; + case path_state::escape_u3: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u4; + break; + case path_state::escape_u4: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + if (unicode_traits::is_high_surrogate(cp)) + { + ++p_; + ++column_; + state_stack_.back() = path_state::escape_expect_surrogate_pair1; + } + else + { + unicode_traits::convert(&cp, 1, buffer); + ++p_; + ++column_; + state_stack_.pop_back(); + } + break; + case path_state::escape_expect_surrogate_pair1: + switch (*p_) + { + case '\\': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_expect_surrogate_pair2; + break; + default: + ec = jmespath_errc::invalid_codepoint; + return jmespath_expression(); + } + break; + case path_state::escape_expect_surrogate_pair2: + switch (*p_) + { + case 'u': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u5; + break; + default: + ec = jmespath_errc::invalid_codepoint; + return jmespath_expression(); + } + break; + case path_state::escape_u5: + cp2 = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u6; + break; + case path_state::escape_u6: + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u7; + break; + case path_state::escape_u7: + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u8; + break; + case path_state::escape_u8: + { + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); + unicode_traits::convert(&codepoint, 1, buffer); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + case path_state::raw_string: + switch (*p_) + { + case '\'': + { + push_token(token(literal_arg, Json(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); // raw_string + ++p_; + ++column_; + break; + } + case '\\': + state_stack_.emplace_back(path_state::raw_string_escape_char); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::literal: + switch (*p_) + { + case '`': + { + json_decoder decoder; + basic_json_reader> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) + { + ec = jmespath_errc::invalid_literal; + return jmespath_expression(); + } + auto j = decoder.get_result(); + + push_token(token(literal_arg, std::move(j)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); // json_value + ++p_; + ++column_; + break; + } + case '\\': + if (p_+1 < end_input_) + { + ++p_; + ++column_; + if (*p_ != '`') + { + buffer.push_back('\\'); + } + buffer.push_back(*p_); + } + else + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::number: + switch(*p_) + { + case '-': + buffer.push_back(*p_); + state_stack_.back() = path_state::digit; + ++p_; + ++column_; + break; + default: + state_stack_.back() = path_state::digit; + break; + } + break; + case path_state::digit: + switch(*p_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.push_back(*p_); + ++p_; + ++column_; + break; + default: + state_stack_.pop_back(); // digit + break; + } + break; + + case path_state::bracket_specifier: + switch(*p_) + { + case '*': + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + case ']': // [] + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); // bracket_specifier + ++p_; + ++column_; + break; + case '?': + push_token(token(begin_filter_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::filter; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + case ':': // slice_expression + state_stack_.back() = path_state::rhs_slice_expression_stop ; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + // number + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state_stack_.back() = path_state::index_or_slice_expression; + state_stack_.emplace_back(path_state::number); + break; + default: + ec = jmespath_errc::expected_index_expression; + return jmespath_expression(); + } + break; + case path_state::bracket_specifier_or_multi_select_list: + switch(*p_) + { + case '*': + if (p_+1 >= end_input_) + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + if (*(p_+1) == ']') + { + state_stack_.back() = path_state::bracket_specifier; + } + else + { + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + } + break; + case ']': // [] + case '?': + case ':': // slice_expression + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state_stack_.back() = path_state::bracket_specifier; + break; + default: + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + + case path_state::expect_multi_select_list: + switch(*p_) + { + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ec = jmespath_errc::expected_multi_select_list; + return jmespath_expression(); + case '*': + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + default: + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + + case path_state::multi_select_hash: + switch(*p_) + { + case '*': + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + break; + default: + state_stack_.back() = path_state::key_val_expr; + break; + } + break; + + case path_state::index_or_slice_expression: + switch(*p_) + { + case ']': + { + if (buffer.empty()) + { + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jmespath_expression();} + } + else + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + push_token(token(jsoncons::make_unique(val)), ec); + if (ec) {return jmespath_expression();} + + buffer.clear(); + } + state_stack_.pop_back(); // bracket_specifier + ++p_; + ++column_; + break; + } + case ':': + { + if (!buffer.empty()) + { + int64_t val; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + slic.start_ = val; + buffer.clear(); + } + state_stack_.back() = path_state::rhs_slice_expression_stop; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + case path_state::rhs_slice_expression_stop : + { + if (!buffer.empty()) + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + slic.stop_ = jsoncons::optional(val); + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique(slic)), ec); + if (ec) {return jmespath_expression();} + slic = slice{}; + state_stack_.pop_back(); // bracket_specifier2 + ++p_; + ++column_; + break; + case ':': + state_stack_.back() = path_state::rhs_slice_expression_step; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::rhs_slice_expression_step: + { + if (!buffer.empty()) + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + if (val == 0) + { + ec = jmespath_errc::step_cannot_be_zero; + return jmespath_expression(); + } + slic.step_ = val; + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique(slic)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + slic = slice{}; + state_stack_.pop_back(); // rhs_slice_expression_step + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rbracket: + { + switch(*p_) + { + case ']': + state_stack_.pop_back(); // expect_rbracket + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rparen: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ')': + ++p_; + ++column_; + push_token(rparen_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::rhs_expression; + break; + default: + ec = jmespath_errc::expected_rparen; + return jmespath_expression(); + } + break; + case path_state::key_val_expr: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::key_expr); + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::raw_string); + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::key_expr); + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_key; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::cmp_lt_or_lte: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_lte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(resources_.get_lt_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_gt_or_gte: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_gte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(resources_.get_gt_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_eq: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_eq_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_comparator; + return jmespath_expression(); + } + break; + } + case path_state::cmp_ne: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_ne_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_comparator; + return jmespath_expression(); + } + break; + } + case path_state::expect_dot: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '.': + state_stack_.pop_back(); // expect_dot + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_dot; + return jmespath_expression(); + } + break; + } + case path_state::expect_pipe_or_or: + { + switch(*p_) + { + case '|': + push_token(token(resources_.get_or_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(pipe_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::expect_and: + { + switch(*p_) + { + case '&': + push_token(token(resources_.get_and_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); // expect_and + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_and; + return jmespath_expression(); + } + break; + } + case path_state::multi_select_list: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(separator_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + case '[': + state_stack_.emplace_back(path_state::lhs_expression); + break; + case '.': + state_stack_.emplace_back(path_state::sub_expression); + ++p_; + ++column_; + break; + case '|': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_pipe_or_or); + break; + } + case ']': + { + push_token(token(end_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::filter: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ']': + { + push_token(token(end_filter_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rbrace: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(separator_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::key_val_expr; + ++p_; + ++column_; + break; + case '[': + case '{': + state_stack_.emplace_back(path_state::lhs_expression); + break; + case '.': + state_stack_.emplace_back(path_state::sub_expression); + ++p_; + ++column_; + break; + case '}': + { + state_stack_.pop_back(); + push_token(end_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbrace; + return jmespath_expression(); + } + break; + } + case path_state::expect_colon: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ':': + state_stack_.back() = path_state::expect_rbrace; + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_colon; + return jmespath_expression(); + } + break; + } + } + + } + + if (state_stack_.empty()) + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + while (state_stack_.size() > 1) + { + switch (state_stack_.back()) + { + case path_state::rhs_expression: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case path_state::identifier_or_function_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case path_state::unquoted_string: + state_stack_.pop_back(); + break; + default: + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + break; + } + } + + if (!(state_stack_.size() == 1 && state_stack_.back() == path_state::rhs_expression)) + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + + state_stack_.pop_back(); + + push_token(end_of_expression_arg, ec); + if (ec) {return jmespath_expression();} + + //for (auto& t : output_stack_) + //{ + // std::cout << t.to_string() << std::endl; + //} + + return jmespath_expression(std::move(resources_), std::move(output_stack_)); + } + + void advance_past_space_character(std::error_code& ec) + { + switch (*p_) + { + case ' ':case '\t': + ++p_; + ++column_; + break; + case '\r': + if (p_+1 >= end_input_) + { + ec = jmespath_errc::unexpected_end_of_input; + return; + } + if (*(p_+1) == '\n') + ++p_; + ++line_; + column_ = 1; + ++p_; + break; + case '\n': + ++line_; + column_ = 1; + ++p_; + break; + default: + break; + } + } + + void unwind_rparen(std::error_code& ec) + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && !it->is_lparen()) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + if (it == operator_stack_.rend()) + { + ec = jmespath_errc::unbalanced_parentheses; + return; + } + ++it; + operator_stack_.erase(it.base(),operator_stack_.end()); + } + + void push_token(token&& tok, std::error_code& ec) + { + switch (tok.type()) + { + case token_kind::end_filter: + { + unwind_rparen(ec); + std::vector toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_filter) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(toks)))); + } + break; + } + case token_kind::end_multi_select_list: + { + unwind_rparen(ec); + std::vector> vals; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list) + { + std::vector toks; + do + { + toks.emplace_back(std::move(*it)); + ++it; + } while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list && it->type() != token_kind::separator); + if (it->type() == token_kind::separator) + { + ++it; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + vals.emplace_back(std::move(toks)); + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + std::reverse(vals.begin(), vals.end()); + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique(std::move(vals))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(vals)))); + } + break; + } + case token_kind::end_multi_select_hash: + { + unwind_rparen(ec); + std::vector key_toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_hash) + { + std::vector toks; + do + { + toks.emplace_back(std::move(*it)); + ++it; + } while (it != output_stack_.rend() && it->type() != token_kind::key); + JSONCONS_ASSERT(it->is_key()); + auto key = std::move(it->key_); + ++it; + if (it->type() == token_kind::separator) + { + ++it; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + key_toks.emplace_back(std::move(key), std::move(toks)); + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + std::reverse(key_toks.begin(), key_toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique(std::move(key_toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(key_toks)))); + } + break; + } + case token_kind::end_expression_type: + { + std::vector toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_expression_type) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + JSONCONS_THROW(json_runtime_error("Unbalanced braces")); + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + output_stack_.erase(it.base(),output_stack_.end()); + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(toks)))); + break; + } + case token_kind::literal: + if (!output_stack_.empty() && output_stack_.back().type() == token_kind::current_node) + { + output_stack_.back() = std::move(tok); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + case token_kind::expression: + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(std::move(tok.expression_)); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + case token_kind::rparen: + { + unwind_rparen(ec); + break; + } + case token_kind::end_function: + { + unwind_rparen(ec); + std::vector toks; + auto it = output_stack_.rbegin(); + std::size_t arg_count = 0; + while (it != output_stack_.rend() && it->type() != token_kind::function) + { + if (it->type() == token_kind::argument) + { + ++arg_count; + } + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_parentheses; + return; + } + if (it->arity() && arg_count != *(it->arity())) + { + ec = jmespath_errc::invalid_arity; + return; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + toks.push_back(std::move(*it)); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(toks)))); + } + break; + } + case token_kind::end_of_expression: + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend()) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + operator_stack_.clear(); + break; + } + case token_kind::unary_operator: + case token_kind::binary_operator: + { + if (operator_stack_.empty() || operator_stack_.back().is_lparen()) + { + operator_stack_.emplace_back(std::move(tok)); + } + else if (tok.precedence_level() < operator_stack_.back().precedence_level() + || (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative())) + { + operator_stack_.emplace_back(std::move(tok)); + } + else + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && it->is_operator() + && (tok.precedence_level() > it->precedence_level() + || (tok.precedence_level() == it->precedence_level() && tok.is_right_associative()))) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + + operator_stack_.erase(it.base(),operator_stack_.end()); + operator_stack_.emplace_back(std::move(tok)); + } + break; + } + case token_kind::separator: + { + unwind_rparen(ec); + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + } + case token_kind::begin_filter: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::begin_multi_select_list: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::begin_multi_select_hash: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::function: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::current_node: + output_stack_.emplace_back(std::move(tok)); + break; + case token_kind::key: + case token_kind::pipe: + case token_kind::argument: + case token_kind::begin_expression_type: + output_stack_.emplace_back(std::move(tok)); + break; + case token_kind::lparen: + operator_stack_.emplace_back(std::move(tok)); + break; + default: + break; + } + } + + uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec) + { + cp *= 16; + if (c >= '0' && c <= '9') + { + cp += c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + cp += c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + cp += c - 'A' + 10; + } + else + { + ec = jmespath_errc::invalid_codepoint; + } + return cp; + } + }; + + } // detail + + template + using jmespath_expression = typename jsoncons::jmespath::detail::jmespath_evaluator::jmespath_expression; + + template + Json search(const Json& doc, const typename Json::string_view_type& path) + { + jsoncons::jmespath::detail::jmespath_evaluator evaluator; + std::error_code ec; + auto expr = evaluator.compile(path.data(), path.size(), ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column())); + } + auto result = expr.evaluate(doc, ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec)); + } + return result; + } + + template + Json search(const Json& doc, const typename Json::string_view_type& path, std::error_code& ec) + { + jsoncons::jmespath::detail::jmespath_evaluator evaluator; + auto expr = evaluator.compile(path.data(), path.size(), ec); + if (ec) + { + return Json::null(); + } + auto result = expr.evaluate(doc, ec); + if (ec) + { + return Json::null(); + } + return result; + } + + template + jmespath_expression make_expression(const typename json::string_view_type& expr) + { + return jmespath_expression::compile(expr); + } + + template + jmespath_expression make_expression(const typename json::string_view_type& expr, + std::error_code& ec) + { + return jmespath_expression::compile(expr, ec); + } + + +} // namespace jmespath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jmespath/jmespath_error.hpp b/include/jsoncons_ext/jmespath/jmespath_error.hpp new file mode 100644 index 0000000..6422c65 --- /dev/null +++ b/include/jsoncons_ext/jmespath/jmespath_error.hpp @@ -0,0 +1,215 @@ +/// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JMESPATH_JMESPATH_ERROR_HPP +#define JSONCONS_JMESPATH_JMESPATH_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace jmespath { + + class jmespath_error : public std::system_error, public virtual json_exception + { + std::size_t line_number_; + std::size_t column_number_; + mutable std::string what_; + public: + jmespath_error(std::error_code ec) + : std::system_error(ec), line_number_(0), column_number_(0) + { + } + jmespath_error(std::error_code ec, const std::string& what_arg) + : std::system_error(ec, what_arg), line_number_(0), column_number_(0) + { + } + jmespath_error(std::error_code ec, std::size_t position) + : std::system_error(ec), line_number_(0), column_number_(position) + { + } + jmespath_error(std::error_code ec, std::size_t line, std::size_t column) + : std::system_error(ec), line_number_(line), column_number_(column) + { + } + jmespath_error(const jmespath_error& other) = default; + + jmespath_error(jmespath_error&& other) = default; + + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::system_error::what()); + if (line_number_ != 0 && column_number_ != 0) + { + what_.append(" at line "); + what_.append(std::to_string(line_number_)); + what_.append(" and column "); + what_.append(std::to_string(column_number_)); + } + else if (column_number_ != 0) + { + what_.append(" at position "); + what_.append(std::to_string(column_number_)); + } + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::system_error::what(); + } + } + else + { + return what_.c_str(); + } + } + + std::size_t line() const noexcept + { + return line_number_; + } + + std::size_t column() const noexcept + { + return column_number_; + } + }; + +enum class jmespath_errc +{ + success = 0, + expected_identifier, + expected_index, + expected_A_Za_Z_, + expected_rbracket, + expected_rparen, + expected_rbrace, + expected_colon, + expected_dot, + expected_or, + expected_and, + expected_multi_select_list, + invalid_number, + invalid_literal, + expected_comparator, + expected_key, + invalid_argument, + unknown_function, + invalid_type, + unexpected_end_of_input, + step_cannot_be_zero, + syntax_error, + invalid_codepoint, + illegal_escaped_character, + unbalanced_parentheses, + unbalanced_braces, + invalid_arity, + identifier_not_found, + expected_index_expression, + unknown_error +}; + +class jmespath_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/jmespath"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case jmespath_errc::expected_identifier: + return "Expected identifier"; + case jmespath_errc::expected_index: + return "Expected index"; + case jmespath_errc::expected_A_Za_Z_: + return "Expected A-Z, a-z, or _"; + case jmespath_errc::expected_rbracket: + return "Expected ]"; + case jmespath_errc::expected_rparen: + return "Expected )"; + case jmespath_errc::expected_rbrace: + return "Expected }"; + case jmespath_errc::expected_colon: + return "Expected :"; + case jmespath_errc::expected_dot: + return "Expected \".\""; + case jmespath_errc::expected_or: + return "Expected \"||\""; + case jmespath_errc::expected_and: + return "Expected \"&&\""; + case jmespath_errc::expected_multi_select_list: + return "Expected multi-select-list"; + case jmespath_errc::invalid_number: + return "Invalid number"; + case jmespath_errc::invalid_literal: + return "Invalid literal"; + case jmespath_errc::expected_comparator: + return "Expected <, <=, ==, >=, > or !="; + case jmespath_errc::expected_key: + return "Expected key"; + case jmespath_errc::invalid_argument: + return "Invalid argument type"; + case jmespath_errc::unknown_function: + return "Unknown function"; + case jmespath_errc::invalid_type: + return "Invalid type"; + case jmespath_errc::unexpected_end_of_input: + return "Unexpected end of jmespath input"; + case jmespath_errc::step_cannot_be_zero: + return "Slice step cannot be zero"; + case jmespath_errc::syntax_error: + return "Syntax error"; + case jmespath_errc::invalid_codepoint: + return "Invalid codepoint"; + case jmespath_errc::illegal_escaped_character: + return "Illegal escaped character"; + case jmespath_errc::unbalanced_parentheses: + return "Unbalanced parentheses"; + case jmespath_errc::unbalanced_braces: + return "Unbalanced braces"; + case jmespath_errc::invalid_arity: + return "Function called with wrong number of arguments"; + case jmespath_errc::identifier_not_found: + return "Identifier not found"; + case jmespath_errc::expected_index_expression: + return "Expected index expression"; + case jmespath_errc::unknown_error: + default: + return "Unknown jmespath parser error"; + } + } +}; + +inline +const std::error_category& jmespath_error_category() +{ + static jmespath_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(jmespath_errc result) +{ + return std::error_code(static_cast(result),jmespath_error_category()); +} + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp new file mode 100644 index 0000000..ab4ace7 --- /dev/null +++ b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp @@ -0,0 +1,579 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATCH_JSONPATCH_HPP +#define JSONCONS_JSONPATCH_JSONPATCH_HPP + +#include +#include +#include +#include // std::min +#include // std::move +#include +#include +#include + +namespace jsoncons { namespace jsonpatch { + +namespace detail { + + template + struct jsonpatch_names + { + static std::basic_string test_name() + { + static std::basic_string name{'t','e','s','t'}; + return name; + } + static std::basic_string add_name() + { + static std::basic_string name{'a','d','d'}; + return name; + } + static std::basic_string remove_name() + { + static std::basic_string name{'r','e','m','o','v','e'}; + return name; + } + static std::basic_string replace_name() + { + static std::basic_string name{'r','e','p','l','a','c','e'}; + return name; + } + static std::basic_string move_name() + { + static std::basic_string name{'m','o','v','e'}; + return name; + } + static std::basic_string copy_name() + { + static std::basic_string name{'c','o','p','y'}; + return name; + } + static std::basic_string op_name() + { + static std::basic_string name{'o','p'}; + return name; + } + static std::basic_string path_name() + { + static std::basic_string name{'p','a','t','h'}; + return name; + } + static std::basic_string from_name() + { + static std::basic_string name{'f','r','o','m'}; + return name; + } + static std::basic_string value_name() + { + static std::basic_string name{'v','a','l','u','e'}; + return name; + } + static std::basic_string dash_name() + { + static std::basic_string name{'-'}; + return name; + } + }; + + template + jsonpointer::basic_json_pointer definite_path(const Json& root, jsonpointer::basic_json_pointer& location) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + auto rit = location.rbegin(); + if (rit == location.rend()) + { + return location; + } + + if (*rit != jsonpatch_names::dash_name()) + { + return location; + } + + std::vector tokens; + for (auto it = location.begin(); it != location.rbegin().base()-1; ++it) + { + tokens.push_back(*it); + } + jsonpointer::basic_json_pointer pointer(tokens); + + std::error_code ec; + + Json val = jsonpointer::get(root, pointer, ec); + if (ec || !val.is_array()) + { + return location; + } + string_type last_token; + jsoncons::detail::from_integer(val.size(), last_token); + tokens.emplace_back(std::move(last_token)); + + return jsonpointer::basic_json_pointer(std::move(tokens)); + } + + enum class op_type {add,remove,replace}; + enum class state_type {begin,abort,commit}; + + template + struct operation_unwinder + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using json_pointer_type = jsonpointer::basic_json_pointer; + + struct entry + { + op_type op; + json_pointer_type path; + Json value; + + entry(op_type op, const json_pointer_type& path, const Json& value) + : op(op), path(path), value(value) + { + } + + entry(const entry&) = default; + + entry(entry&&) = default; + + entry& operator=(const entry&) = default; + + entry& operator=(entry&&) = default; + }; + + Json& target; + state_type state; + std::vector stack; + + operation_unwinder(Json& j) + : target(j), state(state_type::begin) + { + } + + ~operation_unwinder() noexcept + { + std::error_code ec; + if (state != state_type::commit) + { + for (auto it = stack.rbegin(); it != stack.rend(); ++it) + { + if (it->op == op_type::add) + { + jsonpointer::add(target,it->path,it->value,ec); + if (ec) + { + //std::cout << "add: " << it->path << std::endl; + break; + } + } + else if (it->op == op_type::remove) + { + jsonpointer::remove(target,it->path,ec); + if (ec) + { + //std::cout << "remove: " << it->path << std::endl; + break; + } + } + else if (it->op == op_type::replace) + { + jsonpointer::replace(target,it->path,it->value,ec); + if (ec) + { + //std::cout << "replace: " << it->path << std::endl; + break; + } + } + } + } + } + }; + + template + Json from_diff(const Json& source, const Json& target, const typename Json::string_view_type& path) + { + using char_type = typename Json::char_type; + + Json result = typename Json::array(); + + if (source == target) + { + return result; + } + + if (source.is_array() && target.is_array()) + { + std::size_t common = (std::min)(source.size(),target.size()); + for (std::size_t i = 0; i < common; ++i) + { + std::basic_string ss(path); + ss.push_back('/'); + jsoncons::detail::from_integer(i,ss); + auto temp_diff = from_diff(source[i],target[i],ss); + result.insert(result.array_range().end(),temp_diff.array_range().begin(),temp_diff.array_range().end()); + } + // Element in source, not in target - remove + for (std::size_t i = source.size(); i-- > target.size();) + { + std::basic_string ss(path); + ss.push_back('/'); + jsoncons::detail::from_integer(i,ss); + Json val(json_object_arg); + val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::remove_name()); + val.insert_or_assign(jsonpatch_names::path_name(), ss); + result.push_back(std::move(val)); + } + // Element in target, not in source - add, + // Fix contributed by Alexander rog13 + for (std::size_t i = source.size(); i < target.size(); ++i) + { + const auto& a = target[i]; + std::basic_string ss(path); + ss.push_back('/'); + jsoncons::detail::from_integer(i,ss); + Json val(json_object_arg); + val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::add_name()); + val.insert_or_assign(jsonpatch_names::path_name(), ss); + val.insert_or_assign(jsonpatch_names::value_name(), a); + result.push_back(std::move(val)); + } + } + else if (source.is_object() && target.is_object()) + { + for (const auto& a : source.object_range()) + { + std::basic_string ss(path); + ss.push_back('/'); + jsonpointer::escape(a.key(),ss); + auto it = target.find(a.key()); + if (it != target.object_range().end()) + { + auto temp_diff = from_diff(a.value(),it->value(),ss); + result.insert(result.array_range().end(),temp_diff.array_range().begin(),temp_diff.array_range().end()); + } + else + { + Json val(json_object_arg); + val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::remove_name()); + val.insert_or_assign(jsonpatch_names::path_name(), ss); + result.push_back(std::move(val)); + } + } + for (const auto& a : target.object_range()) + { + auto it = source.find(a.key()); + if (it == source.object_range().end()) + { + std::basic_string ss(path); + ss.push_back('/'); + jsonpointer::escape(a.key(),ss); + Json val(json_object_arg); + val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::add_name()); + val.insert_or_assign(jsonpatch_names::path_name(), ss); + val.insert_or_assign(jsonpatch_names::value_name(), a.value()); + result.push_back(std::move(val)); + } + } + } + else + { + Json val(json_object_arg); + val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::replace_name()); + val.insert_or_assign(jsonpatch_names::path_name(), path); + val.insert_or_assign(jsonpatch_names::value_name(), target); + result.push_back(std::move(val)); + } + + return result; + } +} + +template +void apply_patch(Json& target, const Json& patch, std::error_code& ec) +{ + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using json_pointer_type = jsonpointer::basic_json_pointer; + + jsoncons::jsonpatch::detail::operation_unwinder unwinder(target); + std::error_code local_ec; + + // Validate + + for (const auto& operation : patch.array_range()) + { + unwinder.state =jsoncons::jsonpatch::detail::state_type::begin; + + auto it_op = operation.find(detail::jsonpatch_names::op_name()); + if (it_op == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + string_type op = it_op->value().template as(); + + auto it_path = operation.find(detail::jsonpatch_names::path_name()); + if (it_path == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + string_type path = it_path->value().template as(); + auto location = json_pointer_type::parse(path, local_ec); + if (local_ec) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + + if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::test_name()) + { + Json val = jsonpointer::get(target,location,local_ec); + if (local_ec) + { + ec = jsonpatch_errc::test_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + auto it_value = operation.find(detail::jsonpatch_names::value_name()); + if (it_value == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + if (val != it_value->value()) + { + ec = jsonpatch_errc::test_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + } + else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::add_name()) + { + auto it_value = operation.find(detail::jsonpatch_names::value_name()); + if (it_value == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + Json val = it_value->value(); + auto npath = jsonpatch::detail::definite_path(target,location); + + std::error_code insert_ec; + jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace + if (insert_ec) // try a replace + { + std::error_code select_ec; + Json orig_val = jsonpointer::get(target,npath,select_ec); + if (select_ec) // shouldn't happen + { + ec = jsonpatch_errc::add_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + std::error_code replace_ec; + jsonpointer::replace(target,npath,val,replace_ec); + if (replace_ec) + { + ec = jsonpatch_errc::add_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(detail::op_type::replace,npath,orig_val); + } + else // insert without replace succeeded + { + unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null()); + } + } + else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::remove_name()) + { + Json val = jsonpointer::get(target,location,local_ec); + if (local_ec) + { + ec = jsonpatch_errc::remove_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + jsonpointer::remove(target,location,local_ec); + if (local_ec) + { + ec = jsonpatch_errc::remove_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(detail::op_type::add, location, val); + } + else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::replace_name()) + { + Json val = jsonpointer::get(target,location,local_ec); + if (local_ec) + { + ec = jsonpatch_errc::replace_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + auto it_value = operation.find(detail::jsonpatch_names::value_name()); + if (it_value == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + jsonpointer::replace(target, location, it_value->value(), local_ec); + if (local_ec) + { + ec = jsonpatch_errc::replace_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(detail::op_type::replace,location,val); + } + else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::move_name()) + { + auto it_from = operation.find(detail::jsonpatch_names::from_name()); + if (it_from == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + string_type from = it_from->value().as_string(); + auto from_pointer = json_pointer_type::parse(from, local_ec); + if (local_ec) + { + ec = jsonpatch_errc::move_failed; + unwinder.state = jsoncons::jsonpatch::detail::state_type::abort; + return; + } + + Json val = jsonpointer::get(target, from_pointer, local_ec); + if (local_ec) + { + ec = jsonpatch_errc::move_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + jsonpointer::remove(target, from_pointer, local_ec); + if (local_ec) + { + ec = jsonpatch_errc::move_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(detail::op_type::add, from_pointer, val); + // add + std::error_code insert_ec; + auto npath = jsonpatch::detail::definite_path(target,location); + jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace + if (insert_ec) // try a replace + { + std::error_code select_ec; + Json orig_val = jsonpointer::get(target,npath,select_ec); + if (select_ec) // shouldn't happen + { + ec = jsonpatch_errc::copy_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + std::error_code replace_ec; + jsonpointer::replace(target, npath, val, replace_ec); + if (replace_ec) + { + ec = jsonpatch_errc::copy_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(jsoncons::jsonpatch::detail::op_type::replace,npath,orig_val); + } + else + { + unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null()); + } + } + else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names::copy_name()) + { + auto it_from = operation.find(detail::jsonpatch_names::from_name()); + if (it_from == operation.object_range().end()) + { + ec = jsonpatch_errc::invalid_patch; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + string_type from = it_from->value().as_string(); + Json val = jsonpointer::get(target,from,local_ec); + if (local_ec) + { + ec = jsonpatch_errc::copy_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + // add + auto npath = jsonpatch::detail::definite_path(target,location); + std::error_code insert_ec; + jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace + if (insert_ec) // Failed, try a replace + { + std::error_code select_ec; + Json orig_val = jsonpointer::get(target,npath, select_ec); + if (select_ec) // shouldn't happen + { + ec = jsonpatch_errc::copy_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + std::error_code replace_ec; + jsonpointer::replace(target, npath, val,replace_ec); + if (replace_ec) + { + ec = jsonpatch_errc::copy_failed; + unwinder.state =jsoncons::jsonpatch::detail::state_type::abort; + return; + } + unwinder.stack.emplace_back(jsoncons::jsonpatch::detail::op_type::replace,npath,orig_val); + } + else + { + unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null()); + } + } + } + if (unwinder.state ==jsoncons::jsonpatch::detail::state_type::begin) + { + unwinder.state =jsoncons::jsonpatch::detail::state_type::commit; + } +} + +template +Json from_diff(const Json& source, const Json& target) +{ + std::basic_string path; + return jsoncons::jsonpatch::detail::from_diff(source, target, path); +} + +template +void apply_patch(Json& target, const Json& patch) +{ + std::error_code ec; + apply_patch(target, patch, ec); + if (ec) + { + JSONCONS_THROW(jsonpatch_error(ec)); + } +} + +}} + +#endif diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp new file mode 100644 index 0000000..33f8007 --- /dev/null +++ b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp @@ -0,0 +1,121 @@ +/// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATCH_JSONPATCH_ERROR_HPP +#define JSONCONS_JSONPATCH_JSONPATCH_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace jsonpatch { + + enum class jsonpatch_errc + { + success = 0, + invalid_patch = 1, + test_failed, + add_failed, + remove_failed, + replace_failed, + move_failed, + copy_failed + + }; + + class jsonpatch_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/jsonpatch"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case jsonpatch_errc::invalid_patch: + return "Invalid JSON Patch document"; + case jsonpatch_errc::test_failed: + return "JSON Patch test operation failed"; + case jsonpatch_errc::add_failed: + return "JSON Patch add operation failed"; + case jsonpatch_errc::remove_failed: + return "JSON Patch remove operation failed"; + case jsonpatch_errc::replace_failed: + return "JSON Patch replace operation failed"; + case jsonpatch_errc::move_failed: + return "JSON Patch move operation failed"; + case jsonpatch_errc::copy_failed: + return "JSON Patch copy operation failed"; + default: + return "Unknown JSON Patch error"; + } + } + }; + + inline + const std::error_category& jsonpatch_error_category() + { + static jsonpatch_error_category_impl instance; + return instance; + } + + inline + std::error_code make_error_code(jsonpatch_errc result) + { + return std::error_code(static_cast(result),jsonpatch_error_category()); + } + +} // jsonpatch +} // jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace jsoncons { namespace jsonpatch { + +// allow to disable exceptions +#if !defined(JSONCONS_NO_EXCEPTIONS) + #define JSONCONS_THROW(exception) throw exception + #define JSONCONS_RETHROW throw + #define JSONCONS_TRY try + #define JSONCONS_CATCH(exception) catch(exception) +#else + #define JSONCONS_THROW(exception) std::terminate() + #define JSONCONS_RETHROW std::terminate() + #define JSONCONS_TRY if (true) + #define JSONCONS_CATCH(exception) if (false) +#endif + + class jsonpatch_error : public std::system_error, public virtual json_exception + { + public: + jsonpatch_error(const std::error_code& ec) + : std::system_error(ec) + { + } + + jsonpatch_error(const jsonpatch_error& other) = default; + + jsonpatch_error(jsonpatch_error&& other) = default; + + jsonpatch_error& operator=(const jsonpatch_error& e) = default; + jsonpatch_error& operator=(jsonpatch_error&& e) = default; + + const char* what() const noexcept override + { + return std::system_error::what(); + } + }; +} // jsonpatch +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/expression.hpp b/include/jsoncons_ext/jsonpath/expression.hpp new file mode 100644 index 0000000..f655f2d --- /dev/null +++ b/include/jsoncons_ext/jsonpath/expression.hpp @@ -0,0 +1,3329 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_EXPRESSION_HPP +#define JSONCONS_JSONPATH_EXPRESSION_HPP + +#include // std::basic_string +#include // std::vector +#include // std::unordered_map +#include // std::unordered_set +#include // std::numeric_limits +#include // std::set +#include // std::move +#if defined(JSONCONS_HAS_STD_REGEX) +#include +#endif +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { + + struct reference_arg_t + { + explicit reference_arg_t() = default; + }; + constexpr reference_arg_t reference_arg{}; + + struct const_reference_arg_t + { + explicit const_reference_arg_t() = default; + }; + constexpr const_reference_arg_t const_reference_arg{}; + + struct literal_arg_t + { + explicit literal_arg_t() = default; + }; + constexpr literal_arg_t literal_arg{}; + + struct end_of_expression_arg_t + { + explicit end_of_expression_arg_t() = default; + }; + constexpr end_of_expression_arg_t end_of_expression_arg{}; + + struct separator_arg_t + { + explicit separator_arg_t() = default; + }; + constexpr separator_arg_t separator_arg{}; + + struct lparen_arg_t + { + explicit lparen_arg_t() = default; + }; + constexpr lparen_arg_t lparen_arg{}; + + struct rparen_arg_t + { + explicit rparen_arg_t() = default; + }; + constexpr rparen_arg_t rparen_arg{}; + + struct begin_union_arg_t + { + explicit begin_union_arg_t() = default; + }; + constexpr begin_union_arg_t begin_union_arg{}; + + struct end_union_arg_t + { + explicit end_union_arg_t() = default; + }; + constexpr end_union_arg_t end_union_arg{}; + + struct begin_filter_arg_t + { + explicit begin_filter_arg_t() = default; + }; + constexpr begin_filter_arg_t begin_filter_arg{}; + + struct end_filter_arg_t + { + explicit end_filter_arg_t() = default; + }; + constexpr end_filter_arg_t end_filter_arg{}; + + struct begin_expression_arg_t + { + explicit begin_expression_arg_t() = default; + }; + constexpr begin_expression_arg_t begin_expression_arg{}; + + struct end_index_expression_arg_t + { + explicit end_index_expression_arg_t() = default; + }; + constexpr end_index_expression_arg_t end_index_expression_arg{}; + + struct end_argument_expression_arg_t + { + explicit end_argument_expression_arg_t() = default; + }; + constexpr end_argument_expression_arg_t end_argument_expression_arg{}; + + struct current_node_arg_t + { + explicit current_node_arg_t() = default; + }; + constexpr current_node_arg_t current_node_arg{}; + + struct root_node_arg_t + { + explicit root_node_arg_t() = default; + }; + constexpr root_node_arg_t root_node_arg{}; + + struct end_function_arg_t + { + explicit end_function_arg_t() = default; + }; + constexpr end_function_arg_t end_function_arg{}; + + struct argument_arg_t + { + explicit argument_arg_t() = default; + }; + constexpr argument_arg_t argument_arg{}; + + enum class result_options {value=0, nodups=1, sort=2, path=4}; + + using result_type = result_options; + + inline result_options operator~(result_options a) + { + return static_cast(~static_cast(a)); + } + + inline result_options operator&(result_options a, result_options b) + { + return static_cast(static_cast(a) & static_cast(b)); + } + + inline result_options operator^(result_options a, result_options b) + { + return static_cast(static_cast(a) ^ static_cast(b)); + } + + inline result_options operator|(result_options a, result_options b) + { + return static_cast(static_cast(a) | static_cast(b)); + } + + inline result_options operator&=(result_options& a, result_options b) + { + a = a & b; + return a; + } + + inline result_options operator^=(result_options& a, result_options b) + { + a = a ^ b; + return a; + } + + inline result_options operator|=(result_options& a, result_options b) + { + a = a | b; + return a; + } + + template + class parameter; + + template + class value_or_pointer + { + public: + friend class parameter; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + private: + bool is_value_; + union + { + value_type val_; + pointer ptr_; + }; + public: + value_or_pointer(value_type&& val) + : is_value_(true), val_(std::move(val)) + { + } + + value_or_pointer(pointer ptr) + : is_value_(false), ptr_(std::move(ptr)) + { + } + + value_or_pointer(value_or_pointer&& other) noexcept + : is_value_(other.is_value_) + { + if (is_value_) + { + new(&val_)value_type(std::move(other.val_)); + } + else + { + ptr_ = other.ptr_; + } + } + + ~value_or_pointer() noexcept + { + if (is_value_) + { + val_.~value_type(); + } + } + + value_or_pointer& operator=(value_or_pointer&& other) noexcept + { + if (is_value_) + { + val_.~value_type(); + } + is_value_ = other.is_value_; + + if (is_value_) + { + new(&val_)value_type(std::move(other.val_)); + } + else + { + ptr_ = other.ptr_; + } + return *this; + } + + reference value() + { + return is_value_ ? val_ : *ptr_; + } + + pointer ptr() + { + return is_value_ ? &val_ : ptr_; + } + }; + + template + class parameter + { + using value_type = Json; + using reference = const Json&; + using pointer = const Json*; + private: + value_or_pointer data_; + public: + template + parameter(value_or_pointer&& data) noexcept + : data_(nullptr) + { + data_.is_value_ = data.is_value_; + if (data.is_value_) + { + data_.val_ = std::move(data.val_); + } + else + { + data_.ptr_ = data.ptr_; + } + } + + parameter(parameter&& other) noexcept = default; + + parameter& operator=(parameter&& other) noexcept = default; + + const Json& value() const + { + return data_.is_value_ ? data_.val_ : *data_.ptr_; + } + }; + + template + class custom_function + { + public: + using value_type = Json; + using char_type = typename Json::char_type; + using parameter_type = parameter; + using function_type = std::function, std::error_code& ec)>; + using string_type = std::basic_string; + + string_type function_name_; + optional arity_; + function_type f_; + + custom_function(const string_type& function_name, + const optional& arity, + const function_type& f) + : function_name_(function_name), + arity_(arity), + f_(f) + { + } + + custom_function(string_type&& function_name, + optional&& arity, + function_type&& f) + : function_name_(std::move(function_name)), + arity_(std::move(arity)), + f_(std::move(f)) + { + } + + custom_function(const custom_function&) = default; + + custom_function(custom_function&&) = default; + + const string_type& name() const + { + return function_name_; + } + + optional arity() const + { + return arity_; + } + + const function_type& function() const + { + return f_; + } + }; + + template + class custom_functions + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using value_type = Json; + using parameter_type = parameter; + using function_type = std::function, std::error_code& ec)>; + using const_iterator = typename std::vector>::const_iterator; + + std::vector> functions_; + public: + void register_function(const string_type& name, + jsoncons::optional arity, + const function_type& f) + { + functions_.emplace_back(name, arity, f); + } + + const_iterator begin() const + { + return functions_.begin(); + } + + const_iterator end() const + { + return functions_.end(); + } + }; + +namespace detail { + + template + class dynamic_resources; + + template + struct unary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + + unary_operator(std::size_t precedence_level, + bool is_right_associative) + : precedence_level_(precedence_level), + is_right_associative_(is_right_associative) + { + } + + virtual ~unary_operator() = default; + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual Json evaluate(JsonReference, + std::error_code&) const = 0; + }; + + template + bool is_false(const Json& val) + { + return ((val.is_array() && val.empty()) || + (val.is_object() && val.empty()) || + (val.is_string() && val.as_string_view().empty()) || + (val.is_bool() && !val.as_bool()) || + val.is_null()); + } + + template + bool is_true(const Json& val) + { + return !is_false(val); + } + + template + class unary_not_operator final : public unary_operator + { + public: + unary_not_operator() + : unary_operator(1, true) + {} + + Json evaluate(JsonReference val, + std::error_code&) const override + { + return is_false(val) ? Json(true) : Json(false); + } + }; + + template + class unary_minus_operator final : public unary_operator + { + public: + unary_minus_operator() + : unary_operator(1, true) + {} + + Json evaluate(JsonReference val, + std::error_code&) const override + { + if (val.is_int64()) + { + return Json(-val.template as()); + } + else if (val.is_double()) + { + return Json(-val.as_double()); + } + else + { + return Json::null(); + } + } + }; + + template + class regex_operator final : public unary_operator + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + std::basic_regex pattern_; + public: + regex_operator(std::basic_regex&& pattern) + : unary_operator(2, true), + pattern_(std::move(pattern)) + { + } + + regex_operator(regex_operator&&) = default; + regex_operator& operator=(regex_operator&&) = default; + + Json evaluate(JsonReference val, + std::error_code&) const override + { + if (!val.is_string()) + { + return Json::null(); + } + return std::regex_search(val.as_string(), pattern_) ? Json(true) : Json(false); + } + }; + + template + struct binary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + + binary_operator(std::size_t precedence_level, + bool is_right_associative = false) + : precedence_level_(precedence_level), + is_right_associative_(is_right_associative) + { + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual Json evaluate(JsonReference, + JsonReference, + + std::error_code&) const = 0; + + virtual std::string to_string(int = 0) const + { + return "binary operator"; + } + + protected: + ~binary_operator() = default; + }; + + // Implementations + + template + class or_operator final : public binary_operator + { + public: + or_operator() + : binary_operator(9) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (lhs.is_null() && rhs.is_null()) + { + return Json::null(); + } + if (!is_false(lhs)) + { + return lhs; + } + else + { + return rhs; + } + } + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + //s.append("\n"); + s.append(level*2, ' '); + } + s.append("or operator"); + return s; + } + }; + + template + class and_operator final : public binary_operator + { + public: + and_operator() + : binary_operator(8) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (is_true(lhs)) + { + return rhs; + } + else + { + return lhs; + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("and operator"); + return s; + } + }; + + template + class eq_operator final : public binary_operator + { + public: + eq_operator() + : binary_operator(6) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + return lhs == rhs ? Json(true) : Json(false); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("equal operator"); + return s; + } + }; + + template + class ne_operator final : public binary_operator + { + public: + ne_operator() + : binary_operator(6) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + return lhs != rhs ? Json(true) : Json(false); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("not equal operator"); + return s; + } + }; + + template + class lt_operator final : public binary_operator + { + public: + lt_operator() + : binary_operator(5) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (lhs.is_number() && rhs.is_number()) + { + return lhs < rhs ? Json(true) : Json(false); + } + else if (lhs.is_string() && rhs.is_string()) + { + return lhs < rhs ? Json(true) : Json(false); + } + return Json::null(); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("less than operator"); + return s; + } + }; + + template + class lte_operator final : public binary_operator + { + public: + lte_operator() + : binary_operator(5) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (lhs.is_number() && rhs.is_number()) + { + return lhs <= rhs ? Json(true) : Json(false); + } + else if (lhs.is_string() && rhs.is_string()) + { + return lhs <= rhs ? Json(true) : Json(false); + } + return Json::null(); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("less than or equal operator"); + return s; + } + }; + + template + class gt_operator final : public binary_operator + { + public: + gt_operator() + : binary_operator(5) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + //std::cout << "operator> lhs: " << lhs << ", rhs: " << rhs << "\n"; + + if (lhs.is_number() && rhs.is_number()) + { + return lhs > rhs ? Json(true) : Json(false); + } + else if (lhs.is_string() && rhs.is_string()) + { + return lhs > rhs ? Json(true) : Json(false); + } + return Json::null(); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("greater than operator"); + return s; + } + }; + + template + class gte_operator final : public binary_operator + { + public: + gte_operator() + : binary_operator(5) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (lhs.is_number() && rhs.is_number()) + { + return lhs >= rhs ? Json(true) : Json(false); + } + else if (lhs.is_string() && rhs.is_string()) + { + return lhs >= rhs ? Json(true) : Json(false); + } + return Json::null(); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("greater than or equal operator"); + return s; + } + }; + + template + class plus_operator final : public binary_operator + { + public: + plus_operator() + : binary_operator(4) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return Json::null(); + } + else if (lhs.is_int64() && rhs.is_int64()) + { + return Json(((lhs.template as() + rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() + rhs.template as())); + } + else + { + return Json((lhs.as_double() + rhs.as_double())); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("plus operator"); + return s; + } + }; + + template + class minus_operator final : public binary_operator + { + public: + minus_operator() + : binary_operator(4) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return Json::null(); + } + else if (lhs.is_int64() && rhs.is_int64()) + { + return Json(((lhs.template as() - rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() - rhs.template as())); + } + else + { + return Json((lhs.as_double() - rhs.as_double())); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("minus operator"); + return s; + } + }; + + template + class mult_operator final : public binary_operator + { + public: + mult_operator() + : binary_operator(3) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return Json::null(); + } + else if (lhs.is_int64() && rhs.is_int64()) + { + return Json(((lhs.template as() * rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() * rhs.template as())); + } + else + { + return Json((lhs.as_double() * rhs.as_double())); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("multiply operator"); + return s; + } + }; + + template + class div_operator final : public binary_operator + { + public: + div_operator() + : binary_operator(3) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n"; + + if (!(lhs.is_number() && rhs.is_number())) + { + return Json::null(); + } + else if (lhs.is_int64() && rhs.is_int64()) + { + return Json(((lhs.template as() / rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() / rhs.template as())); + } + else + { + return Json((lhs.as_double() / rhs.as_double())); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("divide operator"); + return s; + } + }; + + template + class modulus_operator final : public binary_operator + { + public: + modulus_operator() + : binary_operator(3) + { + } + + Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override + { + //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n"; + + if (!(lhs.is_number() && rhs.is_number())) + { + return Json::null(); + } + else if (lhs.is_int64() && rhs.is_int64()) + { + return Json(((lhs.template as() % rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() % rhs.template as())); + } + else + { + return Json(fmod(lhs.as_double(), rhs.as_double())); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("modulus operator"); + return s; + } + }; + + // function_base + template + class function_base + { + jsoncons::optional arg_count_; + public: + using value_type = Json; + using parameter_type = parameter; + + function_base(jsoncons::optional arg_count) + : arg_count_(arg_count) + { + } + + virtual ~function_base() noexcept = default; + + jsoncons::optional arity() const + { + return arg_count_; + } + + virtual value_type evaluate(const std::vector& args, + std::error_code& ec) const = 0; + + virtual std::string to_string(int level = 0) const + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("function"); + return s; + } + }; + + template + class decorator_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + using function_type = std::function, std::error_code& ec)>; + private: + function_type f_; + public: + decorator_function(jsoncons::optional arity, + const function_type& f) + : function_base(arity), f_(f) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + return f_(args, ec); + } + }; + + template + class contains_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + contains_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + auto arg1= args[1].value(); + + switch (arg0.type()) + { + case json_type::array_value: + for (auto& j : arg0.array_range()) + { + if (j == arg1) + { + return value_type(true); + } + } + return value_type(false); + case json_type::string_value: + { + if (!arg1.is_string()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + return sv0.find(sv1) != string_view_type::npos ? value_type(true) : value_type(false); + } + default: + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("contains function"); + return s; + } + }; + + template + class ends_with_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + ends_with_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_string()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + auto arg1= args[1].value(); + if (!arg1.is_string()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + if (sv1.length() <= sv0.length() && sv1 == sv0.substr(sv0.length() - sv1.length())) + { + return value_type(true); + } + else + { + return value_type(false); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("ends_with function"); + return s; + } + }; + + template + class starts_with_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + starts_with_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_string()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + auto arg1= args[1].value(); + if (!arg1.is_string()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + if (sv1.length() <= sv0.length() && sv1 == sv0.substr(0, sv1.length())) + { + return value_type(true); + } + else + { + return value_type(false); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("starts_with function"); + return s; + } + }; + + template + class sum_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + sum_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_array()) + { + //std::cout << "arg: " << arg0 << "\n"; + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + //std::cout << "sum function arg: " << arg0 << "\n"; + + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + sum += j.template as(); + } + + return value_type(sum); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("sum function"); + return s; + } + }; + +#if defined(JSONCONS_HAS_STD_REGEX) + + template + class tokenize_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + tokenize_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + if (!args[0].value().is_string() || !args[1].value().is_string()) + { + //std::cout << "arg: " << arg0 << "\n"; + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + auto arg0 = args[0].value().template as(); + auto arg1 = args[1].value().template as(); + + std::regex::flag_type options = std::regex_constants::ECMAScript; + std::basic_regex pieces_regex(arg1, options); + + std::regex_token_iterator rit ( arg0.begin(), arg0.end(), pieces_regex, -1); + std::regex_token_iterator rend; + + value_type j(json_array_arg); + while (rit != rend) + { + j.emplace_back(rit->str()); + ++rit; + } + return j; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("tokenize function"); + return s; + } + }; + +#endif // defined(JSONCONS_HAS_STD_REGEX) + + template + class ceil_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + ceil_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return value_type(arg0.template as()); + } + case json_type::double_value: + { + return value_type(std::ceil(arg0.template as())); + } + default: + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("ceil function"); + return s; + } + }; + + template + class floor_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + floor_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return value_type(arg0.template as()); + } + case json_type::double_value: + { + return value_type(std::floor(arg0.template as())); + } + default: + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("floor function"); + return s; + } + }; + + template + class to_number_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + to_number_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + switch (arg0.type()) + { + case json_type::int64_value: + case json_type::uint64_value: + case json_type::double_value: + return arg0; + case json_type::string_value: + { + auto sv = arg0.as_string_view(); + uint64_t un{0}; + auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), un); + if (result1) + { + return value_type(un); + } + int64_t sn{0}; + auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sn); + if (result2) + { + return value_type(sn); + } + jsoncons::detail::chars_to to_double; + try + { + auto s = arg0.as_string(); + double d = to_double(s.c_str(), s.length()); + return value_type(d); + } + catch (const std::exception&) + { + return value_type::null(); + } + } + default: + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("to_number function"); + return s; + } + }; + + template + class prod_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + prod_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_array() || arg0.empty()) + { + //std::cout << "arg: " << arg0 << "\n"; + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + double prod = 1; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + prod *= j.template as(); + } + + return value_type(prod); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("prod function"); + return s; + } + }; + + template + class avg_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + avg_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_array()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + if (arg0.empty()) + { + return value_type::null(); + } + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + sum += j.template as(); + } + + return value_type(sum / static_cast(arg0.size())); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("to_string function"); + return s; + } + }; + + template + class min_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + min_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_array()) + { + //std::cout << "arg: " << arg0 << "\n"; + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + if (arg0.empty()) + { + return value_type::null(); + } + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + if (arg0.at(i) < arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("min function"); + return s; + } + }; + + template + class max_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + max_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_array()) + { + //std::cout << "arg: " << arg0 << "\n"; + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + if (arg0.empty()) + { + return value_type::null(); + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + if (arg0.at(i) > arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("max function"); + return s; + } + }; + + template + class abs_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + abs_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + return arg0; + case json_type::int64_value: + { + return arg0.template as() >= 0 ? arg0 : value_type(std::abs(arg0.template as())); + } + case json_type::double_value: + { + return arg0.template as() >= 0 ? arg0 : value_type(std::abs(arg0.template as())); + } + default: + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("abs function"); + return s; + } + }; + + template + class length_function : public function_base + { + public: + using value_type = Json; + using string_view_type = typename Json::string_view_type; + using parameter_type = parameter; + + length_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + //std::cout << "length function arg: " << arg0 << "\n"; + + switch (arg0.type()) + { + case json_type::object_value: + case json_type::array_value: + return value_type(arg0.size()); + case json_type::string_value: + { + auto sv0 = arg0.template as(); + auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size()); + return value_type(length); + } + default: + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("length function"); + return s; + } + }; + + template + class keys_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + keys_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + if (args.size() != *this->arity()) + { + ec = jsonpath_errc::invalid_arity; + return value_type::null(); + } + + auto arg0= args[0].value(); + if (!arg0.is_object()) + { + ec = jsonpath_errc::invalid_type; + return value_type::null(); + } + + value_type result(json_array_arg); + result.reserve(args.size()); + + for (auto& item : arg0.object_range()) + { + result.emplace_back(item.key()); + } + return result; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("keys function"); + return s; + } + }; + + enum class jsonpath_token_kind + { + root_node, + current_node, + expression, + lparen, + rparen, + begin_union, + end_union, + begin_filter, + end_filter, + begin_expression, + end_index_expression, + end_argument_expression, + separator, + literal, + selector, + function, + end_function, + argument, + unary_operator, + binary_operator + }; + + inline + std::string to_string(jsonpath_token_kind kind) + { + switch (kind) + { + case jsonpath_token_kind::root_node: + return "root_node"; + case jsonpath_token_kind::current_node: + return "current_node"; + case jsonpath_token_kind::lparen: + return "lparen"; + case jsonpath_token_kind::rparen: + return "rparen"; + case jsonpath_token_kind::begin_union: + return "begin_union"; + case jsonpath_token_kind::end_union: + return "end_union"; + case jsonpath_token_kind::begin_filter: + return "begin_filter"; + case jsonpath_token_kind::end_filter: + return "end_filter"; + case jsonpath_token_kind::begin_expression: + return "begin_expression"; + case jsonpath_token_kind::end_index_expression: + return "end_index_expression"; + case jsonpath_token_kind::end_argument_expression: + return "end_argument_expression"; + case jsonpath_token_kind::separator: + return "separator"; + case jsonpath_token_kind::literal: + return "literal"; + case jsonpath_token_kind::selector: + return "selector"; + case jsonpath_token_kind::function: + return "function"; + case jsonpath_token_kind::end_function: + return "end_function"; + case jsonpath_token_kind::argument: + return "argument"; + case jsonpath_token_kind::unary_operator: + return "unary_operator"; + case jsonpath_token_kind::binary_operator: + return "binary_operator"; + default: + return ""; + } + } + + template + struct path_value_pair + { + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using value_type = Json; + using reference = JsonReference; + using value_pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using path_pointer = const json_location_node_type*; + + json_location_type path_; + value_pointer value_ptr_; + + path_value_pair(const json_location_type& path, reference value) noexcept + : path_(path), value_ptr_(std::addressof(value)) + { + } + + path_value_pair(json_location_type&& path, value_pointer valp) noexcept + : path_(std::move(path)), value_ptr_(valp) + { + } + + path_value_pair(const path_value_pair&) = default; + path_value_pair(path_value_pair&& other) = default; + path_value_pair& operator=(const path_value_pair&) = default; + path_value_pair& operator=(path_value_pair&& other) = default; + + json_location_type path() const + { + return path_; + } + + reference value() + { + return *value_ptr_; + } + }; + + template + struct path_value_pair_less + { + bool operator()(const path_value_pair& lhs, + const path_value_pair& rhs) const noexcept + { + return lhs.path() < rhs.path(); + } + }; + + template + struct path_value_pair_equal + { + bool operator()(const path_value_pair& lhs, + const path_value_pair& rhs) const noexcept + { + return lhs.path() == rhs.path(); + } + }; + + template + struct path_component_value_pair + { + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using value_type = Json; + using reference = JsonReference; + using value_pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using path_pointer = const json_location_node_type*; + private: + const json_location_node_type* last_ptr_; + value_pointer value_ptr_; + public: + path_component_value_pair(const json_location_node_type& last, reference value) noexcept + : last_ptr_(std::addressof(last)), value_ptr_(std::addressof(value)) + { + } + + const json_location_node_type& last() const + { + return *last_ptr_; + } + + reference value() const + { + return *value_ptr_; + } + }; + + template + class node_receiver + { + public: + using char_type = typename Json::char_type; + using reference = JsonReference; + using json_location_node_type = json_location_node; + + virtual ~node_receiver() noexcept = default; + + virtual void add(const json_location_node_type& path_tail, + reference value) = 0; + }; + + template + class path_value_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using path_value_pair_type = path_value_pair; + + std::vector nodes; + + void add(const json_location_node_type& path_tail, + reference value) override + { + nodes.emplace_back(json_location_type(path_tail), std::addressof(value)); + } + }; + + template + class path_stem_value_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using path_stem_value_pair_type = path_component_value_pair; + + std::vector nodes; + + void add(const json_location_node_type& path_tail, + reference value) override + { + nodes.emplace_back(path_tail, value); + } + }; + + template + class dynamic_resources + { + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using path_stem_value_pair_type = path_component_value_pair; + std::vector> temp_json_values_; + std::vector> temp_path_node_values_; + std::unordered_map cache_; + public: + bool is_cached(std::size_t id) const + { + return cache_.find(id) != cache_.end(); + } + void add_to_cache(std::size_t id, reference val) + { + cache_.emplace(id, std::addressof(val)); + } + reference retrieve_from_cache(std::size_t id) + { + return *cache_[id]; + } + + reference null_value() + { + static Json j{ null_type{} }; + return j; + } + + template + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + Json* ptr = temp.get(); + temp_json_values_.emplace_back(std::move(temp)); + return ptr; + } + + const json_location_node_type& root_path_node() const + { + static json_location_node_type root('$'); + return root; + } + + const json_location_node_type& current_path_node() const + { + static json_location_node_type root('@'); + return root; + } + + template + const json_location_node_type* create_path_node(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + json_location_node_type* ptr = temp.get(); + temp_path_node_values_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template + struct node_less + { + bool operator()(const path_value_pair& a, const path_value_pair& b) const + { + return *(a.ptr) < *(b.ptr); + } + }; + + template + class jsonpath_selector + { + bool is_path_; + std::size_t precedence_level_; + + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = jsoncons::basic_string_view>; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using path_value_pair_type = path_value_pair; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using node_receiver_type = node_receiver; + using selector_type = jsonpath_selector; + + jsonpath_selector(bool is_path, + std::size_t precedence_level = 0) + : is_path_(is_path), + precedence_level_(precedence_level) + { + } + + virtual ~jsonpath_selector() noexcept = default; + + bool is_path() const + { + return is_path_; + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + + bool is_right_associative() const + { + return true; + } + + virtual void select(dynamic_resources& resources, + reference root, + const json_location_node_type& path_tail, + reference val, + node_receiver_type& receiver, + result_options options) const = 0; + + virtual reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& path_tail, + reference current, + result_options options, + std::error_code& ec) const = 0; + + virtual void append_selector(jsonpath_selector*) + { + } + + virtual std::string to_string(int = 0) const + { + return std::string(); + } + }; + + template + struct static_resources + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using value_type = Json; + using reference = JsonReference; + using function_base_type = function_base; + using selector_type = jsonpath_selector; + + std::vector> selectors_; + std::vector> temp_json_values_; + std::vector>> unary_operators_; + std::unordered_map> custom_functions_; + + static_resources() + { + } + + static_resources(const custom_functions& functions) + { + for (const auto& item : functions) + { + custom_functions_.emplace(item.name(), + jsoncons::make_unique>(item.arity(),item.function())); + } + } + + static_resources(const static_resources&) = default; + + static_resources(static_resources&& other) noexcept + : selectors_(std::move(other.selectors_)), + temp_json_values_(std::move(other.temp_json_values_)), + unary_operators_(std::move(other.unary_operators_)), + custom_functions_(std::move(other.custom_functions_)) + { + } + + const function_base_type* get_function(const string_type& name, std::error_code& ec) const + { + static abs_function abs_func; + static contains_function contains_func; + static starts_with_function starts_with_func; + static ends_with_function ends_with_func; + static ceil_function ceil_func; + static floor_function floor_func; + static to_number_function to_number_func; + static sum_function sum_func; + static prod_function prod_func; + static avg_function avg_func; + static min_function min_func; + static max_function max_func; + static length_function length_func; + static keys_function keys_func; +#if defined(JSONCONS_HAS_STD_REGEX) + static tokenize_function tokenize_func; +#endif + + static std::unordered_map functions = + { + {string_type{'a','b','s'}, &abs_func}, + {string_type{'c','o','n','t','a','i','n','s'}, &contains_func}, + {string_type{'s','t','a','r','t','s','_','w','i','t','h'}, &starts_with_func}, + {string_type{'e','n','d','s','_','w','i','t','h'}, &ends_with_func}, + {string_type{'c','e','i','l'}, &ceil_func}, + {string_type{'f','l','o','o','r'}, &floor_func}, + {string_type{'t','o','_','n','u','m','b','e','r'}, &to_number_func}, + {string_type{'s','u','m'}, &sum_func}, + {string_type{'p','r','o', 'd'}, &prod_func}, + {string_type{'a','v','g'}, &avg_func}, + {string_type{'m','i','n'}, &min_func}, + {string_type{'m','a','x'}, &max_func}, + {string_type{'l','e','n','g','t','h'}, &length_func}, + {string_type{'k','e','y','s'}, &keys_func}, +#if defined(JSONCONS_HAS_STD_REGEX) + {string_type{'t','o','k','e','n','i','z','e'}, &tokenize_func}, +#endif + {string_type{'c','o','u','n','t'}, &length_func} + }; + + auto it = functions.find(name); + if (it == functions.end()) + { + auto it2 = custom_functions_.find(name); + if (it2 == custom_functions_.end()) + { + ec = jsonpath_errc::unknown_function; + return nullptr; + } + else + { + return it2->second.get(); + } + } + else + { + return it->second; + } + } + + const unary_operator* get_unary_not() const + { + static unary_not_operator oper; + return &oper; + } + + const unary_operator* get_unary_minus() const + { + static unary_minus_operator oper; + return &oper; + } + + const unary_operator* get_regex_operator(std::basic_regex&& pattern) + { + unary_operators_.push_back(jsoncons::make_unique>(std::move(pattern))); + return unary_operators_.back().get(); + } + + const binary_operator* get_or_operator() const + { + static or_operator oper; + + return &oper; + } + + const binary_operator* get_and_operator() const + { + static and_operator oper; + + return &oper; + } + + const binary_operator* get_eq_operator() const + { + static eq_operator oper; + return &oper; + } + + const binary_operator* get_ne_operator() const + { + static ne_operator oper; + return &oper; + } + + const binary_operator* get_lt_operator() const + { + static lt_operator oper; + return &oper; + } + + const binary_operator* get_lte_operator() const + { + static lte_operator oper; + return &oper; + } + + const binary_operator* get_gt_operator() const + { + static gt_operator oper; + return &oper; + } + + const binary_operator* get_gte_operator() const + { + static gte_operator oper; + return &oper; + } + + const binary_operator* get_plus_operator() const + { + static plus_operator oper; + return &oper; + } + + const binary_operator* get_minus_operator() const + { + static minus_operator oper; + return &oper; + } + + const binary_operator* get_mult_operator() const + { + static mult_operator oper; + return &oper; + } + + const binary_operator* get_div_operator() const + { + static div_operator oper; + return &oper; + } + + const binary_operator* get_modulus_operator() const + { + static modulus_operator oper; + return &oper; + } + + template + selector_type* new_selector(T&& val) + { + selectors_.emplace_back(jsoncons::make_unique(std::forward(val))); + return selectors_.back().get(); + } + + template + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + Json* ptr = temp.get(); + temp_json_values_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template + class expression_base + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = jsoncons::basic_string_view>; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using path_value_pair_type = path_value_pair; + using json_location_node_type = json_location_node; + + virtual ~expression_base() noexcept = default; + + virtual value_type evaluate(dynamic_resources& resources, + reference root, + //const json_location_node_type& path, + reference val, + result_options options, + std::error_code& ec) const = 0; + + virtual std::string to_string(int level = 0) const = 0; + }; + + template + class token + { + public: + using selector_type = jsonpath_selector; + using expression_base_type = expression_base; + + jsonpath_token_kind token_kind_; + + union + { + selector_type* selector_; + std::unique_ptr expression_; + const unary_operator* unary_operator_; + const binary_operator* binary_operator_; + const function_base* function_; + Json value_; + }; + public: + + token(const unary_operator* expr) noexcept + : token_kind_(jsonpath_token_kind::unary_operator), + unary_operator_(expr) + { + } + + token(const binary_operator* expr) noexcept + : token_kind_(jsonpath_token_kind::binary_operator), + binary_operator_(expr) + { + } + + token(current_node_arg_t) noexcept + : token_kind_(jsonpath_token_kind::current_node) + { + } + + token(root_node_arg_t) noexcept + : token_kind_(jsonpath_token_kind::root_node) + { + } + + token(end_function_arg_t) noexcept + : token_kind_(jsonpath_token_kind::end_function) + { + } + + token(separator_arg_t) noexcept + : token_kind_(jsonpath_token_kind::separator) + { + } + + token(lparen_arg_t) noexcept + : token_kind_(jsonpath_token_kind::lparen) + { + } + + token(rparen_arg_t) noexcept + : token_kind_(jsonpath_token_kind::rparen) + { + } + + token(begin_union_arg_t) noexcept + : token_kind_(jsonpath_token_kind::begin_union) + { + } + + token(end_union_arg_t) noexcept + : token_kind_(jsonpath_token_kind::end_union) + { + } + + token(begin_filter_arg_t) noexcept + : token_kind_(jsonpath_token_kind::begin_filter) + { + } + + token(end_filter_arg_t) noexcept + : token_kind_(jsonpath_token_kind::end_filter) + { + } + + token(begin_expression_arg_t) noexcept + : token_kind_(jsonpath_token_kind::begin_expression) + { + } + + token(end_index_expression_arg_t) noexcept + : token_kind_(jsonpath_token_kind::end_index_expression) + { + } + + token(end_argument_expression_arg_t) noexcept + : token_kind_(jsonpath_token_kind::end_argument_expression) + { + } + + token(selector_type* selector) + : token_kind_(jsonpath_token_kind::selector), selector_(selector) + { + } + + token(std::unique_ptr&& expr) + : token_kind_(jsonpath_token_kind::expression) + { + new (&expression_) std::unique_ptr(std::move(expr)); + } + + token(const function_base* function) noexcept + : token_kind_(jsonpath_token_kind::function), + function_(function) + { + } + + token(argument_arg_t) noexcept + : token_kind_(jsonpath_token_kind::argument) + { + } + + token(literal_arg_t, Json&& value) noexcept + : token_kind_(jsonpath_token_kind::literal), value_(std::move(value)) + { + } + + token(token&& other) noexcept + { + construct(std::forward(other)); + } + + const Json& get_value(const_reference_arg_t, dynamic_resources&) const + { + return value_; + } + + Json& get_value(reference_arg_t, dynamic_resources& resources) const + { + return *resources.create_json(value_); + } + + token& operator=(token&& other) + { + if (&other != this) + { + if (token_kind_ == other.token_kind_) + { + switch (token_kind_) + { + case jsonpath_token_kind::selector: + selector_ = other.selector_; + break; + case jsonpath_token_kind::expression: + expression_ = std::move(other.expression_); + break; + case jsonpath_token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case jsonpath_token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case jsonpath_token_kind::function: + function_ = other.function_; + break; + case jsonpath_token_kind::literal: + value_ = std::move(other.value_); + break; + default: + break; + } + } + else + { + destroy(); + construct(std::forward(other)); + } + } + return *this; + } + + ~token() noexcept + { + destroy(); + } + + jsonpath_token_kind token_kind() const + { + return token_kind_; + } + + bool is_lparen() const + { + return token_kind_ == jsonpath_token_kind::lparen; + } + + bool is_rparen() const + { + return token_kind_ == jsonpath_token_kind::rparen; + } + + bool is_current_node() const + { + return token_kind_ == jsonpath_token_kind::current_node; + } + + bool is_path() const + { + return token_kind_ == jsonpath_token_kind::selector && selector_->is_path(); + } + + bool is_operator() const + { + return token_kind_ == jsonpath_token_kind::unary_operator || + token_kind_ == jsonpath_token_kind::binary_operator; + } + + std::size_t precedence_level() const + { + switch(token_kind_) + { + case jsonpath_token_kind::selector: + return selector_->precedence_level(); + case jsonpath_token_kind::unary_operator: + return unary_operator_->precedence_level(); + case jsonpath_token_kind::binary_operator: + return binary_operator_->precedence_level(); + default: + return 0; + } + } + + jsoncons::optional arity() const + { + return token_kind_ == jsonpath_token_kind::function ? function_->arity() : jsoncons::optional(); + } + + bool is_right_associative() const + { + switch(token_kind_) + { + case jsonpath_token_kind::selector: + return selector_->is_right_associative(); + case jsonpath_token_kind::unary_operator: + return unary_operator_->is_right_associative(); + case jsonpath_token_kind::binary_operator: + return binary_operator_->is_right_associative(); + default: + return false; + } + } + + void construct(token&& other) + { + token_kind_ = other.token_kind_; + switch (token_kind_) + { + case jsonpath_token_kind::selector: + selector_ = other.selector_; + break; + case jsonpath_token_kind::expression: + new (&expression_) std::unique_ptr(std::move(other.expression_)); + break; + case jsonpath_token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case jsonpath_token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case jsonpath_token_kind::function: + function_ = other.function_; + break; + case jsonpath_token_kind::literal: + new (&value_) Json(std::move(other.value_)); + break; + default: + break; + } + } + + void destroy() noexcept + { + switch(token_kind_) + { + case jsonpath_token_kind::expression: + expression_.~unique_ptr(); + break; + case jsonpath_token_kind::literal: + value_.~Json(); + break; + default: + break; + } + } + + std::string to_string(int level = 0) const + { + std::string s; + switch (token_kind_) + { + case jsonpath_token_kind::root_node: + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("root node"); + break; + case jsonpath_token_kind::current_node: + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("current node"); + break; + case jsonpath_token_kind::argument: + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("argument"); + break; + case jsonpath_token_kind::selector: + s.append(selector_->to_string(level)); + break; + case jsonpath_token_kind::expression: + s.append(expression_->to_string(level)); + break; + case jsonpath_token_kind::literal: + { + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + auto sbuf = value_.to_string(); + unicode_traits::convert(sbuf.data(), sbuf.size(), s); + break; + } + case jsonpath_token_kind::binary_operator: + s.append(binary_operator_->to_string(level)); + break; + case jsonpath_token_kind::function: + s.append(function_->to_string(level)); + break; + default: + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("token kind: "); + s.append(jsoncons::jsonpath::detail::to_string(token_kind_)); + break; + } + //s.append("\n"); + return s; + } + }; + + template + class callback_receiver : public node_receiver + { + Callback& callback_; + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + + callback_receiver(Callback& callback) + : callback_(callback) + { + } + + void add(const json_location_node_type& path_tail, + reference value) override + { + callback_(json_location_type(path_tail), value); + } + }; + + template + class path_expression + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_type = path_value_pair; + using path_value_pair_less_type = path_value_pair_less; + using path_value_pair_equal_type = path_value_pair_equal; + using value_type = Json; + using reference = typename path_value_pair_type::reference; + using pointer = typename path_value_pair_type::value_pointer; + using token_type = token; + using reference_arg_type = typename std::conditional::type>::value, + const_reference_arg_t,reference_arg_t>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using selector_type = jsonpath_selector; + private: + selector_type* selector_; + result_options required_options_; + public: + + path_expression() + : required_options_() + { + } + + path_expression(path_expression&& expr) = default; + + path_expression(selector_type* selector, bool paths_required) + : selector_(selector), required_options_() + { + if (paths_required) + { + required_options_ |= result_options::path; + } + } + + path_expression& operator=(path_expression&& expr) = default; + + Json evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& path, + reference instance, + result_options options) const + { + Json result(json_array_arg); + + if ((options & result_options::path) == result_options::path) + { + auto callback = [&result](const json_location_type& path, reference) + { + result.emplace_back(path.to_string()); + }; + evaluate(resources, root, path, instance, callback, options); + } + else + { + auto callback = [&result](const json_location_type&, reference val) + { + result.push_back(val); + }; + evaluate(resources, root, path, instance, callback, options); + } + + return result; + } + + template + typename std::enable_if::value,void>::type + evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& path, + reference current, + Callback callback, + result_options options) const + { + std::error_code ec; + + options |= required_options_; + + const result_options require_more = result_options::nodups | result_options::sort; + + if ((options & require_more) != result_options()) + { + path_value_receiver receiver; + selector_->select(resources, root, path, current, receiver, options); + + if (receiver.nodes.size() > 1 && (options & result_options::sort) == result_options::sort) + { + std::sort(receiver.nodes.begin(), receiver.nodes.end(), path_value_pair_less_type()); + } + + if (receiver.nodes.size() > 1 && (options & result_options::nodups) == result_options::nodups) + { + if ((options & result_options::sort) == result_options::sort) + { + auto last = std::unique(receiver.nodes.begin(),receiver.nodes.end(),path_value_pair_equal_type()); + receiver.nodes.erase(last,receiver.nodes.end()); + for (auto& node : receiver.nodes) + { + callback(node.path(), node.value()); + } + } + else + { + std::vector index(receiver.nodes); + std::sort(index.begin(), index.end(), path_value_pair_less_type()); + auto last = std::unique(index.begin(),index.end(),path_value_pair_equal_type()); + index.erase(last,index.end()); + + std::vector temp2; + temp2.reserve(index.size()); + for (auto&& node : receiver.nodes) + { + auto it = std::lower_bound(index.begin(),index.end(),node, path_value_pair_less_type()); + if (it != index.end() && it->path() == node.path()) + { + temp2.emplace_back(std::move(node)); + index.erase(it); + } + } + for (auto& node : temp2) + { + callback(node.path(), node.value()); + } + } + } + else + { + for (auto& node : receiver.nodes) + { + callback(node.path(), node.value()); + } + } + } + else + { + callback_receiver receiver(callback); + selector_->select(resources, root, path, current, receiver, options); + } + } + + std::string to_string(int level) const + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("expression "); + s.append(selector_->to_string(level+1)); + + return s; + + } + }; + + template + class expression : public expression_base + { + public: + using path_value_pair_type = path_value_pair; + using value_type = Json; + using reference = typename path_value_pair_type::reference; + using pointer = typename path_value_pair_type::value_pointer; + using const_pointer = const value_type*; + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_less_type = path_value_pair_less; + using path_value_pair_equal_type = path_value_pair_equal; + using parameter_type = parameter; + using token_type = token; + using reference_arg_type = typename std::conditional::type>::value, + const_reference_arg_t,reference_arg_t>::type; + using json_location_node_type = json_location_node; + using stack_item_type = value_or_pointer; + private: + std::vector token_list_; + public: + + expression() + { + } + + expression(expression&& expr) + : token_list_(std::move(expr.token_list_)) + { + } + + expression(std::vector&& token_stack) + : token_list_(std::move(token_stack)) + { + } + + expression& operator=(expression&& expr) = default; + + value_type evaluate(dynamic_resources& resources, + reference root, + reference current, + result_options options, + std::error_code& ec) const override + { + std::vector stack; + std::vector arg_stack; + + //std::cout << "EVALUATE TOKENS\n"; + //for (auto& tok : token_list_) + //{ + // std::cout << tok.to_string() << "\n"; + //} + //std::cout << "\n"; + + if (!token_list_.empty()) + { + for (auto& tok : token_list_) + { + //std::cout << "Token: " << tok.to_string() << "\n"; + switch (tok.token_kind()) + { + case jsonpath_token_kind::literal: + { + stack.emplace_back(std::addressof(tok.get_value(reference_arg_type(), resources))); + break; + } + case jsonpath_token_kind::unary_operator: + { + JSONCONS_ASSERT(stack.size() >= 1); + auto item = std::move(stack.back()); + stack.pop_back(); + + auto val = tok.unary_operator_->evaluate(item.value(), ec); + stack.emplace_back(std::move(val)); + break; + } + case jsonpath_token_kind::binary_operator: + { + //std::cout << "binary operator: " << stack.size() << "\n"; + JSONCONS_ASSERT(stack.size() >= 2); + auto rhs = std::move(stack.back()); + //std::cout << "rhs: " << *rhs << "\n"; + stack.pop_back(); + auto lhs = std::move(stack.back()); + //std::cout << "lhs: " << *lhs << "\n"; + stack.pop_back(); + + auto val = tok.binary_operator_->evaluate(lhs.value(), rhs.value(), ec); + //std::cout << "Evaluate binary expression: " << r << "\n"; + stack.emplace_back(std::move(val)); + break; + } + case jsonpath_token_kind::root_node: + //std::cout << "root: " << root << "\n"; + stack.emplace_back(std::addressof(root)); + break; + case jsonpath_token_kind::current_node: + //std::cout << "current: " << current << "\n"; + stack.emplace_back(std::addressof(current)); + break; + case jsonpath_token_kind::argument: + JSONCONS_ASSERT(!stack.empty()); + //std::cout << "argument stack items " << stack.size() << "\n"; + //for (auto& item : stack) + //{ + // std::cout << *item.to_pointer(resources) << "\n"; + //} + //std::cout << "\n"; + arg_stack.emplace_back(std::move(stack.back())); + //for (auto& item : arg_stack) + //{ + // std::cout << *item << "\n"; + //} + //std::cout << "\n"; + stack.pop_back(); + break; + case jsonpath_token_kind::function: + { + if (tok.function_->arity() && *(tok.function_->arity()) != arg_stack.size()) + { + ec = jsonpath_errc::invalid_arity; + return Json::null(); + } + //std::cout << "function arg stack:\n"; + //for (auto& item : arg_stack) + //{ + // std::cout << *item << "\n"; + //} + //std::cout << "\n"; + + value_type val = tok.function_->evaluate(arg_stack, ec); + if (ec) + { + return Json::null(); + } + //std::cout << "function result: " << val << "\n"; + arg_stack.clear(); + stack.emplace_back(std::move(val)); + break; + } + case jsonpath_token_kind::expression: + { + value_type val = tok.expression_->evaluate(resources, root, current, options, ec); + stack.emplace_back(std::move(val)); + break; + } + case jsonpath_token_kind::selector: + { + JSONCONS_ASSERT(!stack.empty()); + auto item = std::move(stack.back()); + //for (auto& item : stack) + //{ + //std::cout << "selector stack input:\n"; + //switch (item.tag) + //{ + // case node_set_tag::single: + // std::cout << "single: " << *(item.node.ptr) << "\n"; + // break; + // case node_set_tag::multi: + // for (auto& node : stack.back().ptr().nodes) + // { + // std::cout << "multi: " << *node.ptr << "\n"; + // } + // break; + // default: + // break; + //} + //std::cout << "\n"; + //} + //std::cout << "selector item: " << *ptr << "\n"; + + reference val = tok.selector_->evaluate(resources, root, resources.current_path_node(), item.value(), options, ec); + + stack.pop_back(); + stack.emplace_back(stack_item_type(std::addressof(val))); + break; + } + default: + break; + } + } + } + + //if (stack.size() != 1) + //{ + // std::cout << "Stack size: " << stack.size() << "\n"; + //} + return stack.empty() ? Json::null() : stack.back().value(); + } + + std::string to_string(int level) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("expression "); + for (const auto& item : token_list_) + { + s.append(item.to_string(level+1)); + } + + return s; + + } + private: + }; + +} // namespace detail +} // namespace jsonpath +} // namespace jsoncons + +#endif // JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP diff --git a/include/jsoncons_ext/jsonpath/flatten.hpp b/include/jsoncons_ext/jsonpath/flatten.hpp new file mode 100644 index 0000000..938391f --- /dev/null +++ b/include/jsoncons_ext/jsonpath/flatten.hpp @@ -0,0 +1,432 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_FLATTEN_HPP +#define JSONCONS_JSONPATH_FLATTEN_HPP + +#include +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include // std::copy +#include // std::back_inserter +#include + +namespace jsoncons { namespace jsonpath { + + template + std::size_t escape_string(const CharT* s, std::size_t length, + Sink& sink) + { + std::size_t count = 0; + const CharT* begin = s; + const CharT* end = s + length; + for (const CharT* it = begin; it != end; ++it) + { + CharT c = *it; + switch (c) + { + case '\\': + sink.push_back('\\'); + sink.push_back('\\'); + count += 2; + break; + case '\'': + sink.push_back('\\'); + sink.push_back('\''); + count += 2; + break; + case '\b': + sink.push_back('\\'); + sink.push_back('b'); + count += 2; + break; + case '\f': + sink.push_back('\\'); + sink.push_back('f'); + count += 2; + break; + case '\n': + sink.push_back('\\'); + sink.push_back('n'); + count += 2; + break; + case '\r': + sink.push_back('\\'); + sink.push_back('r'); + count += 2; + break; + case '\t': + sink.push_back('\\'); + sink.push_back('t'); + count += 2; + break; + default: + sink.push_back(c); + ++count; + break; + } + } + return count; + } + + template + void flatten_(const std::basic_string& parent_key, + const Json& parent_value, + Json& result) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + switch (parent_value.type()) + { + case json_type::array_value: + { + if (parent_value.empty()) + { + result.try_emplace(parent_key, parent_value); + } + else + { + for (std::size_t i = 0; i < parent_value.size(); ++i) + { + string_type key(parent_key); + key.push_back('['); + jsoncons::detail::from_integer(i,key); + key.push_back(']'); + flatten_(key, parent_value.at(i), result); + } + } + break; + } + + case json_type::object_value: + { + if (parent_value.empty()) + { + result.try_emplace(parent_key, Json()); + } + else + { + for (const auto& item : parent_value.object_range()) + { + string_type key(parent_key); + key.push_back('['); + key.push_back('\''); + escape_string(item.key().data(), item.key().length(), key); + key.push_back('\''); + key.push_back(']'); + flatten_(key, item.value(), result); + } + } + break; + } + + default: + { + result[parent_key] = parent_value; + break; + } + } + } + + template + Json flatten(const Json& value) + { + Json result; + std::basic_string parent_key = {'$'}; + flatten_(parent_key, value, result); + return result; + } + + enum class unflatten_state + { + start, + expect_lbracket, + lbracket, + single_quoted_name_state, + double_quoted_name_state, + index_state, + expect_rbracket, + double_quoted_string_escape_char, + single_quoted_string_escape_char + }; + + template + Json unflatten(const Json& value) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + if (JSONCONS_UNLIKELY(!value.is_object())) + { + JSONCONS_THROW(jsonpath_error(jsonpath_errc::argument_to_unflatten_invalid)); + } + + Json result; + + for (const auto& item : value.object_range()) + { + Json* part = &result; + string_type buffer; + unflatten_state state = unflatten_state::start; + + auto it = item.key().begin(); + auto last = item.key().end(); + + for (; it != last; ++it) + { + switch (state) + { + case unflatten_state::start: + { + switch (*it) + { + case '$': + state = unflatten_state::expect_lbracket; + break; + default: + break; + } + break; + } + case unflatten_state::expect_lbracket: + { + switch (*it) + { + case '[': + state = unflatten_state::lbracket; + break; + default: + JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); + break; + } + break; + } + case unflatten_state::lbracket: + { + switch (*it) + { + case '\'': + state = unflatten_state::single_quoted_name_state; + break; + case '\"': + state = unflatten_state::double_quoted_name_state; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.push_back(*it); + state = unflatten_state::index_state; + break; + default: + JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); + break; + } + break; + } + case unflatten_state::single_quoted_name_state: + { + switch (*it) + { + case '\'': + if (it != last-2) + { + auto res = part->try_emplace(buffer,Json()); + part = &(res.first->value()); + } + else + { + auto res = part->try_emplace(buffer,item.value()); + part = &(res.first->value()); + } + buffer.clear(); + state = unflatten_state::expect_rbracket; + break; + case '\\': + state = unflatten_state::single_quoted_string_escape_char; + break; + default: + buffer.push_back(*it); + break; + } + break; + } + case unflatten_state::double_quoted_name_state: + { + switch (*it) + { + case '\"': + if (it != last-2) + { + auto res = part->try_emplace(buffer,Json()); + part = &(res.first->value()); + } + else + { + auto res = part->try_emplace(buffer,item.value()); + part = &(res.first->value()); + } + buffer.clear(); + state = unflatten_state::expect_rbracket; + break; + case '\\': + state = unflatten_state::double_quoted_string_escape_char; + break; + default: + buffer.push_back(*it); + break; + } + break; + } + case unflatten_state::double_quoted_string_escape_char: + switch (*it) + { + case '\"': + buffer.push_back('\"'); + state = unflatten_state::double_quoted_name_state; + break; + case '\\': + buffer.push_back('\\'); + state = unflatten_state::double_quoted_name_state; + break; + case '/': + buffer.push_back('/'); + state = unflatten_state::double_quoted_name_state; + break; + case 'b': + buffer.push_back('\b'); + state = unflatten_state::double_quoted_name_state; + break; + case 'f': + buffer.push_back('\f'); + state = unflatten_state::double_quoted_name_state; + break; + case 'n': + buffer.push_back('\n'); + state = unflatten_state::double_quoted_name_state; + break; + case 'r': + buffer.push_back('\r'); + state = unflatten_state::double_quoted_name_state; + break; + case 't': + buffer.push_back('\t'); + state = unflatten_state::double_quoted_name_state; + break; + default: + break; + } + break; + case unflatten_state::single_quoted_string_escape_char: + switch (*it) + { + case '\'': + buffer.push_back('\''); + state = unflatten_state::single_quoted_name_state; + break; + case '\\': + buffer.push_back('\\'); + state = unflatten_state::double_quoted_name_state; + break; + case '/': + buffer.push_back('/'); + state = unflatten_state::double_quoted_name_state; + break; + case 'b': + buffer.push_back('\b'); + state = unflatten_state::double_quoted_name_state; + break; + case 'f': + buffer.push_back('\f'); + state = unflatten_state::double_quoted_name_state; + break; + case 'n': + buffer.push_back('\n'); + state = unflatten_state::double_quoted_name_state; + break; + case 'r': + buffer.push_back('\r'); + state = unflatten_state::double_quoted_name_state; + break; + case 't': + buffer.push_back('\t'); + state = unflatten_state::double_quoted_name_state; + break; + default: + break; + } + break; + case unflatten_state::index_state: + { + switch (*it) + { + case ']': + { + std::size_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (r) + { + if (!part->is_array()) + { + *part = Json(json_array_arg); + } + if (it != last-1) + { + if (n+1 > part->size()) + { + Json& ref = part->emplace_back(); + part = std::addressof(ref); + } + else + { + part = &part->at(n); + } + } + else + { + Json& ref = part->emplace_back(item.value()); + part = std::addressof(ref); + } + } + buffer.clear(); + state = unflatten_state::expect_lbracket; + break; + } + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.push_back(*it); + break; + default: + JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); + break; + } + break; + } + case unflatten_state::expect_rbracket: + { + switch (*it) + { + case ']': + state = unflatten_state::expect_lbracket; + break; + default: + JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); + break; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + } + + return result; + } +}} + +#endif diff --git a/include/jsoncons_ext/jsonpath/json_location.hpp b/include/jsoncons_ext/jsonpath/json_location.hpp new file mode 100644 index 0000000..9105608 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/json_location.hpp @@ -0,0 +1,445 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSON_LOCATION_HPP +#define JSONCONS_JSONPATH_JSON_LOCATION_HPP + +#include +#include +#include +#include // std::reverse +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { + + template + class json_location; + + enum class json_location_node_kind { root, index, name }; + + template + class json_location_node + { + friend class json_location; + public: + using char_type = CharT; + using string_type = std::basic_string; + private: + + const json_location_node* parent_; + json_location_node_kind node_kind_; + string_type name_; + std::size_t index_; + public: + json_location_node(char_type c) + : parent_(nullptr), node_kind_(json_location_node_kind::root), index_(0) + { + name_.push_back(c); + } + + json_location_node(const json_location_node* parent, const string_type& name) + : parent_(parent), node_kind_(json_location_node_kind::name), name_(name), index_(0) + { + } + + json_location_node(const json_location_node* parent, std::size_t index) + : parent_(parent), node_kind_(json_location_node_kind::index), index_(index) + { + } + + const json_location_node* parent() const { return parent_;} + + json_location_node_kind node_kind() const + { + return node_kind_; + } + + const string_type& name() const + { + return name_; + } + + std::size_t index() const + { + return index_; + } + + void swap(json_location_node& node) + { + std::swap(parent_, node.parent_); + std::swap(node_kind_, node.node_kind_); + std::swap(name_, node.name_); + std::swap(index_, node.index_); + } + + private: + + std::size_t node_hash() const + { + std::size_t h = node_kind_ == json_location_node_kind::index ? std::hash{}(index_) : std::hash{}(name_); + + return h; + } + + int compare_node(const json_location_node& other) const + { + int diff = 0; + if (node_kind_ != other.node_kind_) + { + diff = static_cast(node_kind_) - static_cast(other.node_kind_); + } + else + { + switch (node_kind_) + { + case json_location_node_kind::root: + diff = name_.compare(other.name_); + break; + case json_location_node_kind::index: + diff = index_ < other.index_ ? -1 : index_ > other.index_ ? 1 : 0; + break; + case json_location_node_kind::name: + diff = name_.compare(other.name_); + break; + } + } + return diff; + } + }; + + namespace detail { + + template + class json_location_iterator + { + Iterator it_; + + public: + using iterator_category = std::random_access_iterator_tag; + + using value_type = typename std::remove_pointer::value_type>::type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = const value_type*; + using reference = const value_type&; + + json_location_iterator() : it_() + { + } + + explicit json_location_iterator(Iterator ptr) : it_(ptr) + { + } + + json_location_iterator(const json_location_iterator&) = default; + json_location_iterator(json_location_iterator&&) = default; + json_location_iterator& operator=(const json_location_iterator&) = default; + json_location_iterator& operator=(json_location_iterator&&) = default; + + template ::value && std::is_convertible::value>::type> + json_location_iterator(const json_location_iterator& other) + : it_(other.it_) + { + } + + operator Iterator() const + { + return it_; + } + + reference operator*() const + { + return *(*it_); + } + + pointer operator->() const + { + return (*it_); + } + + json_location_iterator& operator++() + { + ++it_; + return *this; + } + + json_location_iterator operator++(int) + { + json_location_iterator temp = *this; + ++*this; + return temp; + } + + json_location_iterator& operator--() + { + --it_; + return *this; + } + + json_location_iterator operator--(int) + { + json_location_iterator temp = *this; + --*this; + return temp; + } + + json_location_iterator& operator+=(const difference_type offset) + { + it_ += offset; + return *this; + } + + json_location_iterator operator+(const difference_type offset) const + { + json_location_iterator temp = *this; + return temp += offset; + } + + json_location_iterator& operator-=(const difference_type offset) + { + return *this += -offset; + } + + json_location_iterator operator-(const difference_type offset) const + { + json_location_iterator temp = *this; + return temp -= offset; + } + + difference_type operator-(const json_location_iterator& rhs) const noexcept + { + return it_ - rhs.it_; + } + + reference operator[](const difference_type offset) const noexcept + { + return *(*(*this + offset)); + } + + bool operator==(const json_location_iterator& rhs) const noexcept + { + return it_ == rhs.it_; + } + + bool operator!=(const json_location_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + bool operator<(const json_location_iterator& rhs) const noexcept + { + return it_ < rhs.it_; + } + + bool operator>(const json_location_iterator& rhs) const noexcept + { + return rhs < *this; + } + + bool operator<=(const json_location_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + bool operator>=(const json_location_iterator& rhs) const noexcept + { + return !(*this < rhs); + } + + inline + friend json_location_iterator operator+( + difference_type offset, json_location_iterator next) + { + return next += offset; + } + }; + + } // namespace detail + + template + class json_location + { + public: + using char_type = CharT; + using string_type = std::basic_string; + using json_location_node_type = json_location_node; + private: + std::vector nodes_; + public: + using iterator = typename detail::json_location_iterator::iterator>; + using const_iterator = typename detail::json_location_iterator::const_iterator>; + + json_location(const json_location_node_type& node) + { + const json_location_node_type* p = std::addressof(node); + do + { + nodes_.push_back(p); + p = p->parent_; + } + while (p != nullptr); + + std::reverse(nodes_.begin(), nodes_.end()); + } + + iterator begin() + { + return iterator(nodes_.begin()); + } + + iterator end() + { + return iterator(nodes_.end()); + } + + const_iterator begin() const + { + return const_iterator(nodes_.begin()); + } + + const_iterator end() const + { + return const_iterator(nodes_.end()); + } + + const json_location_node_type& last() const + { + return *nodes_.back(); + } + + string_type to_string() const + { + string_type buffer; + + for (const auto& node : nodes_) + { + switch (node->node_kind()) + { + case json_location_node_kind::root: + buffer.append(node->name()); + break; + case json_location_node_kind::name: + buffer.push_back('['); + buffer.push_back('\''); + for (auto c : node->name()) + { + if (c == '\'') + { + buffer.push_back('\\'); + buffer.push_back('\''); + } + else + { + buffer.push_back(c); + } + } + buffer.push_back('\''); + buffer.push_back(']'); + break; + case json_location_node_kind::index: + buffer.push_back('['); + jsoncons::detail::from_integer(node->index(), buffer); + buffer.push_back(']'); + break; + } + } + + return buffer; + } + + int compare(const json_location& other) const + { + if (this == &other) + { + return 0; + } + + auto it1 = nodes_.begin(); + auto it2 = other.nodes_.begin(); + while (it1 != nodes_.end() && it2 != other.nodes_.end()) + { + int diff = (*it1)->compare_node(*(*it2)); + if (diff != 0) + { + return diff; + } + ++it1; + ++it2; + } + return (nodes_.size() < other.nodes_.size()) ? -1 : (nodes_.size() == other.nodes_.size()) ? 0 : 1; + } + + std::size_t hash() const + { + + auto it = nodes_.begin(); + std::size_t hash = (*it).hash(); + ++it; + + while (it != nodes_.end()) + { + hash += 17*(*it)->node_hash(); + ++it; + } + + return hash; + } + + friend bool operator==(const json_location& lhs, const json_location& rhs) + { + return lhs.compare(rhs) == 0; + } + + friend bool operator!=(const json_location& lhs, const json_location& rhs) + { + return !(lhs == rhs); + } + + friend bool operator<(const json_location& lhs, const json_location& rhs) + { + return lhs.compare(rhs) < 0; + } + }; + + template + Json* select(Json& root, const json_location& path) + { + Json* current = std::addressof(root); + for (const auto& json_location_node : path) + { + if (json_location_node.node_kind() == json_location_node_kind::index) + { + if (current->type() != json_type::array_value || json_location_node.index() >= current->size()) + { + return nullptr; + } + current = std::addressof(current->at(json_location_node.index())); + } + else if (json_location_node.node_kind() == json_location_node_kind::name) + { + if (current->type() != json_type::object_value) + { + return nullptr; + } + auto it = current->find(json_location_node.name()); + if (it == current->object_range().end()) + { + return nullptr; + } + current = std::addressof(it->value()); + } + } + return current; + } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/json_query.hpp b/include/jsoncons_ext/jsonpath/json_query.hpp new file mode 100644 index 0000000..8facfa9 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/json_query.hpp @@ -0,0 +1,115 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSON_QUERY_HPP +#define JSONCONS_JSONPATH_JSON_QUERY_HPP + +#include +#include + +namespace jsoncons { +namespace jsonpath { + + template + Json json_query(const Json& instance, + const typename Json::string_view_type& path, + result_options options = result_options(), + const custom_functions& functions = custom_functions()) + { + auto expr = make_expression(path, functions); + return expr.evaluate(instance, options); + } + + template + typename std::enable_if&,const Json&>::value,void>::type + json_query(const Json& instance, + const typename Json::string_view_type& path, + Callback callback, + result_options options = result_options(), + const custom_functions& functions = custom_functions()) + { + auto expr = make_expression(path, functions); + expr.evaluate(instance, callback, options); + } + + template + typename std::enable_if::value,void>::type + json_replace(Json& instance, const typename Json::string_view_type& path, T&& new_value, + result_options options = result_options::nodups, + const custom_functions& funcs = custom_functions()) + { + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + //using string_type = typename evaluator_t::string_type; + using value_type = typename evaluator_t::value_type; + using reference = typename evaluator_t::reference; + using json_selector_t = typename evaluator_t::path_expression_type; + using json_location_type = typename evaluator_t::json_location_type; + + jsoncons::jsonpath::detail::static_resources static_resources(funcs); + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources resources; + auto callback = [&new_value](const json_location_type&, reference v) + { + v = std::forward(new_value); + }; + expr.evaluate(resources, instance, resources.root_path_node(), instance, callback, options); + } + + template + typename std::enable_if::value,void>::type + json_replace(Json& instance, const typename Json::string_view_type& path , UnaryCallback callback) + { + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + //using string_type = typename evaluator_t::string_type; + using value_type = typename evaluator_t::value_type; + using reference = typename evaluator_t::reference; + using json_selector_t = typename evaluator_t::path_expression_type; + using json_location_type = typename evaluator_t::json_location_type; + + jsoncons::jsonpath::detail::static_resources static_resources; + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources resources; + auto f = [callback](const json_location_type&, reference v) + { + v = callback(v); + }; + expr.evaluate(resources, instance, resources.root_path_node(), instance, f, result_options::nodups); + } + + template + typename std::enable_if&,Json&>::value,void>::type + json_replace(Json& instance, const typename Json::string_view_type& path , BinaryCallback callback, + result_options options = result_options::nodups, + const custom_functions& funcs = custom_functions()) + { + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + //using string_type = typename evaluator_t::string_type; + using value_type = typename evaluator_t::value_type; + using reference = typename evaluator_t::reference; + using json_selector_t = typename evaluator_t::path_expression_type; + using json_location_type = typename evaluator_t::json_location_type; + + jsoncons::jsonpath::detail::static_resources static_resources(funcs); + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources resources; + + auto f = [&callback](const json_location_type& path, reference val) + { + callback(path.to_string(), val); + }; + expr.evaluate(resources, instance, resources.root_path_node(), instance, f, options); + } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath.hpp b/include/jsoncons_ext/jsonpath/jsonpath.hpp new file mode 100644 index 0000000..18500c5 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath.hpp @@ -0,0 +1,13 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONPATH_HPP +#define JSONCONS_JSONPATH_JSONPATH_HPP + +#include +#include + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_error.hpp b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp new file mode 100644 index 0000000..8157bba --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp @@ -0,0 +1,240 @@ +/// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONPATH_ERROR_HPP +#define JSONCONS_JSONPATH_JSONPATH_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace jsonpath { + + enum class jsonpath_errc + { + success = 0, + expected_root_or_function, + expected_current_node, + expected_rparen, + expected_rbracket, + expected_separator, + expected_forward_slash, + expected_slice_start, + expected_slice_end, + expected_slice_step, + expected_bracket_specifier_or_union, + unexpected_operator, + invalid_function_name, + invalid_argument, + invalid_arity, + function_name_not_found, + parse_error_in_filter, + argument_parse_error, + unidentified_error, + unexpected_eof, + expected_colon_dot_left_bracket_comma_or_rbracket, + argument_to_unflatten_invalid, + invalid_flattened_key, + step_cannot_be_zero, + invalid_number, + illegal_escaped_character, + invalid_codepoint, + unknown_function, + invalid_type, + unbalanced_parentheses, + syntax_error, + expected_comparator, + expected_or, + expected_and, + expected_comma_or_rparen, + expected_comma_or_rbracket, + expected_relative_path + }; + + class jsonpath_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/jsonpath"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case jsonpath_errc::expected_root_or_function: + return "Expected '$' or function expression"; + case jsonpath_errc::expected_current_node: + return "Expected @"; + case jsonpath_errc::expected_rbracket: + return "Expected ]"; + case jsonpath_errc::expected_rparen: + return "Expected )"; + case jsonpath_errc::expected_slice_start: + return "Expected slice start"; + case jsonpath_errc::expected_slice_end: + return "Expected slice end"; + case jsonpath_errc::expected_slice_step: + return "Expected slice step"; + case jsonpath_errc::expected_separator: + return "Expected dot or left bracket separator"; + case jsonpath_errc::expected_forward_slash: + return "Invalid path filter, expected '/'"; + case jsonpath_errc::expected_bracket_specifier_or_union: + return "Expected index, single or double quoted name, expression, filter, absolute ('$') path or relative ('@') path"; + case jsonpath_errc::invalid_function_name: + return "Invalid function name"; + case jsonpath_errc::invalid_argument: + return "Invalid argument type"; + case jsonpath_errc::invalid_arity: + return "Incorrect number of arguments"; + case jsonpath_errc::function_name_not_found: + return "Function name not found"; + case jsonpath_errc::parse_error_in_filter: + return "Could not parse JSON expression in a JSONPath filter"; + case jsonpath_errc::argument_parse_error: + return "Could not parse JSON expression passed to JSONPath function"; + case jsonpath_errc::unidentified_error: + return "Unidentified error"; + case jsonpath_errc::unexpected_eof: + return "Unexpected EOF while parsing jsonpath expression"; + case jsonpath_errc::expected_colon_dot_left_bracket_comma_or_rbracket: + return "Expected ':', '.', '[', ',', or ']'"; + case jsonpath_errc::argument_to_unflatten_invalid: + return "Argument to unflatten must be an object"; + case jsonpath_errc::invalid_flattened_key: + return "Flattened key is invalid"; + case jsonpath_errc::step_cannot_be_zero: + return "Slice step cannot be zero"; + case jsonpath_errc::invalid_number: + return "Invalid number"; + case jsonpath_errc::illegal_escaped_character: + return "Illegal escaped character"; + case jsonpath_errc::invalid_codepoint: + return "Invalid codepoint"; + case jsonpath_errc::unknown_function: + return "Unknown function"; + case jsonpath_errc::invalid_type: + return "Invalid type"; + case jsonpath_errc::unbalanced_parentheses: + return "Unbalanced parentheses"; + case jsonpath_errc::syntax_error: + return "Syntax error"; + case jsonpath_errc::expected_comparator: + return "Expected comparator"; + case jsonpath_errc::expected_or: + return "Expected operator '||'"; + case jsonpath_errc::expected_and: + return "Expected operator '&&'"; + case jsonpath_errc::expected_comma_or_rparen: + return "Expected comma or right parenthesis"; + case jsonpath_errc::expected_comma_or_rbracket: + return "Expected comma or right bracket"; + case jsonpath_errc::expected_relative_path: + return "Expected unquoted string, or single or double quoted string, or index or '*'"; + default: + return "Unknown jsonpath parser error"; + } + } + }; + + inline + const std::error_category& jsonpath_error_category() + { + static jsonpath_error_category_impl instance; + return instance; + } + + inline + std::error_code make_error_code(jsonpath_errc result) + { + return std::error_code(static_cast(result),jsonpath_error_category()); + } + +} // jsonpath +} // jsoncons + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace jsoncons { namespace jsonpath { + + class jsonpath_error : public std::system_error, public virtual json_exception + { + std::size_t line_number_; + std::size_t column_number_; + mutable std::string what_; + public: + jsonpath_error(std::error_code ec) + : std::system_error(ec), line_number_(0), column_number_(0) + { + } + jsonpath_error(std::error_code ec, const std::string& what_arg) + : std::system_error(ec, what_arg), line_number_(0), column_number_(0) + { + } + jsonpath_error(std::error_code ec, std::size_t position) + : std::system_error(ec), line_number_(0), column_number_(position) + { + } + jsonpath_error(std::error_code ec, std::size_t line, std::size_t column) + : std::system_error(ec), line_number_(line), column_number_(column) + { + } + jsonpath_error(const jsonpath_error& other) = default; + + jsonpath_error(jsonpath_error&& other) = default; + + const char* what() const noexcept override + { + if (what_.empty()) + { + JSONCONS_TRY + { + what_.append(std::system_error::what()); + if (line_number_ != 0 && column_number_ != 0) + { + what_.append(" at line "); + what_.append(std::to_string(line_number_)); + what_.append(" and column "); + what_.append(std::to_string(column_number_)); + } + else if (column_number_ != 0) + { + what_.append(" at position "); + what_.append(std::to_string(column_number_)); + } + return what_.c_str(); + } + JSONCONS_CATCH(...) + { + return std::system_error::what(); + } + } + else + { + return what_.c_str(); + } + } + + std::size_t line() const noexcept + { + return line_number_; + } + + std::size_t column() const noexcept + { + return column_number_; + } + }; + +}} + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp new file mode 100644 index 0000000..a3537b3 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp @@ -0,0 +1,2612 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP +#define JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP + +#include +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include +#include // std::reverse +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { +namespace detail { + + enum class path_state + { + start, + root_or_current_node, + expect_function_expr, + relative_path, + relative_location, + parent_operator, + ancestor_depth, + filter_expression, + expression_rhs, + recursive_descent_or_expression_lhs, + path_or_literal_or_function, + json_text_or_function, + json_text_or_function_name, + json_text_string, + json_value, + json_string, + identifier_or_function_expr, + name_or_lbracket, + unquoted_string, + anything, + number, + function_expression, + argument, + zero_or_one_arguments, + one_or_more_arguments, + identifier, + single_quoted_string, + double_quoted_string, + bracketed_unquoted_name_or_union, + union_expression, + identifier_or_union, + bracket_specifier_or_union, + bracketed_wildcard, + index_or_slice, + wildcard_or_union, + union_element, + index_or_slice_or_union, + integer, + digit, + slice_expression_stop, + slice_expression_step, + comma_or_rbracket, + expect_rparen, + expect_rbracket, + quoted_string_escape_char, + escape_u1, + escape_u2, + escape_u3, + escape_u4, + escape_expect_surrogate_pair1, + escape_expect_surrogate_pair2, + escape_u5, + escape_u6, + escape_u7, + escape_u8, + expression, + comparator_expression, + eq_or_regex, + expect_regex, + regex, + regex_options, + regex_pattern, + cmp_lt_or_lte, + cmp_gt_or_gte, + cmp_ne, + expect_or, + expect_and + }; + + template + class jsonpath_evaluator : public ser_context + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_type = path_value_pair; + using value_type = Json; + using reference = JsonReference; + using pointer = typename path_value_pair_type::value_pointer; + using token_type = token; + using path_expression_type = path_expression; + using expression_type = expression; + using json_location_type = json_location; + using json_location_node_type = json_location_node; + using selector_type = jsonpath_selector; + + private: + + std::size_t line_; + std::size_t column_; + const char_type* begin_input_; + const char_type* end_input_; + const char_type* p_; + + using argument_type = std::vector; + std::vector function_stack_; + std::vector state_stack_; + std::vector output_stack_; + std::vector operator_stack_; + + public: + jsonpath_evaluator() + : line_(1), column_(1), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr) + { + } + + jsonpath_evaluator(std::size_t line, std::size_t column) + : line_(line), column_(column), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr) + { + } + + std::size_t line() const + { + return line_; + } + + std::size_t column() const + { + return column_; + } + + path_expression_type compile(static_resources& resources, const string_view_type& path) + { + std::error_code ec; + auto result = compile(resources, path, ec); + if (ec) + { + JSONCONS_THROW(jsonpath_error(ec, line_, column_)); + } + return result; + } + + path_expression_type compile(static_resources& resources, + const string_view_type& path, + std::error_code& ec) + { + std::size_t selector_id = 0; + + string_type buffer; + string_type buffer2; + uint32_t cp = 0; + uint32_t cp2 = 0; + + begin_input_ = path.data(); + end_input_ = path.data() + path.length(); + p_ = begin_input_; + + slice slic; + bool paths_required = false; + int ancestor_depth = 0; + + state_stack_.emplace_back(path_state::start); + while (p_ < end_input_ && !state_stack_.empty()) + { + switch (state_stack_.back()) + { + case path_state::start: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '$': + case '@': + { + push_token(resources, token_type(resources.new_selector(current_node_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.emplace_back(path_state::relative_location); + ++p_; + ++column_; + break; + } + default: + { + state_stack_.emplace_back(path_state::relative_location); + state_stack_.emplace_back(path_state::expect_function_expr); + state_stack_.emplace_back(path_state::unquoted_string); + break; + } + } + break; + } + case path_state::root_or_current_node: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '$': + push_token(resources, token_type(root_node_arg), ec); + push_token(resources, token_type(resources.new_selector(root_selector(selector_id++))), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '@': + push_token(resources, token_type(current_node_arg), ec); // ISSUE + push_token(resources, token_type(resources.new_selector(current_node_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + break; + case path_state::recursive_descent_or_expression_lhs: + switch (*p_) + { + case '.': + push_token(resources, token_type(resources.new_selector(recursive_selector())), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + state_stack_.back() = path_state::name_or_lbracket; + break; + default: + state_stack_.back() = path_state::relative_path; + break; + } + break; + case path_state::name_or_lbracket: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '[': // [ can follow .. + state_stack_.back() = path_state::bracket_specifier_or_union; + ++p_; + ++column_; + break; + default: + buffer.clear(); + state_stack_.back() = path_state::relative_path; + break; + } + break; + case path_state::json_string: + { + //std::cout << "literal: " << buffer << "\n"; + push_token(resources, token_type(literal_arg, Json(buffer)), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); // json_value + break; + } + case path_state::path_or_literal_or_function: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '$': + case '@': + state_stack_.back() = path_state::relative_location; + state_stack_.push_back(path_state::root_or_current_node); + break; + case '(': + { + ++p_; + ++column_; + push_token(resources, lparen_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::expect_rparen; + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + break; + } + case '\'': + state_stack_.back() = path_state::json_string; + state_stack_.emplace_back(path_state::single_quoted_string); + ++p_; + ++column_; + break; + case '\"': + state_stack_.back() = path_state::json_string; + state_stack_.emplace_back(path_state::double_quoted_string); + ++p_; + ++column_; + break; + case '!': + { + ++p_; + ++column_; + push_token(resources, token_type(resources.get_unary_not()), ec); + if (ec) {return path_expression_type();} + break; + } + case '-': + { + ++p_; + ++column_; + push_token(resources, token_type(resources.get_unary_minus()), ec); + if (ec) {return path_expression_type();} + break; + } + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + { + state_stack_.back() = path_state::json_value; + state_stack_.emplace_back(path_state::number); + break; + } + default: + { + state_stack_.back() = path_state::json_text_or_function_name; + break; + } + } + break; + } + case path_state::json_text_or_function: + { + switch(*p_) + { + case '(': + { + auto f = resources.get_function(buffer, ec); + if (ec) + { + return path_expression_type(); + } + buffer.clear(); + push_token(resources, current_node_arg, ec); + if (ec) {return path_expression_type();} + push_token(resources, token_type(f), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::function_expression; + state_stack_.emplace_back(path_state::zero_or_one_arguments); + ++p_; + ++column_; + break; + } + default: + { + json_decoder decoder; + basic_json_parser parser; + parser.update(buffer.data(),buffer.size()); + parser.parse_some(decoder, ec); + if (ec) + { + return path_expression_type(); + } + parser.finish_parse(decoder, ec); + if (ec) + { + return path_expression_type(); + } + push_token(resources, token_type(literal_arg, decoder.get_result()), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + } + break; + } + case path_state::json_value: + { + json_decoder decoder; + basic_json_parser parser; + parser.update(buffer.data(),buffer.size()); + parser.parse_some(decoder, ec); + if (ec) + { + return path_expression_type(); + } + parser.finish_parse(decoder, ec); + if (ec) + { + return path_expression_type(); + } + push_token(resources, token_type(literal_arg, decoder.get_result()), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + case path_state::json_text_or_function_name: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '{': + case '[': + { + json_decoder decoder; + basic_json_parser parser; + parser.update(p_,end_input_ - p_); + parser.parse_some(decoder, ec); + if (ec) + { + return path_expression_type(); + } + parser.finish_parse(decoder, ec); + if (ec) + { + return path_expression_type(); + } + push_token(resources, token_type(literal_arg, decoder.get_result()), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + p_ = parser.current(); + column_ = column_ + parser.column() - 1; + break; + } + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state_stack_.back() = path_state::json_text_or_function; + state_stack_.emplace_back(path_state::number); + buffer.push_back(*p_); + ++p_; + ++column_; + break; + case '\"': + state_stack_.back() = path_state::json_text_or_function; + state_stack_.emplace_back(path_state::json_text_string); + buffer.push_back(*p_); + ++p_; + ++column_; + break; + default: + state_stack_.back() = path_state::json_text_or_function; + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::number: + switch (*p_) + { + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + case 'e':case 'E':case '.': + buffer.push_back(*p_); + ++p_; + ++column_; + break; + default: + state_stack_.pop_back(); // number + break; + }; + break; + case path_state::json_text_string: + switch (*p_) + { + case '\\': + buffer.push_back(*p_); + ++p_; + ++column_; + if (p_ == end_input_) + { + ec = jsonpath_errc::unexpected_eof; + return path_expression_type(); + } + buffer.push_back(*p_); + ++p_; + ++column_; + break; + case '\"': + buffer.push_back(*p_); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::relative_path: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '*': + push_token(resources, token_type(resources.new_selector(wildcard_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::identifier; + state_stack_.emplace_back(path_state::single_quoted_string); + ++p_; + ++column_; + break; + case '\"': + state_stack_.back() = path_state::identifier; + state_stack_.emplace_back(path_state::double_quoted_string); + ++p_; + ++column_; + break; + case '[': + case '.': + ec = jsonpath_errc::expected_relative_path; + return path_expression_type(); + default: + buffer.clear(); + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + break; + } + break; + case path_state::identifier_or_function_expr: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '(': + { + auto f = resources.get_function(buffer, ec); + if (ec) + { + return path_expression_type(); + } + buffer.clear(); + push_token(resources, current_node_arg, ec); + push_token(resources, token_type(f), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::function_expression; + state_stack_.emplace_back(path_state::zero_or_one_arguments); + ++p_; + ++column_; + break; + } + default: + { + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + } + break; + } + case path_state::expect_function_expr: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '(': + { + auto f = resources.get_function(buffer, ec); + if (ec) + { + return path_expression_type(); + } + buffer.clear(); + push_token(resources, current_node_arg, ec); + push_token(resources, token_type(f), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::function_expression; + state_stack_.emplace_back(path_state::zero_or_one_arguments); + ++p_; + ++column_; + break; + } + default: + { + ec = jsonpath_errc::expected_root_or_function; + return path_expression_type(); + } + } + break; + } + case path_state::function_expression: + { + + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + push_token(resources, token_type(current_node_arg), ec); + if (ec) {return path_expression_type();} + push_token(resources, token_type(begin_expression_arg), ec); + if (ec) {return path_expression_type();} + if (ec) {return path_expression_type();} + state_stack_.emplace_back(path_state::argument); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + case ')': + { + push_token(resources, token_type(end_function_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + break; + } + case path_state::zero_or_one_arguments: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ')': + state_stack_.pop_back(); + break; + default: + push_token(resources, token_type(begin_expression_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::one_or_more_arguments; + state_stack_.emplace_back(path_state::argument); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + break; + } + break; + } + case path_state::one_or_more_arguments: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ')': + state_stack_.pop_back(); + break; + case ',': + push_token(resources, token_type(begin_expression_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.emplace_back(path_state::argument); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + } + break; + } + case path_state::argument: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + case ')': + { + push_token(resources, token_type(end_argument_expression_arg), ec); + push_token(resources, argument_arg, ec); + //push_token(resources, argument_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + break; + } + default: + ec = jsonpath_errc::expected_comma_or_rparen; + return path_expression_type(); + } + break; + } + case path_state::unquoted_string: + switch (*p_) + { + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v':case 'w':case 'x':case 'y':case 'z': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z': + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + case '_': + buffer.push_back(*p_); + ++p_; + ++column_; + break; + default: + if (typename std::make_unsigned::type(*p_) > 127) + { + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + state_stack_.pop_back(); // unquoted_string + } + break; + }; + break; + case path_state::relative_location: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '.': + state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs); + ++p_; + ++column_; + break; + case '[': + state_stack_.emplace_back(path_state::bracket_specifier_or_union); + ++p_; + ++column_; + break; + case '^': + ancestor_depth = 0; + state_stack_.emplace_back(path_state::parent_operator); + state_stack_.emplace_back(path_state::ancestor_depth); + break; + default: + state_stack_.pop_back(); + break; + }; + break; + case path_state::parent_operator: + { + push_token(resources, token_type(resources.new_selector(parent_node_selector(ancestor_depth))), ec); + paths_required = true; + ancestor_depth = 0; + ++p_; + ++column_; + state_stack_.pop_back(); + break; + } + case path_state::ancestor_depth: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '^': + { + ++ancestor_depth; + ++p_; + ++column_; + break; + } + default: + { + state_stack_.pop_back(); + break; + } + } + break; + } + case path_state::expression_rhs: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '.': + state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs); + ++p_; + ++column_; + break; + case '[': + state_stack_.emplace_back(path_state::bracket_specifier_or_union); + ++p_; + ++column_; + break; + case ')': + { + state_stack_.pop_back(); + break; + } + case '|': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::path_or_literal_or_function); + state_stack_.emplace_back(path_state::expect_or); + break; + case '&': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::path_or_literal_or_function); + state_stack_.emplace_back(path_state::expect_and); + break; + case '<': + case '>': + { + state_stack_.emplace_back(path_state::comparator_expression); + break; + } + case '=': + { + state_stack_.emplace_back(path_state::eq_or_regex); + ++p_; + ++column_; + break; + } + case '!': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::path_or_literal_or_function); + state_stack_.emplace_back(path_state::cmp_ne); + break; + } + case '+': + state_stack_.emplace_back(path_state::path_or_literal_or_function); + push_token(resources, token_type(resources.get_plus_operator()), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + break; + case '-': + state_stack_.emplace_back(path_state::path_or_literal_or_function); + push_token(resources, token_type(resources.get_minus_operator()), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + break; + case '*': + state_stack_.emplace_back(path_state::path_or_literal_or_function); + push_token(resources, token_type(resources.get_mult_operator()), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + break; + case '/': + state_stack_.emplace_back(path_state::path_or_literal_or_function); + push_token(resources, token_type(resources.get_div_operator()), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + break; + case '%': + state_stack_.emplace_back(path_state::path_or_literal_or_function); + push_token(resources, token_type(resources.get_modulus_operator()), ec); + if (ec) {return path_expression_type();} + ++p_; + ++column_; + break; + case ']': + case ',': + state_stack_.pop_back(); + break; + default: + ec = jsonpath_errc::expected_separator; + return path_expression_type(); + }; + break; + case path_state::expect_or: + { + switch (*p_) + { + case '|': + push_token(resources, token_type(resources.get_or_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_or; + return path_expression_type(); + } + break; + } + case path_state::expect_and: + { + switch(*p_) + { + case '&': + push_token(resources, token_type(resources.get_and_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); // expect_and + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_and; + return path_expression_type(); + } + break; + } + case path_state::comparator_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '<': + ++p_; + ++column_; + state_stack_.back() = path_state::path_or_literal_or_function; + state_stack_.emplace_back(path_state::cmp_lt_or_lte); + break; + case '>': + ++p_; + ++column_; + state_stack_.back() = path_state::path_or_literal_or_function; + state_stack_.emplace_back(path_state::cmp_gt_or_gte); + break; + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + break; + } + break; + case path_state::eq_or_regex: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '=': + { + push_token(resources, token_type(resources.get_eq_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::path_or_literal_or_function; + ++p_; + ++column_; + break; + } + case '~': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::expect_regex); + break; + } + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + break; + } + break; + case path_state::expect_regex: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '/': + state_stack_.back() = path_state::regex; + state_stack_.emplace_back(path_state::regex_options); + state_stack_.emplace_back(path_state::regex_pattern); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_forward_slash; + return path_expression_type(); + }; + break; + case path_state::regex: + { + std::regex::flag_type options = std::regex_constants::ECMAScript; + if (buffer2.find('i') != string_type::npos) + { + options |= std::regex_constants::icase; + } + std::basic_regex pattern(buffer, options); + push_token(resources, resources.get_regex_operator(std::move(pattern)), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + buffer2.clear(); + state_stack_.pop_back(); + break; + } + case path_state::regex_pattern: + { + switch (*p_) + { + case '/': + { + state_stack_.pop_back(); + ++p_; + ++column_; + } + break; + + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + } + break; + } + case path_state::regex_options: + { + if (*p_ == 'i') + { + buffer2.push_back(*p_); + ++p_; + ++column_; + } + else + { + state_stack_.pop_back(); + } + break; + } + case path_state::cmp_lt_or_lte: + { + switch(*p_) + { + case '=': + push_token(resources, token_type(resources.get_lte_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(resources, token_type(resources.get_lt_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_gt_or_gte: + { + switch(*p_) + { + case '=': + push_token(resources, token_type(resources.get_gte_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + //std::cout << "Parse: gt_operator\n"; + push_token(resources, token_type(resources.get_gt_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_ne: + { + switch(*p_) + { + case '=': + push_token(resources, token_type(resources.get_ne_operator()), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_comparator; + return path_expression_type(); + } + break; + } + case path_state::identifier: + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::single_quoted_string: + switch (*p_) + { + case '\'': + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '\\': + state_stack_.emplace_back(path_state::quoted_string_escape_char); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::double_quoted_string: + switch (*p_) + { + case '\"': + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '\\': + state_stack_.emplace_back(path_state::quoted_string_escape_char); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::comma_or_rbracket: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + state_stack_.back() = path_state::bracket_specifier_or_union; + ++p_; + ++column_; + break; + case ']': + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_comma_or_rbracket; + return path_expression_type(); + } + break; + case path_state::expect_rbracket: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::expect_rparen: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ')': + ++p_; + ++column_; + push_token(resources, rparen_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::expression_rhs; + break; + default: + ec = jsonpath_errc::expected_rparen; + return path_expression_type(); + } + break; + case path_state::bracket_specifier_or_union: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '(': + { + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(begin_expression_arg), ec); + push_token(resources, lparen_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::expression); + state_stack_.emplace_back(path_state::expect_rparen); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + } + case '?': + { + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(begin_filter_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::filter_expression); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + } + case '*': + state_stack_.back() = path_state::wildcard_or_union; + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::identifier_or_union; + state_stack_.push_back(path_state::single_quoted_string); + ++p_; + ++column_; + break; + case '\"': + state_stack_.back() = path_state::identifier_or_union; + state_stack_.push_back(path_state::double_quoted_string); + ++p_; + ++column_; + break; + case ':': // slice_expression + state_stack_.back() = path_state::index_or_slice_or_union; + break; + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state_stack_.back() = path_state::index_or_slice_or_union; + state_stack_.emplace_back(path_state::integer); + break; + case '$': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, root_node_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::relative_location); + ++p_; + ++column_; + break; + case '@': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(current_node_arg), ec); // ISSUE + push_token(resources, token_type(resources.new_selector(current_node_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::relative_location); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_bracket_specifier_or_union; + return path_expression_type(); + } + break; + case path_state::union_element: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ':': // slice_expression + state_stack_.back() = path_state::index_or_slice; + break; + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + state_stack_.back() = path_state::index_or_slice; + state_stack_.emplace_back(path_state::integer); + break; + case '(': + { + push_token(resources, token_type(begin_expression_arg), ec); + push_token(resources, lparen_arg, ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::expression; + state_stack_.emplace_back(path_state::expect_rparen); + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + } + case '?': + { + push_token(resources, token_type(begin_filter_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::filter_expression; + state_stack_.emplace_back(path_state::expression_rhs); + state_stack_.emplace_back(path_state::path_or_literal_or_function); + ++p_; + ++column_; + break; + } + case '*': + push_token(resources, token_type(resources.new_selector(wildcard_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::relative_location; + ++p_; + ++column_; + break; + case '$': + push_token(resources, token_type(root_node_arg), ec); + push_token(resources, token_type(resources.new_selector(root_selector(selector_id++))), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::relative_location; + ++p_; + ++column_; + break; + case '@': + push_token(resources, token_type(current_node_arg), ec); // ISSUE + push_token(resources, token_type(resources.new_selector(current_node_selector())), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::relative_location; + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::identifier; + state_stack_.push_back(path_state::single_quoted_string); + ++p_; + ++column_; + break; + case '\"': + state_stack_.back() = path_state::identifier; + state_stack_.push_back(path_state::double_quoted_string); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_bracket_specifier_or_union; + return path_expression_type(); + } + break; + + case path_state::integer: + switch(*p_) + { + case '-': + buffer.push_back(*p_); + state_stack_.back() = path_state::digit; + ++p_; + ++column_; + break; + default: + state_stack_.back() = path_state::digit; + break; + } + break; + case path_state::digit: + switch(*p_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + buffer.push_back(*p_); + ++p_; + ++column_; + break; + default: + state_stack_.pop_back(); // digit + break; + } + break; + case path_state::index_or_slice_or_union: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + { + if (buffer.empty()) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + push_token(resources, token_type(resources.new_selector(index_selector(n))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); // index_or_slice_or_union + ++p_; + ++column_; + break; + } + case ',': + { + push_token(resources, token_type(begin_union_arg), ec); + if (ec) {return path_expression_type();} + if (buffer.empty()) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + else + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + push_token(resources, token_type(resources.new_selector(index_selector(n))), ec); + if (ec) {return path_expression_type();} + + buffer.clear(); + } + push_token(resources, token_type(separator_arg), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::union_element); + ++p_; + ++column_; + break; + } + case ':': + { + if (!buffer.empty()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + slic.start_ = n; + buffer.clear(); + } + push_token(resources, token_type(begin_union_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::slice_expression_stop); + state_stack_.emplace_back(path_state::integer); + ++p_; + ++column_; + break; + } + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::slice_expression_stop: + { + if (!buffer.empty()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + slic.stop_ = jsoncons::optional(n); + buffer.clear(); + } + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + case ',': + push_token(resources, token_type(resources.new_selector(slice_selector(slic))), ec); + if (ec) {return path_expression_type();} + slic = slice{}; + state_stack_.pop_back(); // bracket_specifier2 + break; + case ':': + state_stack_.back() = path_state::slice_expression_step; + state_stack_.emplace_back(path_state::integer); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + } + case path_state::slice_expression_step: + { + if (!buffer.empty()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + if (n == 0) + { + ec = jsonpath_errc::step_cannot_be_zero; + return path_expression_type(); + } + slic.step_ = n; + buffer.clear(); + } + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + case ',': + push_token(resources, token_type(resources.new_selector(slice_selector(slic))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + slic = slice{}; + state_stack_.pop_back(); // slice_expression_step + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + } + + case path_state::bracketed_unquoted_name_or_union: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '.': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::relative_path); + ++p_; + ++column_; + break; + case '[': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::relative_path); + ++p_; + ++column_; + break; + case ',': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + push_token(resources, token_type(separator_arg), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::relative_path); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + } + break; + case path_state::union_expression: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '.': + state_stack_.emplace_back(path_state::relative_path); + ++p_; + ++column_; + break; + case '[': + state_stack_.emplace_back(path_state::bracket_specifier_or_union); + ++p_; + ++column_; + break; + case ',': + push_token(resources, token_type(separator_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.emplace_back(path_state::union_element); + ++p_; + ++column_; + break; + case ']': + push_token(resources, token_type(end_union_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::identifier_or_union: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case ',': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + push_token(resources, token_type(separator_arg), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::union_element); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::bracketed_wildcard: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case '[': + case ']': + case ',': + case '.': + push_token(resources, token_type(resources.new_selector(wildcard_selector())), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::index_or_slice: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + case ']': + { + if (buffer.empty()) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + else + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + push_token(resources, token_type(resources.new_selector(index_selector(n))), ec); + if (ec) {return path_expression_type();} + + buffer.clear(); + } + state_stack_.pop_back(); // bracket_specifier + break; + } + case ':': + { + if (!buffer.empty()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + if (!r) + { + ec = jsonpath_errc::invalid_number; + return path_expression_type(); + } + slic.start_ = n; + buffer.clear(); + } + state_stack_.back() = path_state::slice_expression_stop; + state_stack_.emplace_back(path_state::integer); + ++p_; + ++column_; + break; + } + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::wildcard_or_union: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ']': + push_token(resources, token_type(resources.new_selector(wildcard_selector())), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case ',': + push_token(resources, token_type(begin_union_arg), ec); + push_token(resources, token_type(resources.new_selector(wildcard_selector())), ec); + push_token(resources, token_type(separator_arg), ec); + if (ec) {return path_expression_type();} + buffer.clear(); + state_stack_.back() = path_state::union_expression; // union + state_stack_.emplace_back(path_state::union_element); + ++p_; + ++column_; + break; + default: + ec = jsonpath_errc::expected_rbracket; + return path_expression_type(); + } + break; + case path_state::quoted_string_escape_char: + switch (*p_) + { + case '\"': + buffer.push_back('\"'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '\'': + buffer.push_back('\''); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '\\': + buffer.push_back('\\'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '/': + buffer.push_back('/'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'b': + buffer.push_back('\b'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'f': + buffer.push_back('\f'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'n': + buffer.push_back('\n'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'r': + buffer.push_back('\r'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 't': + buffer.push_back('\t'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case 'u': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u1; + break; + default: + ec = jsonpath_errc::illegal_escaped_character; + return path_expression_type(); + } + break; + case path_state::escape_u1: + cp = append_to_codepoint(0, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u2; + break; + case path_state::escape_u2: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u3; + break; + case path_state::escape_u3: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u4; + break; + case path_state::escape_u4: + cp = append_to_codepoint(cp, *p_, ec); + if (ec) + { + return path_expression_type(); + } + if (unicode_traits::is_high_surrogate(cp)) + { + ++p_; + ++column_; + state_stack_.back() = path_state::escape_expect_surrogate_pair1; + } + else + { + unicode_traits::convert(&cp, 1, buffer); + ++p_; + ++column_; + state_stack_.pop_back(); + } + break; + case path_state::escape_expect_surrogate_pair1: + switch (*p_) + { + case '\\': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_expect_surrogate_pair2; + break; + default: + ec = jsonpath_errc::invalid_codepoint; + return path_expression_type(); + } + break; + case path_state::escape_expect_surrogate_pair2: + switch (*p_) + { + case 'u': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u5; + break; + default: + ec = jsonpath_errc::invalid_codepoint; + return path_expression_type(); + } + break; + case path_state::escape_u5: + cp2 = append_to_codepoint(0, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u6; + break; + case path_state::escape_u6: + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u7; + break; + case path_state::escape_u7: + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return path_expression_type(); + } + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u8; + break; + case path_state::escape_u8: + { + cp2 = append_to_codepoint(cp2, *p_, ec); + if (ec) + { + return path_expression_type(); + } + uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); + unicode_traits::convert(&codepoint, 1, buffer); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + case path_state::filter_expression: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + case ']': + { + push_token(resources, token_type(end_filter_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + break; + } + default: + ec = jsonpath_errc::expected_comma_or_rbracket; + return path_expression_type(); + } + break; + } + case path_state::expression: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(); + break; + case ',': + case ']': + { + push_token(resources, token_type(end_index_expression_arg), ec); + if (ec) {return path_expression_type();} + state_stack_.pop_back(); + break; + } + default: + ec = jsonpath_errc::expected_comma_or_rbracket; + return path_expression_type(); + } + break; + } + default: + ++p_; + ++column_; + break; + } + } + + if (state_stack_.empty()) + { + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + + while (state_stack_.size() > 1) + { + switch (state_stack_.back()) + { + case path_state::name_or_lbracket: + state_stack_.back() = path_state::relative_path; + break; + case path_state::relative_path: + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + break; + case path_state::identifier_or_function_expr: + if (!buffer.empty()) // Can't be quoted string + { + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + } + state_stack_.pop_back(); + break; + case path_state::unquoted_string: + state_stack_.pop_back(); // unquoted_string + break; + case path_state::relative_location: + state_stack_.pop_back(); + break; + case path_state::identifier: + if (!buffer.empty()) // Can't be quoted string + { + push_token(resources, token_type(resources.new_selector(identifier_selector(buffer))), ec); + if (ec) {return path_expression_type();} + } + state_stack_.pop_back(); + break; + case path_state::parent_operator: + { + push_token(resources, token_type(resources.new_selector(parent_node_selector(ancestor_depth))), ec); + if (ec) { return path_expression_type(); } + paths_required = true; + state_stack_.pop_back(); + break; + } + case path_state::ancestor_depth: + state_stack_.pop_back(); + break; + default: + ec = jsonpath_errc::syntax_error; + return path_expression_type(); + } + } + + if (state_stack_.size() > 2) + { + ec = jsonpath_errc::unexpected_eof; + return path_expression_type(); + } + + //std::cout << "\nTokens\n\n"; + //for (const auto& tok : output_stack_) + //{ + // std::cout << tok.to_string() << "\n"; + //} + //std::cout << "\n"; + + if (output_stack_.empty() || !operator_stack_.empty()) + { + ec = jsonpath_errc::unexpected_eof; + return path_expression_type(); + } + + return path_expression_type(output_stack_.back().selector_, paths_required); + } + + void advance_past_space_character() + { + switch (*p_) + { + case ' ':case '\t': + ++p_; + ++column_; + break; + case '\r': + if (p_+1 < end_input_ && *(p_+1) == '\n') + ++p_; + ++line_; + column_ = 1; + ++p_; + break; + case '\n': + ++line_; + column_ = 1; + ++p_; + break; + default: + break; + } + } + + void unwind_rparen(std::error_code& ec) + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && !it->is_lparen()) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + if (it == operator_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + ++it; + operator_stack_.erase(it.base(),operator_stack_.end()); + } + + void push_token(jsoncons::jsonpath::detail::static_resources& resources, token_type&& tok, std::error_code& ec) + { + //std::cout << tok.to_string() << "\n"; + switch (tok.token_kind()) + { + case jsonpath_token_kind::begin_filter: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token_type(lparen_arg)); + break; + case jsonpath_token_kind::end_filter: + { + //std::cout << "push_token end_filter 1\n"; + //for (const auto& tok2 : output_stack_) + //{ + // std::cout << tok2.to_string() << "\n"; + //} + //std::cout << "\n\n"; + unwind_rparen(ec); + if (ec) + { + return; + } + std::vector toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_filter) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + std::reverse(toks.begin(), toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_path()) + { + output_stack_.back().selector_->append_selector(resources.new_selector(filter_selector(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(filter_selector(expression_type(std::move(toks)))))); + } + //std::cout << "push_token end_filter 2\n"; + //for (const auto& tok2 : output_stack_) + //{ + // std::cout << tok2.to_string() << "\n"; + //} + //std::cout << "\n\n"; + break; + } + case jsonpath_token_kind::begin_expression: + //std::cout << "begin_expression\n"; + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token_type(lparen_arg)); + break; + case jsonpath_token_kind::end_index_expression: + { + //std::cout << "jsonpath_token_kind::end_index_expression\n"; + //for (const auto& t : output_stack_) + //{ + // std::cout << t.to_string() << "\n"; + //} + //std::cout << "/jsonpath_token_kind::end_index_expression\n"; + unwind_rparen(ec); + if (ec) + { + return; + } + std::vector toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + std::reverse(toks.begin(), toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_path()) + { + output_stack_.back().selector_->append_selector(resources.new_selector(index_expression_selector(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(index_expression_selector(expression_type(std::move(toks)))))); + } + break; + } + case jsonpath_token_kind::end_argument_expression: + { + //std::cout << "jsonpath_token_kind::end_index_expression\n"; + //for (const auto& t : output_stack_) + //{ + // std::cout << t.to_string() << "\n"; + //} + //std::cout << "/jsonpath_token_kind::end_index_expression\n"; + unwind_rparen(ec); + if (ec) + { + return; + } + std::vector toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + std::reverse(toks.begin(), toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + output_stack_.emplace_back(token_type(jsoncons::make_unique(std::move(toks)))); + break; + } + case jsonpath_token_kind::selector: + { + if (!output_stack_.empty() && output_stack_.back().is_path()) + { + output_stack_.back().selector_->append_selector(std::move(tok.selector_)); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + } + case jsonpath_token_kind::separator: + output_stack_.emplace_back(std::move(tok)); + break; + case jsonpath_token_kind::begin_union: + output_stack_.emplace_back(std::move(tok)); + break; + case jsonpath_token_kind::end_union: + { + std::vector expressions; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union) + { + if (it->token_kind() == jsonpath_token_kind::selector) + { + expressions.emplace_back(std::move(it->selector_)); + } + do + { + ++it; + } + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union && it->token_kind() != jsonpath_token_kind::separator); + if (it->token_kind() == jsonpath_token_kind::separator) + { + ++it; + } + } + if (it == output_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + std::reverse(expressions.begin(), expressions.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_path()) + { + output_stack_.back().selector_->append_selector(resources.new_selector(union_selector(std::move(expressions)))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(union_selector(std::move(expressions))))); + } + break; + } + case jsonpath_token_kind::lparen: + operator_stack_.emplace_back(std::move(tok)); + break; + case jsonpath_token_kind::rparen: + { + unwind_rparen(ec); + break; + } + case jsonpath_token_kind::end_function: + { + //std::cout << "jsonpath_token_kind::end_function\n"; + unwind_rparen(ec); + if (ec) + { + return; + } + std::vector toks; + auto it = output_stack_.rbegin(); + std::size_t arg_count = 0; + while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::function) + { + if (it->token_kind() == jsonpath_token_kind::argument) + { + ++arg_count; + } + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jsonpath_errc::unbalanced_parentheses; + return; + } + std::reverse(toks.begin(), toks.end()); + if (it->arity() && arg_count != *(it->arity())) + { + ec = jsonpath_errc::invalid_arity; + return; + } + toks.push_back(std::move(*it)); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_path()) + { + output_stack_.back().selector_->append_selector(resources.new_selector(function_selector(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(function_selector(std::move(toks))))); + } + break; + } + case jsonpath_token_kind::literal: + if (!output_stack_.empty() && (output_stack_.back().token_kind() == jsonpath_token_kind::current_node || output_stack_.back().token_kind() == jsonpath_token_kind::root_node)) + { + output_stack_.back() = std::move(tok); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + case jsonpath_token_kind::function: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token_type(lparen_arg)); + break; + case jsonpath_token_kind::argument: + output_stack_.emplace_back(std::move(tok)); + break; + case jsonpath_token_kind::root_node: + case jsonpath_token_kind::current_node: + output_stack_.emplace_back(std::move(tok)); + break; + case jsonpath_token_kind::unary_operator: + case jsonpath_token_kind::binary_operator: + { + if (operator_stack_.empty() || operator_stack_.back().is_lparen()) + { + operator_stack_.emplace_back(std::move(tok)); + } + else if (tok.precedence_level() < operator_stack_.back().precedence_level() + || (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative())) + { + operator_stack_.emplace_back(std::move(tok)); + } + else + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && it->is_operator() + && (tok.precedence_level() > it->precedence_level() + || (tok.precedence_level() == it->precedence_level() && tok.is_right_associative()))) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + + operator_stack_.erase(it.base(),operator_stack_.end()); + operator_stack_.emplace_back(std::move(tok)); + } + break; + } + default: + break; + } + //std::cout << " " << "Output Stack\n"; + //for (auto&& t : output_stack_) + //{ + // std::cout << t.to_string(2) << "\n"; + //} + //if (!operator_stack_.empty()) + //{ + // std::cout << " " << "Operator Stack\n"; + // for (auto&& t : operator_stack_) + // { + // std::cout << t.to_string(2) << "\n"; + // } + //} + } + + uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec) + { + cp *= 16; + if (c >= '0' && c <= '9') + { + cp += c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + cp += c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + cp += c - 'A' + 10; + } + else + { + ec = jsonpath_errc::invalid_codepoint; + } + return cp; + } + }; + + } // namespace detail + + template + class jsonpath_expression + { + public: + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + using char_type = typename evaluator_t::char_type; + using string_type = typename evaluator_t::string_type; + using string_view_type = typename evaluator_t::string_view_type; + using value_type = typename evaluator_t::value_type; + using reference = typename evaluator_t::reference; + using parameter_type = parameter; + using json_selector_t = typename evaluator_t::path_expression_type; + using path_value_pair_type = typename evaluator_t::path_value_pair_type; + using json_location_type = typename evaluator_t::json_location_type; + using function_type = std::function, std::error_code& ec)>; + private: + jsoncons::jsonpath::detail::static_resources static_resources_; + json_selector_t expr_; + public: + jsonpath_expression(jsoncons::jsonpath::detail::static_resources&& resources, + json_selector_t&& expr) + : static_resources_(std::move(resources)), + expr_(std::move(expr)) + { + } + + jsonpath_expression(jsoncons::jsonpath::detail::static_resources&& resources, + json_selector_t&& expr, std::vector&& custom_functions) + : static_resources_(std::move(resources)), + expr_(std::move(expr), std::move(custom_functions)) + { + } + + template + typename std::enable_if::value,void>::type + evaluate(reference instance, BinaryCallback callback, result_options options = result_options()) + { + jsoncons::jsonpath::detail::dynamic_resources resources; + auto f = [&callback](const json_location_type& path, reference val) + { + callback(path.to_string(), val); + }; + expr_.evaluate(resources, instance, resources.root_path_node(), instance, f, options); + } + + Json evaluate(reference instance, result_options options = result_options()) + { + if ((options & result_options::path) == result_options::path) + { + jsoncons::jsonpath::detail::dynamic_resources resources; + + Json result(json_array_arg); + auto callback = [&result](const json_location_type& p, reference) + { + result.emplace_back(p.to_string()); + }; + expr_.evaluate(resources, instance, resources.root_path_node(), instance, callback, options); + return result; + } + else + { + jsoncons::jsonpath::detail::dynamic_resources resources; + return expr_.evaluate(resources, instance, resources.current_path_node(), instance, options); + } + } + + static jsonpath_expression compile(const string_view_type& path) + { + jsoncons::jsonpath::detail::static_resources resources; + + evaluator_t e; + json_selector_t expr = e.compile(resources, path); + return jsonpath_expression(std::move(resources), std::move(expr)); + } + + static jsonpath_expression compile(const string_view_type& path, std::error_code& ec) + { + jsoncons::jsonpath::detail::static_resources resources; + evaluator_t e; + json_selector_t expr = e.compile(resources, path, ec); + return jsonpath_expression(std::move(resources), std::move(expr)); + } + + static jsonpath_expression compile(const string_view_type& path, + const custom_functions& functions) + { + jsoncons::jsonpath::detail::static_resources resources(functions); + + evaluator_t e; + json_selector_t expr = e.compile(resources, path); + return jsonpath_expression(std::move(resources), std::move(expr)); + } + + static jsonpath_expression compile(const string_view_type& path, + const custom_functions& functions, + std::error_code& ec) + { + jsoncons::jsonpath::detail::static_resources resources(functions); + evaluator_t e; + json_selector_t expr = e.compile(resources, path, ec); + return jsonpath_expression(std::move(resources), std::move(expr)); + } + }; + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, + const custom_functions& functions = custom_functions()) + { + return jsonpath_expression::compile(expr, functions); + } + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, std::error_code& ec) + { + return jsonpath_expression::compile(expr, ec); + } + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, + const custom_functions& functions, + std::error_code& ec) + { + return jsonpath_expression::compile(expr, functions, ec); + } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp new file mode 100644 index 0000000..e06d37c --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp @@ -0,0 +1,1322 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONPATH_SELECTOR_HPP +#define JSONCONS_JSONPATH_JSONPATH_SELECTOR_HPP + +#include +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { +namespace detail { + + struct slice + { + jsoncons::optional start_; + jsoncons::optional stop_; + int64_t step_; + + slice() + : start_(), stop_(), step_(1) + { + } + + slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) + : start_(start), stop_(end), step_(step) + { + } + + slice(const slice& other) + : start_(other.start_), stop_(other.stop_), step_(other.step_) + { + } + + slice& operator=(const slice& rhs) + { + if (this != &rhs) + { + if (rhs.start_) + { + start_ = rhs.start_; + } + else + { + start_.reset(); + } + if (rhs.stop_) + { + stop_ = rhs.stop_; + } + else + { + stop_.reset(); + } + step_ = rhs.step_; + } + return *this; + } + + int64_t get_start(std::size_t size) const + { + if (start_) + { + auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + if (step_ >= 0) + { + return 0; + } + else + { + return static_cast(size); + } + } + } + + int64_t get_stop(std::size_t size) const + { + if (stop_) + { + auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + return step_ >= 0 ? static_cast(size) : -1; + } + } + + int64_t step() const + { + return step_; // Allow negative + } + }; + + template + class json_array_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + + Json* val; + + json_array_receiver(Json* ptr) + : val(ptr) + { + } + + void add(const json_location_node_type&, reference value) override + { + val->emplace_back(value); + } + }; + + template + struct path_generator + { + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using string_type = std::basic_string; + + static const json_location_node_type& generate(dynamic_resources& resources, + const json_location_node_type& last, + std::size_t index, + result_options options) + { + const result_options require_path = result_options::path | result_options::nodups | result_options::sort; + if ((options & require_path) != result_options()) + { + return *resources.create_path_node(&last, index); + } + else + { + return last; + } + } + + static const json_location_node_type& generate(dynamic_resources& resources, + const json_location_node_type& last, + const string_type& identifier, + result_options options) + { + const result_options require_path = result_options::path | result_options::nodups | result_options::sort; + if ((options & require_path) != result_options()) + { + return *resources.create_path_node(&last, identifier); + } + else + { + return last; + } + } + }; + + template + class base_selector : public jsonpath_selector + { + using supertype = jsonpath_selector; + + supertype* tail_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using node_receiver_type = typename supertype::node_receiver_type; + using selector_type = typename supertype::selector_type; + + base_selector() + : supertype(true, 11), tail_(nullptr) + { + } + + base_selector(bool is_path, std::size_t precedence_level) + : supertype(is_path, precedence_level), tail_(nullptr) + { + } + + void append_selector(selector_type* expr) override + { + if (!tail_) + { + tail_ = expr; + } + else + { + tail_->append_selector(expr); + } + } + + void tail_select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const + { + if (!tail_) + { + receiver.add(last, current); + } + else + { + tail_->select(resources, root, last, current, receiver, options); + } + } + + reference evaluate_tail(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const + { + if (!tail_) + { + return current; + } + else + { + return tail_->evaluate(resources, root, last, current, options, ec); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + if (tail_) + { + s.append(tail_->to_string(level)); + } + return s; + } + }; + + template + class identifier_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using string_view_type = basic_string_view; + using node_receiver_type = typename supertype::node_receiver_type; + private: + string_type identifier_; + public: + + identifier_selector(const string_view_type& identifier) + : base_selector(), identifier_(identifier) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + //std::string buf; + //buf.append("identifier selector: "); + //unicode_traits::convert(identifier_.data(),identifier_.size(),buf); + + static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; + + if (current.is_object()) + { + auto it = current.find(identifier_); + if (it != current.object_range().end()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + it->value(), receiver, options); + } + } + else if (current.is_array()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); + if (r) + { + std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); + if (index < current.size()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, index, options), + current[index], receiver, options); + } + } + else if (identifier_ == length_name && current.size() > 0) + { + pointer ptr = resources.create_json(current.size()); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, + receiver, options); + } + } + else if (current.is_string() && identifier_ == length_name) + { + string_view_type sv = current.as_string_view(); + std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); + pointer ptr = resources.create_json(count); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, receiver, options); + } + //std::cout << "end identifier_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; + + if (current.is_object()) + { + auto it = current.find(identifier_); + if (it != current.object_range().end()) + { + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + it->value(), options, ec); + } + else + { + return resources.null_value(); + } + } + else if (current.is_array()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); + if (r) + { + std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); + if (index < current.size()) + { + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, index, options), + current[index], options, ec); + } + else + { + return resources.null_value(); + } + } + else if (identifier_ == length_name && current.size() > 0) + { + pointer ptr = resources.create_json(current.size()); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, + options, ec); + } + else + { + return resources.null_value(); + } + } + else if (current.is_string() && identifier_ == length_name) + { + string_view_type sv = current.as_string_view(); + std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); + pointer ptr = resources.create_json(count); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, options, ec); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("identifier selector "); + unicode_traits::convert(identifier_.data(),identifier_.size(),s); + s.append(base_selector::to_string(level+1)); + //s.append("\n"); + + return s; + } + }; + + template + class root_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + + std::size_t id_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using node_receiver_type = typename supertype::node_receiver_type; + + root_selector(std::size_t id) + : base_selector(), id_(id) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + node_receiver_type& receiver, + result_options options) const override + { + this->tail_select(resources, root, last, root, receiver, options); + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + result_options options, + std::error_code& ec) const override + { + if (resources.is_cached(id_)) + { + return resources.retrieve_from_cache(id_); + } + else + { + auto& ref = this->evaluate_tail(resources, root, last, root, options, ec); + if (!ec) + { + resources.add_to_cache(id_, ref); + } + + return ref; + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("root_selector "); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class current_node_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + current_node_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + this->tail_select(resources, + root, last, current, receiver, options); + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + //std::cout << "current_node_selector: " << current << "\n"; + return this->evaluate_tail(resources, + root, last, current, options, ec); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("current_node_selector"); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class parent_node_selector final : public base_selector + { + using supertype = base_selector; + + int ancestor_depth_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + parent_node_selector(int ancestor_depth) + { + ancestor_depth_ = ancestor_depth; + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + node_receiver_type& receiver, + result_options options) const override + { + const json_location_node_type* ancestor = std::addressof(last); + int index = 0; + while (ancestor != nullptr && index < ancestor_depth_) + { + ancestor = ancestor->parent(); + ++index; + } + + if (ancestor != nullptr) + { + json_location_type path(*ancestor); + pointer ptr = jsoncons::jsonpath::select(root,path); + if (ptr != nullptr) + { + this->tail_select(resources, root, path.last(), *ptr, receiver, options); + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + result_options options, + std::error_code& ec) const override + { + const json_location_node_type* ancestor = std::addressof(last); + int index = 0; + while (ancestor != nullptr && index < ancestor_depth_) + { + ancestor = ancestor->parent(); + ++index; + } + + if (ancestor != nullptr) + { + json_location_type path(*ancestor); + pointer ptr = jsoncons::jsonpath::select(root,path); + if (ptr != nullptr) + { + return this->evaluate_tail(resources, root, path.last(), *ptr, options, ec); + } + else + { + return resources.null_value(); + } + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("parent_node_selector"); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class index_selector final : public base_selector + { + using supertype = base_selector; + + int64_t index_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + index_selector(int64_t index) + : base_selector(), index_(index) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + int64_t slen = static_cast(current.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t i = static_cast(index_); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), receiver, options); + } + else + { + int64_t index = slen + index_; + if (index >= 0 && index < slen) + { + std::size_t i = static_cast(index); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), receiver, options); + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + if (current.is_array()) + { + int64_t slen = static_cast(current.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t i = static_cast(index_); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), options, ec); + } + else + { + int64_t index = slen + index_; + if (index >= 0 && index < slen) + { + std::size_t i = static_cast(index); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), options, ec); + } + else + { + return resources.null_value(); + } + } + } + else + { + return resources.null_value(); + } + } + }; + + template + class wildcard_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + wildcard_selector() + : base_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + for (std::size_t i = 0; i < current.size(); ++i) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), current[i], + receiver, options); + } + } + else if (current.is_object()) + { + for (auto& member : current.object_range()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, member.key(), options), + member.value(), receiver, options); + } + } + //std::cout << "end wildcard_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("wildcard selector"); + s.append(base_selector::to_string(level)); + + return s; + } + }; + + template + class recursive_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + recursive_selector() + : base_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + this->tail_select(resources, root, last, current, receiver, options); + for (std::size_t i = 0; i < current.size(); ++i) + { + select(resources, root, + path_generator_type::generate(resources, last, i, options), current[i], receiver, options); + } + } + else if (current.is_object()) + { + this->tail_select(resources, root, last, current, receiver, options); + for (auto& item : current.object_range()) + { + select(resources, root, + path_generator_type::generate(resources, last, item.key(), options), item.value(), receiver, options); + } + } + //std::cout << "end wildcard_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("wildcard selector"); + s.append(base_selector::to_string(level)); + + return s; + } + }; + + template + class union_selector final : public jsonpath_selector + { + using supertype = jsonpath_selector; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using path_expression_type = path_expression; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + using selector_type = typename supertype::selector_type; + private: + std::vector selectors_; + selector_type* tail_; + public: + union_selector(std::vector&& selectors) + : supertype(true, 11), selectors_(std::move(selectors)), tail_(nullptr) + { + } + + void append_selector(selector_type* tail) override + { + if (tail_ == nullptr) + { + tail_ = tail; + for (auto& selector : selectors_) + { + selector->append_selector(tail); + } + } + else + { + tail_->append_selector(tail); + } + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + for (auto& selector : selectors_) + { + selector->select(resources, root, last, current, receiver, options); + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources,root,last,current,receiver,options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("union selector "); + for (auto& selector : selectors_) + { + s.append(selector->to_string(level+1)); + //s.push_back('\n'); + } + + return s; + } + }; + + template + class filter_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + filter_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + for (std::size_t i = 0; i < current.size(); ++i) + { + std::error_code ec; + value_type r = expr_.evaluate(resources, root, current[i], options, ec); + bool t = ec ? false : detail::is_true(r); + if (t) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current[i], receiver, options); + } + } + } + else if (current.is_object()) + { + for (auto& member : current.object_range()) + { + std::error_code ec; + value_type r = expr_.evaluate(resources, root, member.value(), options, ec); + bool t = ec ? false : detail::is_true(r); + if (t) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, member.key(), options), + member.value(), receiver, options); + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("filter selector "); + s.append(expr_.to_string(level+1)); + + return s; + } + }; + + template + class index_expression_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + index_expression_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + std::error_code ec; + value_type j = expr_.evaluate(resources, root, current, options, ec); + + if (!ec) + { + if (j.template is() && current.is_array()) + { + std::size_t start = j.template as(); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, start, options), + current.at(start), receiver, options); + } + else if (j.is_string() && current.is_object()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, j.as_string(), options), + current.at(j.as_string_view()), receiver, options); + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + //std::cout << "index_expression_selector current: " << current << "\n"; + + value_type j = expr_.evaluate(resources, root, current, options, ec); + + if (!ec) + { + if (j.template is() && current.is_array()) + { + std::size_t start = j.template as(); + return this->evaluate_tail(resources, root, last, current.at(start), options, ec); + } + else if (j.is_string() && current.is_object()) + { + return this->evaluate_tail(resources, root, last, current.at(j.as_string_view()), options, ec); + } + else + { + return resources.null_value(); + } + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("bracket expression selector "); + s.append(expr_.to_string(level+1)); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class slice_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + + slice slice_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using node_receiver_type = typename supertype::node_receiver_type; + + slice_selector(const slice& slic) + : base_selector(), slice_(slic) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + auto start = slice_.get_start(current.size()); + auto end = slice_.get_stop(current.size()); + auto step = slice_.step(); + + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > static_cast(current.size())) + { + end = current.size(); + } + for (int64_t i = start; i < end; i += step) + { + std::size_t j = static_cast(i); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, j, options), + current[j], receiver, options); + } + } + else if (step < 0) + { + if (start >= static_cast(current.size())) + { + start = static_cast(current.size()) - 1; + } + if (end < -1) + { + end = -1; + } + for (int64_t i = start; i > end; i += step) + { + std::size_t j = static_cast(i); + if (j < current.size()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last,j,options), current[j], receiver, options); + } + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver accum(jptr); + select(resources, root, last, current, accum, options); + return *jptr; + } + }; + + template + class function_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + function_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + std::error_code ec; + value_type ref = expr_.evaluate(resources, root, current, options, ec); + if (!ec) + { + this->tail_select(resources, root, last, *resources.create_json(std::move(ref)), receiver, options); + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + value_type ref = expr_.evaluate(resources, root, current, options, ec); + if (!ec) + { + return this->evaluate_tail(resources, root, last, *resources.create_json(std::move(ref)), + options, ec); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("function_selector "); + s.append(expr_.to_string(level+1)); + + return s; + } + }; + +} // namespace detail +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp new file mode 100644 index 0000000..41e41e2 --- /dev/null +++ b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp @@ -0,0 +1,1577 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPOINTER_JSONPOINTER_HPP +#define JSONCONS_JSONPOINTER_JSONPOINTER_HPP + +#include +#include +#include +#include +#include +#include // std::move +#include // system_error +#include // std::enable_if, std::true_type +#include +#include +#include + +namespace jsoncons { namespace jsonpointer { + + namespace detail { + + enum class pointer_state + { + start, + escaped, + delim + }; + + } // namespace detail + + template + std::basic_string escape_string(const std::basic_string& s) + { + std::basic_string result; + for (auto c : s) + { + switch (c) + { + case '~': + result.push_back('~'); + result.push_back('0'); + break; + case '/': + result.push_back('~'); + result.push_back('1'); + break; + default: + result.push_back(c); + break; + } + } + return result; + } + + // basic_json_pointer + + template + class basic_json_pointer + { + public: + // Member types + using char_type = CharT; + using string_type = std::basic_string; + using string_view_type = jsoncons::basic_string_view; + using const_iterator = typename std::vector::const_iterator; + using iterator = const_iterator; + using const_reverse_iterator = typename std::vector::const_reverse_iterator; + using reverse_iterator = const_reverse_iterator; + private: + std::vector tokens_; + public: + // Constructors + basic_json_pointer() + { + } + + basic_json_pointer(const std::vector& tokens) + : tokens_(tokens) + { + } + + basic_json_pointer(std::vector&& tokens) + : tokens_(std::move(tokens)) + { + } + + explicit basic_json_pointer(const string_view_type& s) + { + std::error_code ec; + auto jp = parse(s, ec); + if (ec) + { + throw jsonpointer_error(ec); + } + tokens_ = std::move(jp.tokens_); + } + + explicit basic_json_pointer(const string_view_type& s, std::error_code& ec) + { + auto jp = parse(s, ec); + if (!ec) + { + tokens_ = std::move(jp.tokens_); + } + } + + basic_json_pointer(const basic_json_pointer&) = default; + + basic_json_pointer(basic_json_pointer&&) = default; + + static basic_json_pointer parse(const string_view_type& input, std::error_code& ec) + { + std::vector tokens; + if (input.empty() || (input[0] == '#' && input.size() == 1)) + { + return basic_json_pointer(); + } + + const char_type* p; + const char_type* pend; + string_type unescaped; + if (input[0] == '#') + { + unescaped = unescape_uri_string(input, ec); + p = unescaped.data() + 1; + pend = unescaped.data() + unescaped.size(); + } + else + { + p = input.data(); + pend = input.data() + input.size(); + } + + auto state = jsonpointer::detail::pointer_state::start; + string_type buffer; + + while (p < pend) + { + bool done = false; + while (p < pend && !done) + { + switch (state) + { + case jsonpointer::detail::pointer_state::start: + switch (*p) + { + case '/': + state = jsonpointer::detail::pointer_state::delim; + break; + default: + ec = jsonpointer_errc::expected_slash; + return basic_json_pointer(); + }; + break; + case jsonpointer::detail::pointer_state::delim: + switch (*p) + { + case '/': + done = true; + break; + case '~': + state = jsonpointer::detail::pointer_state::escaped; + break; + default: + buffer.push_back(*p); + break; + }; + break; + case jsonpointer::detail::pointer_state::escaped: + switch (*p) + { + case '0': + buffer.push_back('~'); + state = jsonpointer::detail::pointer_state::delim; + break; + case '1': + buffer.push_back('/'); + state = jsonpointer::detail::pointer_state::delim; + break; + default: + ec = jsonpointer_errc::expected_0_or_1; + return basic_json_pointer(); + }; + break; + } + ++p; + } + tokens.push_back(buffer); + buffer.clear(); + } + if (!buffer.empty()) + { + tokens.push_back(buffer); + } + return basic_json_pointer(tokens); + } + + static string_type escape_uri_string(const string_type& s) + { + string_type escaped; + for (auto ch : s) + { + switch (ch) + { + case '%': + escaped.append(string_type{'%','2','5'}); + break; + case '^': + escaped.append(string_type{'%','5','E'}); + break; + case '|': + escaped.append(string_type{'%','7','C'}); + break; + case '\\': + escaped.append(string_type{'%','5','C'}); + break; + case '\"': + escaped.append(string_type{'%','2','2'}); + break; + case ' ': + escaped.append(string_type{'%','2','0'}); + break; + default: + escaped.push_back(ch); + break; + } + } + + return escaped; + } + + static string_type unescape_uri_string(const string_view_type& s, std::error_code& ec) + { + if (s.size() < 3) + { + return string_type(s); + } + string_type unescaped; + std::size_t last = s.size() - 2; + std::size_t pos = 0; + while (pos < last) + { + if (s[pos] == '%') + { + uint8_t ch; + auto result = jsoncons::detail::to_integer_base16(s.data() + (pos+1), 2, ch); + if (!result) + { + ec = jsonpointer_errc::invalid_uri_escaped_data; + return string_type(s); + } + unescaped.push_back(ch); + pos += 3; + } + else + { + unescaped.push_back(s[pos]); + ++pos; + } + } + while (pos < s.size()) + { + unescaped.push_back(s[pos]); + ++pos; + } + return unescaped; + } + + // operator= + basic_json_pointer& operator=(const basic_json_pointer&) = default; + + basic_json_pointer& operator=(basic_json_pointer&&) = default; + + // Modifiers + + void clear() + { + tokens_.clear(); + } + + basic_json_pointer& operator/=(const string_type& s) + { + tokens_.push_back(s); + return *this; + } + + template + typename std::enable_if::value, basic_json_pointer&>::type + operator/=(IntegerType val) + { + string_type s; + jsoncons::detail::from_integer(val, s); + tokens_.push_back(s); + + return *this; + } + + basic_json_pointer& operator+=(const basic_json_pointer& p) + { + for (const auto& s : p.tokens_) + { + tokens_.push_back(s); + } + return *this; + } + + // Accessors + bool empty() const + { + return tokens_.empty(); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + JSONCONS_DEPRECATED_MSG("Instead, use to_string()") + string_type string() const + { + return to_string(); + } +#endif + string_type to_string() const + { + string_type buffer; + for (const auto& token : tokens_) + { + buffer.push_back('/'); + for (auto c : token) + { + switch (c) + { + case '~': + buffer.push_back('~'); + buffer.push_back('0'); + break; + case '/': + buffer.push_back('~'); + buffer.push_back('1'); + break; + default: + buffer.push_back(c); + break; + } + } + } + return buffer; + } + + string_type to_uri_fragment() const + { + string_type buffer{'#'}; + for (const auto& token : tokens_) + { + buffer.push_back('/'); + string_type s = escape_uri_string(token); + for (auto c : s) + { + switch (c) + { + case '~': + buffer.push_back('~'); + buffer.push_back('0'); + break; + case '/': + buffer.push_back('~'); + buffer.push_back('1'); + break; + default: + buffer.push_back(c); + break; + } + } + } + return buffer; + } + + // Iterators + iterator begin() const + { + return tokens_.begin(); + } + iterator end() const + { + return tokens_.end(); + } + + reverse_iterator rbegin() const + { + return tokens_.rbegin(); + } + reverse_iterator rend() const + { + return tokens_.rend(); + } + + // Non-member functions + friend basic_json_pointer operator/(const basic_json_pointer& lhs, const string_type& rhs) + { + basic_json_pointer p(lhs); + p /= rhs; + return p; + } + + friend basic_json_pointer operator+( const basic_json_pointer& lhs, const basic_json_pointer& rhs ) + { + basic_json_pointer p(lhs); + p += rhs; + return p; + } + + friend bool operator==( const basic_json_pointer& lhs, const basic_json_pointer& rhs ) + { + return lhs.tokens_ == rhs.okens_; + } + + friend bool operator!=( const basic_json_pointer& lhs, const basic_json_pointer& rhs ) + { + return lhs.tokens_ != rhs.tokens_; + } + + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const basic_json_pointer& p ) + { + os << p.to_string(); + return os; + } + }; + + template + typename std::enable_if::value, basic_json_pointer>::type + operator/(const basic_json_pointer& lhs, IntegerType rhs) + { + basic_json_pointer p(lhs); + p /= rhs; + return p; + } + + using json_pointer = basic_json_pointer; + using wjson_pointer = basic_json_pointer; + + #if !defined(JSONCONS_NO_DEPRECATED) + template + using basic_address = basic_json_pointer; + template + using basic_json_ptr = basic_json_pointer; + JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer address; + JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer json_ptr; + JSONCONS_DEPRECATED_MSG("Instead, use wjson_pointer") typedef json_pointer wjson_ptr; + #endif + + namespace detail { + + template + const Json* resolve(const Json* current, const typename Json::string_view_type& buffer, std::error_code& ec) + { + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + ec = jsonpointer_errc::index_exceeds_array_size; + return current; + } + std::size_t index{0}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return current; + } + if (index >= current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return current; + } + current = std::addressof(current->at(index)); + } + else if (current->is_object()) + { + if (!current->contains(buffer)) + { + ec = jsonpointer_errc::key_not_found; + return current; + } + current = std::addressof(current->at(buffer)); + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return current; + } + return current; + } + + template + Json* resolve(Json* current, const typename Json::string_view_type& buffer, bool create_if_missing, std::error_code& ec) + { + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + ec = jsonpointer_errc::index_exceeds_array_size; + return current; + } + std::size_t index{0}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return current; + } + if (index >= current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return current; + } + current = std::addressof(current->at(index)); + } + else if (current->is_object()) + { + if (!current->contains(buffer)) + { + if (create_if_missing) + { + auto r = current->try_emplace(buffer, Json()); + current = std::addressof(r.first->value()); + } + else + { + ec = jsonpointer_errc::key_not_found; + return current; + } + } + else + { + current = std::addressof(current->at(buffer)); + } + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return current; + } + return current; + } + + } // namespace detail + + // get + + template + Json& get(Json& root, + const basic_json_pointer& location, + bool create_if_missing, + std::error_code& ec) + { + if (location.empty()) + { + return root; + } + + Json* current = std::addressof(root); + auto it = location.begin(); + auto end = location.end(); + while (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, *it, create_if_missing, ec); + if (ec) + return *current; + ++it; + } + return *current; + } + + template + typename std::enable_if>::value,Json&>::type + get(Json& root, + const StringSource& location_str, + bool create_if_missing, + std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return root; + } + return get(root, jsonptr, create_if_missing, ec); + } + + template + const Json& get(const Json& root, + const basic_json_pointer& location, + std::error_code& ec) + { + if (location.empty()) + { + return root; + } + + const Json* current = std::addressof(root); + auto it = location.begin(); + auto end = location.end(); + while (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, *it, ec); + if (ec) + return *current; + ++it; + } + return *current; + } + + template + typename std::enable_if>::value,const Json&>::type + get(const Json& root, + const StringSource& location_str, + std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return root; + } + return get(root, jsonptr, ec); + } + + template + Json& get(Json& root, + const basic_json_pointer& location, + std::error_code& ec) + { + return get(root, location, false, ec); + } + + template + typename std::enable_if>::value,Json&>::type + get(Json& root, + const StringSource& location_str, + std::error_code& ec) + { + return get(root, location_str, false, ec); + } + + template + Json& get(Json& root, + const basic_json_pointer& location, + bool create_if_missing = false) + { + std::error_code ec; + Json& j = get(root, location, create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + return j; + } + + template + typename std::enable_if>::value,Json&>::type + get(Json& root, + const StringSource& location_str, + bool create_if_missing = false) + { + std::error_code ec; + Json& result = get(root, location_str, create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + return result; + } + + template + const Json& get(const Json& root, const basic_json_pointer& location) + { + std::error_code ec; + const Json& j = get(root, location, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + return j; + } + + template + typename std::enable_if>::value,const Json&>::type + get(const Json& root, const StringSource& location_str) + { + std::error_code ec; + const Json& j = get(root, location_str, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + return j; + } + + // contains + + template + bool contains(const Json& root, const basic_json_pointer& location) + { + std::error_code ec; + get(root, location, ec); + return !ec ? true : false; + } + + template + typename std::enable_if>::value,bool>::type + contains(const Json& root, const StringSource& location_str) + { + std::error_code ec; + get(root, location_str, ec); + return !ec ? true : false; + } + + template + void add(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + Json* current = std::addressof(root); + + std::basic_string buffer; + auto it = location.begin(); + auto end = location.end(); + while (it != end) + { + buffer = *it; + ++it; + if (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); + if (ec) + return; + } + } + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + current->emplace_back(std::forward(value)); + current = std::addressof(current->at(current->size()-1)); + } + else + { + std::size_t index{0}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return; + } + if (index > current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + if (index == current->size()) + { + current->emplace_back(std::forward(value)); + current = std::addressof(current->at(current->size()-1)); + } + else + { + auto it2 = current->insert(current->array_range().begin()+index,std::forward(value)); + current = std::addressof(*it2); + } + } + } + else if (current->is_object()) + { + auto r = current->insert_or_assign(buffer,std::forward(value)); + current = std::addressof(r.first->value()); + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return; + } + } + + // add + template + typename std::enable_if>::value,void>::type + add(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return; + } + add(root, jsonptr, std::forward(value), create_if_missing, ec); + } + + template + void add(Json& root, + const basic_json_pointer& location, + T&& value, + std::error_code& ec) + { + add(root, location, std::forward(value), false, ec); + } + + template + typename std::enable_if>::value,void>::type + add(Json& root, + const StringSource& location_str, + T&& value, + std::error_code& ec) + { + add(root, location_str, std::forward(value), false, ec); + } + + template + void add(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + add(root, location, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + template + typename std::enable_if>::value,void>::type + add(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + add(root, location_str, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + // add_if_absent + + template + void add_if_absent(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + Json* current = std::addressof(root); + + std::basic_string buffer; + auto it = location.begin(); + auto end = location.end(); + + while (it != end) + { + buffer = *it; + ++it; + if (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); + if (ec) + return; + } + } + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + current->emplace_back(std::forward(value)); + current = std::addressof(current->at(current->size()-1)); + } + else + { + std::size_t index{0}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return; + } + if (index > current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + if (index == current->size()) + { + current->emplace_back(std::forward(value)); + current = std::addressof(current->at(current->size()-1)); + } + else + { + auto it2 = current->insert(current->array_range().begin()+index,std::forward(value)); + current = std::addressof(*it2); + } + } + } + else if (current->is_object()) + { + if (current->contains(buffer)) + { + ec = jsonpointer_errc::key_already_exists; + return; + } + else + { + auto r = current->try_emplace(buffer,std::forward(value)); + current = std::addressof(r.first->value()); + } + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return; + } + } + + template + typename std::enable_if>::value,void>::type + add_if_absent(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return; + } + add_if_absent(root, jsonptr, std::forward(value), create_if_missing, ec); + } + + template + typename std::enable_if>::value,void>::type + add_if_absent(Json& root, + const StringSource& location, + T&& value, + std::error_code& ec) + { + add_if_absent(root, location, std::forward(value), false, ec); + } + + template + typename std::enable_if>::value,void>::type + add_if_absent(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + add_if_absent(root, location_str, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + template + void add_if_absent(Json& root, + const basic_json_pointer& location, + T&& value, + std::error_code& ec) + { + add_if_absent(root, location, std::forward(value), false, ec); + } + + template + void add_if_absent(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + add_if_absent(root, location, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + // remove + + template + void remove(Json& root, const basic_json_pointer& location, std::error_code& ec) + { + Json* current = std::addressof(root); + + std::basic_string buffer; + auto it = location.begin(); + auto end = location.end(); + + while (it != end) + { + buffer = *it; + ++it; + if (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec); + if (ec) + return; + } + } + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + else + { + std::size_t index{0}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return; + } + if (index >= current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + current->erase(current->array_range().begin()+index); + } + } + else if (current->is_object()) + { + if (!current->contains(buffer)) + { + ec = jsonpointer_errc::key_not_found; + return; + } + else + { + current->erase(buffer); + } + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return; + } + } + + template + typename std::enable_if>::value,void>::type + remove(Json& root, const StringSource& location_str, std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return; + } + remove(root, jsonptr, ec); + } + + template + typename std::enable_if>::value,void>::type + remove(Json& root, const StringSource& location_str) + { + std::error_code ec; + remove(root, location_str, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + template + void remove(Json& root, const basic_json_pointer& location) + { + std::error_code ec; + remove(root, location, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + // replace + + template + void replace(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + Json* current = std::addressof(root); + + std::basic_string buffer; + auto it = location.begin(); + auto end = location.end(); + + while (it != end) + { + buffer = *it; + ++it; + if (it != end) + { + current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec); + if (ec) + return; + } + } + if (current->is_array()) + { + if (buffer.size() == 1 && buffer[0] == '-') + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + else + { + std::size_t index{}; + auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index); + if (!result) + { + ec = jsonpointer_errc::invalid_index; + return; + } + if (index >= current->size()) + { + ec = jsonpointer_errc::index_exceeds_array_size; + return; + } + current->at(index) = std::forward(value); + } + } + else if (current->is_object()) + { + if (!current->contains(buffer)) + { + if (create_if_missing) + { + current->try_emplace(buffer,std::forward(value)); + } + else + { + ec = jsonpointer_errc::key_not_found; + return; + } + } + else + { + auto r = current->insert_or_assign(buffer,std::forward(value)); + current = std::addressof(r.first->value()); + } + } + else + { + ec = jsonpointer_errc::expected_object_or_array; + return; + } + } + + template + typename std::enable_if>::value,void>::type + replace(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + auto jsonptr = basic_json_pointer::parse(location_str, ec); + if (ec) + { + return; + } + replace(root, jsonptr, std::forward(value), create_if_missing, ec); + } + + template + typename std::enable_if>::value,void>::type + replace(Json& root, + const StringSource& location_str, + T&& value, + std::error_code& ec) + { + replace(root, location_str, std::forward(value), false, ec); + } + + template + typename std::enable_if>::value,void>::type + replace(Json& root, + const StringSource& location_str, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + replace(root, location_str, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + template + void replace(Json& root, + const basic_json_pointer& location, + T&& value, + std::error_code& ec) + { + replace(root, location, std::forward(value), false, ec); + } + + template + void replace(Json& root, + const basic_json_pointer& location, + T&& value, + bool create_if_missing = false) + { + std::error_code ec; + replace(root, location, std::forward(value), create_if_missing, ec); + if (ec) + { + JSONCONS_THROW(jsonpointer_error(ec)); + } + } + + template + typename std::enable_if::value>::type + escape(const String& s, Result& result) + { + for (auto c : s) + { + if (c == '~') + { + result.push_back('~'); + result.push_back('0'); + } + else if (c == '/') + { + result.push_back('~'); + result.push_back('1'); + } + else + { + result.push_back(c); + } + } + } + + template + std::basic_string escape(const jsoncons::basic_string_view& s) + { + std::basic_string result; + + for (auto c : s) + { + if (c == '~') + { + result.push_back('~'); + result.push_back('0'); + } + else if (c == '/') + { + result.push_back('~'); + result.push_back('1'); + } + else + { + result.push_back(c); + } + } + return result; + } + + // flatten + + template + void flatten_(const std::basic_string& parent_key, + const Json& parent_value, + Json& result) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + switch (parent_value.type()) + { + case json_type::array_value: + { + if (parent_value.empty()) + { + // Flatten empty array to null + //result.try_emplace(parent_key, null_type{}); + //result[parent_key] = parent_value; + result.try_emplace(parent_key, parent_value); + } + else + { + for (std::size_t i = 0; i < parent_value.size(); ++i) + { + string_type key(parent_key); + key.push_back('/'); + jsoncons::detail::from_integer(i,key); + flatten_(key, parent_value.at(i), result); + } + } + break; + } + + case json_type::object_value: + { + if (parent_value.empty()) + { + // Flatten empty object to null + //result.try_emplace(parent_key, null_type{}); + //result[parent_key] = parent_value; + result.try_emplace(parent_key, parent_value); + } + else + { + for (const auto& item : parent_value.object_range()) + { + string_type key(parent_key); + key.push_back('/'); + escape(jsoncons::basic_string_view(item.key().data(),item.key().size()), key); + flatten_(key, item.value(), result); + } + } + break; + } + + default: + { + // add primitive parent_value with its reference string + //result[parent_key] = parent_value; + result.try_emplace(parent_key, parent_value); + break; + } + } + } + + template + Json flatten(const Json& value) + { + Json result; + std::basic_string parent_key; + flatten_(parent_key, value, result); + return result; + } + + + // unflatten + + enum class unflatten_options {none,assume_object = 1 + #if !defined(JSONCONS_NO_DEPRECATED) +,object = assume_object +#endif +}; + + template + Json safe_unflatten (Json& value) + { + if (!value.is_object() || value.empty()) + { + return value; + } + bool safe = true; + std::size_t index = 0; + for (const auto& item : value.object_range()) + { + std::size_t n; + auto r = jsoncons::detail::to_integer_decimal(item.key().data(),item.key().size(), n); + if (!r || (index++ != n)) + { + safe = false; + break; + } + } + + if (safe) + { + Json j(json_array_arg); + j.reserve(value.size()); + for (auto& item : value.object_range()) + { + j.emplace_back(std::move(item.value())); + } + Json a(json_array_arg); + for (auto& item : j.array_range()) + { + a.emplace_back(safe_unflatten (item)); + } + return a; + } + else + { + Json o(json_object_arg); + for (auto& item : value.object_range()) + { + o.try_emplace(item.key(), safe_unflatten (item.value())); + } + return o; + } + } + + template + jsoncons::optional try_unflatten_array(const Json& value) + { + using char_type = typename Json::char_type; + + if (JSONCONS_UNLIKELY(!value.is_object())) + { + JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); + } + Json result; + + for (const auto& item: value.object_range()) + { + Json* part = &result; + basic_json_pointer ptr(item.key()); + std::size_t index = 0; + for (auto it = ptr.begin(); it != ptr.end(); ) + { + auto s = *it; + size_t n{0}; + auto r = jsoncons::detail::to_integer_decimal(s.data(), s.size(), n); + if (r.ec == jsoncons::detail::to_integer_errc() && (index++ == n)) + { + if (!part->is_array()) + { + *part = Json(json_array_arg); + } + if (++it != ptr.end()) + { + if (n+1 > part->size()) + { + Json& ref = part->emplace_back(); + part = std::addressof(ref); + } + else + { + part = &part->at(n); + } + } + else + { + Json& ref = part->emplace_back(item.value()); + part = std::addressof(ref); + } + } + else if (part->is_object()) + { + if (++it != ptr.end()) + { + auto res = part->try_emplace(s,Json()); + part = &(res.first->value()); + } + else + { + auto res = part->try_emplace(s, item.value()); + part = &(res.first->value()); + } + } + else + { + return jsoncons::optional(); + } + } + } + + return result; + } + + template + Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none) + { + using char_type = typename Json::char_type; + + if (JSONCONS_UNLIKELY(!value.is_object())) + { + JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); + } + Json result; + + for (const auto& item: value.object_range()) + { + Json* part = &result; + basic_json_pointer ptr(item.key()); + for (auto it = ptr.begin(); it != ptr.end(); ) + { + auto s = *it; + if (++it != ptr.end()) + { + auto res = part->try_emplace(s,Json()); + part = &(res.first->value()); + } + else + { + auto res = part->try_emplace(s, item.value()); + part = &(res.first->value()); + } + } + } + + return options == unflatten_options::none ? safe_unflatten (result) : result; + } + + template + Json unflatten(const Json& value, unflatten_options options = unflatten_options::none) + { + if (options == unflatten_options::none) + { + jsoncons::optional j = try_unflatten_array(value); + return j ? *j : unflatten_to_object(value,options); + } + else + { + return unflatten_to_object(value,options); + } + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template + JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&)") + void insert_or_assign(Json& root, const std::basic_string& location, const Json& value) + { + add(root, location, value); + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&, std::error_code&)") + void insert_or_assign(Json& root, const std::basic_string& location, const Json& value, std::error_code& ec) + { + add(root, location, value, ec); + } + template + void insert(Json& root, + const std::basic_string& location, + T&& value, + bool create_if_missing, + std::error_code& ec) + { + add_if_absent(root,location,std::forward(value),create_if_missing,ec); + } + + template + void insert(Json& root, + const std::basic_string& location, + T&& value, + std::error_code& ec) + { + add_if_absent(root, location, std::forward(value), ec); + } + + template + void insert(Json& root, + const std::basic_string& location, + T&& value, + bool create_if_missing = false) + { + add_if_absent(root, location, std::forward(value), create_if_missing); + } +#endif + +} // namespace jsonpointer +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp new file mode 100644 index 0000000..a0cfeff --- /dev/null +++ b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp @@ -0,0 +1,119 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPOINTER_JSONPOINTER_ERROR_HPP +#define JSONCONS_JSONPOINTER_JSONPOINTER_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace jsonpointer { + +class jsonpointer_error : public std::system_error, public virtual json_exception +{ +public: + jsonpointer_error(const std::error_code& ec) + : std::system_error(ec) + { + } + jsonpointer_error(const std::error_code& ec, const std::string& what_arg) + : std::system_error(ec, what_arg) + { + } + jsonpointer_error(const std::error_code& ec, const char* what_arg) + : std::system_error(ec, what_arg) + { + } + jsonpointer_error(const jsonpointer_error& other) = default; + + jsonpointer_error(jsonpointer_error&& other) = default; + + const char* what() const noexcept override + { + return std::system_error::what(); + } +}; + +enum class jsonpointer_errc +{ + success = 0, + expected_slash = 1, + index_exceeds_array_size, + expected_0_or_1, + invalid_index, + key_not_found, + key_already_exists, + expected_object_or_array, + end_of_input, + unexpected_end_of_input, + argument_to_unflatten_invalid, + invalid_flattened_key, + invalid_uri_escaped_data +}; + +class jsonpointer_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/jsonpointer"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case jsonpointer_errc::expected_slash: + return "Expected /"; + case jsonpointer_errc::index_exceeds_array_size: + return "Index exceeds array size"; + case jsonpointer_errc::expected_0_or_1: + return "Expected '0' or '1' after escape character '~'"; + case jsonpointer_errc::key_not_found: + return "Key not found"; + case jsonpointer_errc::invalid_index: + return "Invalid array index"; + case jsonpointer_errc::key_already_exists: + return "Key already exists"; + case jsonpointer_errc::expected_object_or_array: + return "Expected object or array"; + case jsonpointer_errc::end_of_input: + return "Unexpected end of input"; + case jsonpointer_errc::unexpected_end_of_input: + return "Unexpected end of jsonpointer input"; + case jsonpointer_errc::argument_to_unflatten_invalid: + return "Argument to unflatten must be an object"; + case jsonpointer_errc::invalid_flattened_key: + return "Flattened key is invalid"; + default: + return "Unknown jsonpointer error"; + } + } +}; + +inline +const std::error_category& jsonpointer_error_category() +{ + static jsonpointer_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(jsonpointer_errc result) +{ + return std::error_code(static_cast(result),jsonpointer_error_category()); +} + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/jsonschema/format_validator.hpp b/include/jsoncons_ext/jsonschema/format_validator.hpp new file mode 100644 index 0000000..312bf41 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/format_validator.hpp @@ -0,0 +1,968 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_FORMAT_VALIDATOR_HPP +#define JSONCONS_JSONSCHEMA_FORMAT_VALIDATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(JSONCONS_HAS_STD_REGEX) +#include +#endif + +namespace jsoncons { +namespace jsonschema { + + inline + bool is_atext( char c) + { + switch (c) + { + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '*': + case '+': + case '-': + case '/': + case '=': + case '?': + case '^': + case '_': + case '`': + case '{': + case '|': + case '}': + case '~': + return true; + default: + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + } + + inline + bool is_dtext( char c) + { + return (c >= 33 && c <= 90) || (c >= 94 && c <= 126); + } + + // RFC 5322, section 3.4.1 + inline + bool validate_email_rfc5322(const std::string& s) + { + enum class state_t {local_part,atom,dot_atom,quoted_string,amp,domain}; + + state_t state = state_t::local_part; + std::size_t part_length = 0; + + for (char c : s) + { + switch (state) + { + case state_t::local_part: + { + if (is_atext(c)) + { + state = state_t::atom; + } + else if (c == '"') + { + state = state_t::quoted_string; + } + else + { + return false; + } + break; + } + case state_t::dot_atom: + { + if (is_atext(c)) + { + ++part_length; + state = state_t::atom; + } + else + return false; + break; + } + case state_t::atom: + { + switch (c) + { + case '@': + state = state_t::domain; + part_length = 0; + break; + case '.': + state = state_t::dot_atom; + ++part_length; + break; + default: + if (is_atext(c)) + ++part_length; + else + return false; + break; + } + break; + } + case state_t::quoted_string: + { + if (c == '\"') + { + state = state_t::amp; + } + else + { + ++part_length; + } + break; + } + case state_t::amp: + { + if (c == '@') + { + state = state_t::domain; + part_length = 0; + } + else + { + return false; + } + break; + } + case state_t::domain: + { + if (is_dtext(c)) + { + ++part_length; + } + else + { + return false; + } + break; + } + } + } + + return state == state_t::domain && part_length > 0; + } + + // RFC 2673, Section 3.2 + + inline + bool validate_ipv6_rfc2373(const std::string& s) + { + enum class state_t{start,expect_hexdig_or_unspecified, + hexdig, decdig,expect_unspecified, unspecified}; + + state_t state = state_t::start; + + std::size_t digit_count = 0; + std::size_t piece_count = 0; + std::size_t piece_count2 = 0; + bool has_unspecified = false; + std::size_t dec_value = 0; + + for (std::size_t i = 0; i < s.length(); ++i) + { + char c = s[i]; + switch (state) + { + case state_t::start: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + state = state_t::hexdig; + ++digit_count; + piece_count = 0; + break; + case ':': + if (!has_unspecified) + { + state = state_t::expect_unspecified; + } + else + { + return false; + } + break; + default: + return false; + } + break; + } + case state_t::expect_hexdig_or_unspecified: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + dec_value = dec_value*10 + static_cast(c - '0'); // just in case this piece is followed by a dot + state = state_t::hexdig; + ++digit_count; + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + state = state_t::hexdig; + ++digit_count; + break; + case ':': + if (!has_unspecified) + { + has_unspecified = true; + state = state_t::unspecified; + } + else + { + return false; + } + break; + default: + return false; + } + break; + } + case state_t::expect_unspecified: + { + if (c == ':') + { + has_unspecified = true; + state = state_t::unspecified; + } + else + { + return false; + } + break; + } + case state_t::hexdig: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + ++digit_count; + break; + case ':': + if (digit_count <= 4) + { + ++piece_count; + digit_count = 0; + dec_value = 0; + state = state_t::expect_hexdig_or_unspecified; + } + else + { + return false; + } + break; + case '.': + if (piece_count == 6 || has_unspecified) + { + ++piece_count2; + state = state_t::decdig; + dec_value = 0; + } + else + { + return false; + } + break; + default: + return false; + } + break; + } + case state_t::decdig: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + dec_value = dec_value*10 + static_cast(c - '0'); + ++digit_count; + break; + case '.': + if (dec_value > 0xff) + { + return false; + } + digit_count = 0; + dec_value = 0; + ++piece_count2; + break; + default: + return false; + } + break; + } + case state_t::unspecified: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + state = state_t::hexdig; + ++digit_count; + break; + default: + return false; + } + break; + } + default: + return false; + } + } + + switch (state) + { + case state_t::unspecified: + return piece_count <= 8; + case state_t::hexdig: + if (digit_count <= 4) + { + ++piece_count; + return digit_count > 0 && (piece_count == 8 || (has_unspecified && piece_count <= 8)); + } + else + { + return false; + } + case state_t::decdig: + ++piece_count2; + if (dec_value > 0xff) + { + return false; + } + return digit_count > 0 && piece_count2 == 4; + default: + return false; + } + } + + // RFC 2673, Section 3.2 + + inline + bool validate_ipv4_rfc2673(const std::string& s) + { + enum class state_t {expect_indicator_or_dotted_quad,decbyte, + bindig, octdig, hexdig}; + + state_t state = state_t::expect_indicator_or_dotted_quad; + + std::size_t digit_count = 0; + std::size_t decbyte_count = 0; + std::size_t value = 0; + + for (std::size_t i = 0; i < s.length(); ++i) + { + char c = s[i]; + switch (state) + { + case state_t::expect_indicator_or_dotted_quad: + { + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = state_t::decbyte; + decbyte_count = 0; + digit_count = 1; + value = 0; + break; + case 'b': + state = state_t::bindig; + digit_count = 0; + break; + case 'o': + state = state_t::octdig; + digit_count = 0; + break; + case 'x': + state = state_t::hexdig; + digit_count = 0; + break; + default: + return false; + } + break; + } + case state_t::bindig: + { + if (digit_count >= 256) + { + return false; + } + switch (c) + { + case '0':case '1': + ++digit_count; + break; + default: + return false; + } + break; + } + case state_t::octdig: + { + if (digit_count >= 86) + { + return false; + } + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': + ++digit_count; + break; + default: + return false; + } + break; + } + case state_t::hexdig: + { + if (digit_count >= 64) + { + return false; + } + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + ++digit_count; + break; + default: + return false; + } + break; + } + case state_t::decbyte: + { + if (decbyte_count >= 4) + { + return false; + } + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + { + if (digit_count >= 3) + { + return false; + } + ++digit_count; + value = value*10 + static_cast(c - '0'); + if (value > 255) + { + return false; + } + break; + } + case '.': + if (decbyte_count > 3) + { + return false; + } + ++decbyte_count; + digit_count = 0; + value = 0; + break; + default: + return false; + } + break; + } + default: + return false; + } + } + + switch (state) + { + case state_t::decbyte: + if (digit_count > 0) + { + ++decbyte_count; + } + else + { + return false; + } + return (decbyte_count == 4) ? true : false; + case state_t::bindig: + return digit_count > 0 ? true : false; + case state_t::octdig: + return digit_count > 0 ? true : false; + case state_t::hexdig: + return digit_count > 0 ? true : false; + default: + return false; + } + } + + // RFC 1034, Section 3.1 + inline + bool validate_hostname_rfc1034(const std::string& hostname) + { + enum class state_t {start_label,expect_letter_or_digit_or_hyphen_or_dot}; + + state_t state = state_t::start_label; + std::size_t length = hostname.length() - 1; + std::size_t label_length = 0; + + for (std::size_t i = 0; i < length; ++i) + { + char c = hostname[i]; + switch (state) + { + case state_t::start_label: + { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + { + ++label_length; + state = state_t::expect_letter_or_digit_or_hyphen_or_dot; + } + else + { + return false; + } + break; + } + case state_t::expect_letter_or_digit_or_hyphen_or_dot: + { + if (c == '.') + { + label_length = 0; + state = state_t::start_label; + } + else if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c < '9') || c == '-')) + { + return false; + } + if (++label_length > 63) + { + return false; + } + break; + } + } + } + + char last = hostname.back(); + if (!((last >= 'a' && last <= 'z') || (last >= 'A' && last <= 'Z') || (last >= '0' && last < '9'))) + { + return false; + } + return true; + } + + inline + bool is_leap_year(std::size_t year) + { + return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + } + + inline + std::size_t days_in_month(std::size_t year, std::size_t month) + { + switch (month) + { + case 1: return 31; + case 2: return is_leap_year(year) ? 29 : 28; + case 3: return 31; + case 4: return 30; + case 5: return 31; + case 6: return 30; + case 7: return 31; + case 8: return 31; + case 9: return 30; + case 10: return 31; + case 11: return 30; + case 12: return 31; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + enum class date_time_type {date_time,date,time}; + // RFC 3339, Section 5.6 + inline + bool validate_date_time_rfc3339(const std::string& s, date_time_type type) + { + enum class state_t {fullyear,month,mday,hour,minute,second,secfrac,z,offset_hour,offset_minute}; + + std::size_t piece_length = 0; + std::size_t year = 0; + std::size_t month = 0; + std::size_t mday = 0; + std::size_t value = 0; + state_t state = (type == date_time_type::time) ? state_t::hour : state_t::fullyear; + + for (char c : s) + { + switch (state) + { + case state_t::fullyear: + { + if (piece_length < 4 && (c >= '0' && c <= '9')) + { + piece_length++; + year = year*10 + static_cast(c - '0'); + } + else if (c == '-' && piece_length == 4) + { + state = state_t::month; + piece_length = 0; + } + else + { + return false; + } + break; + } + case state_t::month: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + month = month*10 + static_cast(c - '0'); + } + else if (c == '-' && piece_length == 2 && (month >=1 && month <= 12)) + { + state = state_t::mday; + piece_length = 0; + } + else + { + return false; + } + break; + } + case state_t::mday: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + mday = mday *10 + static_cast(c - '0'); + } + else if ((c == 'T' || c == 't') && piece_length == 2 && (mday <= days_in_month(year, month))) + { + piece_length = 0; + state = state_t::hour; + } + else + { + return false; + } + break; + } + case state_t::hour: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + value = value*10 + static_cast(c - '0'); + } + else if (c == ':' && piece_length == 2 && (/*value >=0 && */ value <= 23)) + { + state = state_t::minute; + value = 0; + piece_length = 0; + } + else + { + return false; + } + break; + } + case state_t::minute: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + value = value*10 + static_cast(c - '0'); + } + else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 59)) + { + state = state_t::second; + value = 0; + piece_length = 0; + } + else + { + return false; + } + break; + } + case state_t::second: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + value = value*10 + static_cast(c - '0'); + } + else if (piece_length == 2 && (/*value >=0 && */value <= 60)) // 00-58, 00-59, 00-60 based on leap second rules + { + switch (c) + { + case '.': + value = 0; + state = state_t::secfrac; + break; + case '+': + case '-': + value = 0; + piece_length = 0; + state = state_t::offset_hour; + break; + case 'Z': + case 'z': + state = state_t::z; + break; + default: + return false; + } + } + else + { + return false; + } + break; + } + case state_t::secfrac: + { + if (c >= '0' && c <= '9') + { + value = value*10 + static_cast(c - '0'); + } + else + { + switch (c) + { + case '+': + case '-': + value = 0; + piece_length = 0; + state = state_t::offset_hour; + break; + case 'Z': + case 'z': + state = state_t::z; + break; + default: + return false; + } + } + break; + } + case state_t::offset_hour: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + value = value*10 + static_cast(c - '0'); + } + else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 23)) + { + value = 0; + piece_length = 0; + state = state_t::offset_minute; + } + else + { + return false; + } + break; + } + case state_t::offset_minute: + { + if (piece_length < 2 && (c >= '0' && c <= '9')) + { + piece_length++; + value = value*10 + static_cast(c - '0'); + } + else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 59)) + { + value = 0; + piece_length = 0; + } + else + { + return false; + } + break; + } + case state_t::z: + return false; + } + } + + if (type == date_time_type::date) + { + return state == state_t::mday && piece_length == 2 && (mday >= 1 && mday <= days_in_month(year, month)); + } + else + { + return state == state_t::offset_minute || state == state_t::z || state == state_t::secfrac; + } + } + + // format checkers + using format_checker = std::function; + + inline + void rfc3339_date_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { + if (!validate_date_time_rfc3339(value,date_time_type::date)) + { + reporter.error(validation_output("date", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a RFC 3339 date string")); + } + } + + inline + void rfc3339_time_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string &value, + error_reporter& reporter) + { + if (!validate_date_time_rfc3339(value, date_time_type::time)) + { + reporter.error(validation_output("time", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a RFC 3339 time string")); + } + } + + inline + void rfc3339_date_time_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string &value, + error_reporter& reporter) + { + if (!validate_date_time_rfc3339(value, date_time_type::date_time)) + { + reporter.error(validation_output("date-time", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a RFC 3339 date-time string")); + } + } + + inline + void email_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { + if (!validate_email_rfc5322(value)) + { + reporter.error(validation_output("email", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a valid email address as defined by RFC 5322")); + } + } + + inline + void hostname_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { + if (!validate_hostname_rfc1034(value)) + { + reporter.error(validation_output("hostname", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a valid hostname as defined by RFC 3986 Appendix A")); + } + } + + inline + void ipv4_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { + if (!validate_ipv4_rfc2673(value)) + { + reporter.error(validation_output("ipv4", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a valid IPv4 address as defined by RFC 2673")); + } + } + + inline + void ipv6_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { + if (!validate_ipv6_rfc2373(value)) + { + reporter.error(validation_output("ipv6", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a valid IPv6 address as defined by RFC 2373")); + } + } + + inline + void regex_check(const std::string& absolute_keyword_location, + const jsonpointer::json_pointer& instance_location, + const std::string& value, + error_reporter& reporter) + { +#if defined(JSONCONS_HAS_STD_REGEX) + try + { + std::regex re(value, std::regex::ECMAScript); + } + catch (const std::exception& e) + { + reporter.error(validation_output("pattern", + absolute_keyword_location, + instance_location.to_uri_fragment(), + "\"" + value + "\" is not a valid ECMAScript regular expression. " + e.what())); + } +#endif + } + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_FORMAT_CHECKERS_HPP diff --git a/include/jsoncons_ext/jsonschema/json_validator.hpp b/include/jsoncons_ext/jsonschema/json_validator.hpp new file mode 100644 index 0000000..87bec58 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/json_validator.hpp @@ -0,0 +1,120 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_JSON_VALIDATOR_HPP +#define JSONCONS_JSONSCHEMA_JSON_VALIDATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonschema { + + class throwing_error_reporter : public error_reporter + { + void do_error(const validation_output& o) override + { + JSONCONS_THROW(validation_error(o.message())); + } + }; + + class fail_early_reporter : public error_reporter + { + void do_error(const validation_output&) override + { + } + public: + fail_early_reporter() + : error_reporter(true) + { + } + }; + + using error_reporter_t = std::function; + + struct error_reporter_adaptor : public error_reporter + { + error_reporter_t reporter_; + + error_reporter_adaptor(const error_reporter_t& reporter) + : reporter_(reporter) + { + } + private: + void do_error(const validation_output& e) override + { + reporter_(e); + } + }; + + template + class json_validator + { + std::shared_ptr> root_; + + public: + json_validator(std::shared_ptr> root) + : root_(root) + { + } + + json_validator(json_validator &&) = default; + json_validator &operator=(json_validator &&) = default; + + json_validator(json_validator const &) = delete; + json_validator &operator=(json_validator const &) = delete; + + ~json_validator() = default; + + // Validate input JSON against a JSON Schema with a default throwing error reporter + Json validate(const Json& instance) const + { + throwing_error_reporter reporter; + jsonpointer::json_pointer instance_location("#"); + Json patch(json_array_arg); + + root_->validate(instance, instance_location, reporter, patch); + return patch; + } + + // Validate input JSON against a JSON Schema + bool is_valid(const Json& instance) const + { + fail_early_reporter reporter; + jsonpointer::json_pointer instance_location("#"); + Json patch(json_array_arg); + + root_->validate(instance, instance_location, reporter, patch); + return reporter.error_count() == 0; + } + + // Validate input JSON against a JSON Schema with a provided error reporter + template + typename std::enable_if::value,Json>::type + validate(const Json& instance, const Reporter& reporter) const + { + jsonpointer::json_pointer instance_location("#"); + Json patch(json_array_arg); + + error_reporter_adaptor adaptor(reporter); + root_->validate(instance, instance_location, adaptor, patch); + return patch; + } + }; + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_JSON_VALIDATOR_HPP diff --git a/include/jsoncons_ext/jsonschema/jsonschema.hpp b/include/jsoncons_ext/jsonschema/jsonschema.hpp new file mode 100644 index 0000000..e2c4210 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/jsonschema.hpp @@ -0,0 +1,13 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_JSONSCHEMA_HPP +#define JSONCONS_JSONSCHEMA_JSONSCHEMA_HPP + +#include +#include + +#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_HPP diff --git a/include/jsoncons_ext/jsonschema/jsonschema_error.hpp b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp new file mode 100644 index 0000000..7cb1061 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp @@ -0,0 +1,105 @@ +/// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_JSONSCHEMA_ERROR_HPP +#define JSONCONS_JSONSCHEMA_JSONSCHEMA_ERROR_HPP + +#include +#include + +namespace jsoncons { +namespace jsonschema { + + class schema_error : public std::runtime_error, public virtual json_exception + { + public: + schema_error(const std::string& message) + : std::runtime_error(message) + { + } + + const char* what() const noexcept override + { + return std::runtime_error::what(); + } + }; + + class validation_error : public std::runtime_error, public virtual json_exception + { + public: + validation_error(const std::string& message) + : std::runtime_error(message) + { + } + + const char* what() const noexcept override + { + return std::runtime_error::what(); + } + }; + + class validation_output + { + std::string keyword_; + std::string absolute_keyword_location_; + std::string instance_location_; + std::string message_; + std::vector nested_errors_; + public: + validation_output(std::string keyword, + std::string absolute_keyword_location, + std::string instance_location, + std::string message) + : keyword_(std::move(keyword)), + absolute_keyword_location_(std::move(absolute_keyword_location)), + instance_location_(std::move(instance_location)), + message_(std::move(message)) + { + } + + validation_output(const std::string& keyword, + const std::string& absolute_keyword_location, + const std::string& instance_location, + const std::string& message, + const std::vector& nested_errors) + : keyword_(keyword), + absolute_keyword_location_(absolute_keyword_location), + instance_location_(instance_location), + message_(message), + nested_errors_(nested_errors) + { + } + + const std::string& instance_location() const + { + return instance_location_; + } + + const std::string& message() const + { + return message_; + } + + const std::string& absolute_keyword_location() const + { + return absolute_keyword_location_; + } + + const std::string& keyword() const + { + return keyword_; + } + + const std::vector& nested_errors() const + { + return nested_errors_; + } + }; + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_ERROR_HPP diff --git a/include/jsoncons_ext/jsonschema/jsonschema_version.hpp b/include/jsoncons_ext/jsonschema/jsonschema_version.hpp new file mode 100644 index 0000000..bf0afff --- /dev/null +++ b/include/jsoncons_ext/jsonschema/jsonschema_version.hpp @@ -0,0 +1,18 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP +#define JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP + +#include + +namespace jsoncons { +namespace jsonschema { + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP diff --git a/include/jsoncons_ext/jsonschema/keyword_validator.hpp b/include/jsoncons_ext/jsonschema/keyword_validator.hpp new file mode 100644 index 0000000..249c7d0 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/keyword_validator.hpp @@ -0,0 +1,1745 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_HPP +#define JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(JSONCONS_HAS_STD_REGEX) +#include +#endif + +namespace jsoncons { +namespace jsonschema { + + template + class abstract_keyword_validator_factory + { + public: + using validator_pointer = typename keyword_validator::self_pointer; + + virtual ~abstract_keyword_validator_factory() = default; + + virtual validator_pointer make_keyword_validator(const Json& schema, + const std::vector& uris, + const std::vector& keys) = 0; + virtual validator_pointer make_required_validator(const std::vector& uris, + const std::vector& items) = 0; + + virtual validator_pointer make_null_validator(const std::vector& uris) = 0; + + virtual validator_pointer make_true_validator(const std::vector& uris) = 0; + + virtual validator_pointer make_false_validator(const std::vector& uris) = 0; + + virtual validator_pointer make_object_validator(const Json& sch, + const std::vector& uris) = 0; + + virtual validator_pointer make_array_validator(const Json& sch, + const std::vector& uris) = 0; + + virtual validator_pointer make_string_validator(const Json& sch, + const std::vector& uris) = 0; + + virtual validator_pointer make_boolean_validator(const std::vector& uris) = 0; + + virtual validator_pointer make_integer_validator(const Json& sch, + const std::vector& uris, + std::set& keywords) = 0; + + virtual validator_pointer make_number_validator(const Json& sch, + const std::vector& uris, + std::set& keywords) = 0; + + virtual validator_pointer make_not_validator(const Json& schema, + const std::vector& uris) = 0; + + virtual validator_pointer make_all_of_validator(const Json& schema, + const std::vector& uris) = 0; + + virtual validator_pointer make_any_of_validator(const Json& schema, + const std::vector& uris) = 0; + + virtual validator_pointer make_one_of_validator(const Json& schema, + const std::vector& uris) = 0; + + virtual validator_pointer make_type_validator(const Json& schema, + const std::vector& uris) = 0; + }; + + struct collecting_error_reporter : public error_reporter + { + std::vector errors; + + private: + void do_error(const validation_output& o) override + { + errors.push_back(o); + } + }; + + // string keyword_validator + + inline + std::string make_absolute_keyword_location(const std::vector& uris, + const std::string& keyword) + { + for (auto it = uris.rbegin(); it != uris.rend(); ++it) + { + if (!it->has_identifier() && it->is_absolute()) + { + return it->append(keyword).string(); + } + } + return ""; + } + + template + class string_validator : public keyword_validator + { + jsoncons::optional max_length_; + std::string max_length_location_; + jsoncons::optional min_length_; + std::string min_length_location_; + + #if defined(JSONCONS_HAS_STD_REGEX) + jsoncons::optional pattern_; + std::string pattern_string_; + std::string pattern_location_; + #endif + + format_checker format_check_; + std::string format_location_; + + jsoncons::optional content_encoding_; + std::string content_encoding_location_; + jsoncons::optional content_media_type_; + std::string content_media_type_location_; + + public: + string_validator(const Json& sch, const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), max_length_(), min_length_(), + #if defined(JSONCONS_HAS_STD_REGEX) + pattern_(), + #endif + content_encoding_(), content_media_type_() + { + auto it = sch.find("maxLength"); + if (it != sch.object_range().end()) + { + max_length_ = it->value().template as(); + max_length_location_ = make_absolute_keyword_location(uris, "maxLength"); + } + + it = sch.find("minLength"); + if (it != sch.object_range().end()) + { + min_length_ = it->value().template as(); + min_length_location_ = make_absolute_keyword_location(uris, "minLength"); + } + + it = sch.find("contentEncoding"); + if (it != sch.object_range().end()) + { + content_encoding_ = it->value().template as(); + content_encoding_location_ = make_absolute_keyword_location(uris, "contentEncoding"); + // If "contentEncoding" is set to "binary", a Json value + // of type json_type::byte_string_value is accepted. + } + + it = sch.find("contentMediaType"); + if (it != sch.object_range().end()) + { + content_media_type_ = it->value().template as(); + content_media_type_location_ = make_absolute_keyword_location(uris, "contentMediaType"); + } + + #if defined(JSONCONS_HAS_STD_REGEX) + it = sch.find("pattern"); + if (it != sch.object_range().end()) + { + pattern_string_ = it->value().template as(); + pattern_ = std::regex(it->value().template as(),std::regex::ECMAScript); + pattern_location_ = make_absolute_keyword_location(uris, "pattern"); + } + #endif + + it = sch.find("format"); + if (it != sch.object_range().end()) + { + format_location_ = make_absolute_keyword_location(uris, "format"); + std::string format = it->value().template as(); + if (format == "date-time") + { + format_check_ = rfc3339_date_time_check; + } + else if (format == "date") + { + format_check_ = rfc3339_date_check; + } + else if (format == "time") + { + format_check_ = rfc3339_time_check; + } + else if (format == "email") + { + format_check_ = email_check; + } + else if (format == "hostname") + { + format_check_ = hostname_check; + } + else if (format == "ipv4") + { + format_check_ = ipv4_check; + } + else if (format == "ipv6") + { + format_check_ = ipv6_check; + } + else if (format == "regex") + { + format_check_ = regex_check; + } + else + { + // Not supported - ignore + } + } + } + + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const override + { + std::string content; + if (content_encoding_) + { + if (*content_encoding_ == "base64") + { + auto s = instance.template as(); + auto retval = jsoncons::decode_base64(s.begin(), s.end(), content); + if (retval.ec != jsoncons::conv_errc::success) + { + reporter.error(validation_output("contentEncoding", + content_encoding_location_, + instance_location.to_uri_fragment(), + "Content is not a base64 string")); + if (reporter.fail_early()) + { + return; + } + } + } + else if (!content_encoding_->empty()) + { + reporter.error(validation_output("contentEncoding", + content_encoding_location_, + instance_location.to_uri_fragment(), + "unable to check for contentEncoding '" + *content_encoding_ + "'")); + if (reporter.fail_early()) + { + return; + } + } + } + else + { + content = instance.template as(); + } + + if (content_media_type_) + { + if (content_media_type_ == "application/Json") + { + json_string_reader reader(content); + std::error_code ec; + reader.read(ec); + + if (ec) + { + reporter.error(validation_output("contentMediaType", + content_media_type_location_, + instance_location.to_uri_fragment(), + std::string("Content is not JSON: ") + ec.message())); + } + } + } + else if (instance.type() == json_type::byte_string_value) + { + reporter.error(validation_output("contentMediaType", + content_media_type_location_, + instance_location.to_uri_fragment(), + "Expected string, but is byte string")); + if (reporter.fail_early()) + { + return; + } + } + + if (instance.type() != json_type::string_value) + { + return; + } + + if (min_length_) + { + std::size_t length = unicode_traits::count_codepoints(content.data(), content.size()); + if (length < *min_length_) + { + reporter.error(validation_output("minLength", + min_length_location_, + instance_location.to_uri_fragment(), + std::string("Expected minLength: ") + std::to_string(*min_length_) + + ", actual: " + std::to_string(length))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (max_length_) + { + std::size_t length = unicode_traits::count_codepoints(content.data(), content.size()); + if (length > *max_length_) + { + reporter.error(validation_output("maxLength", + max_length_location_, + instance_location.to_uri_fragment(), + std::string("Expected maxLength: ") + std::to_string(*max_length_) + + ", actual: " + std::to_string(length))); + if (reporter.fail_early()) + { + return; + } + } + } + + #if defined(JSONCONS_HAS_STD_REGEX) + if (pattern_) + { + if (!std::regex_search(content, *pattern_)) + { + std::string message("String \""); + message.append(instance.template as()); + message.append("\" does not match pattern \""); + message.append(pattern_string_); + message.append("\""); + reporter.error(validation_output("pattern", + pattern_location_, + instance_location.to_uri_fragment(), + std::move(message))); + if (reporter.fail_early()) + { + return; + } + } + } + + #endif + + if (format_check_ != nullptr) + { + format_check_(format_location_, instance_location, content, reporter); + if (reporter.error_count() > 0 && reporter.fail_early()) + { + return; + } + } + } + }; + + // not_validator + + template + class not_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + validator_pointer rule_; + + public: + not_validator(abstract_keyword_validator_factory* builder, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + rule_ = builder->make_keyword_validator(sch, uris, {"not"}); + } + + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const final + { + collecting_error_reporter local_reporter; + rule_->validate(instance, instance_location, local_reporter, patch); + + if (local_reporter.errors.empty()) + { + reporter.error(validation_output("not", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Instance must not be valid against schema")); + } + } + + jsoncons::optional get_default_value(const jsonpointer::json_pointer& instance_location, + const Json& instance, + error_reporter& reporter) const override + { + return rule_->get_default_value(instance_location, instance, reporter); + } + }; + + template + struct all_of_criterion + { + static const std::string& key() + { + static const std::string k("allOf"); + return k; + } + + static bool is_complete(const Json&, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + const collecting_error_reporter& local_reporter, + std::size_t) + { + if (!local_reporter.errors.empty()) + reporter.error(validation_output("allOf", + "", + instance_location.to_uri_fragment(), + "At least one schema failed to match, but all are required to match. ", + local_reporter.errors)); + return !local_reporter.errors.empty(); + } + }; + + template + struct any_of_criterion + { + static const std::string& key() + { + static const std::string k("anyOf"); + return k; + } + + static bool is_complete(const Json&, + const jsonpointer::json_pointer&, + error_reporter&, + const collecting_error_reporter&, + std::size_t count) + { + return count == 1; + } + }; + + template + struct one_of_criterion + { + static const std::string& key() + { + static const std::string k("oneOf"); + return k; + } + + static bool is_complete(const Json&, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + const collecting_error_reporter&, + std::size_t count) + { + if (count > 1) + { + std::string message(std::to_string(count)); + message.append(" subschemas matched, but exactly one is required to match"); + reporter.error(validation_output("oneOf", + "", + instance_location.to_uri_fragment(), + std::move(message))); + } + return count > 1; + } + }; + + template + class combining_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + std::vector subschemas_; + + public: + combining_validator(abstract_keyword_validator_factory* builder, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + size_t c = 0; + for (const auto& subsch : sch.array_range()) + { + subschemas_.push_back(builder->make_keyword_validator(subsch, uris, {Criterion::key(), std::to_string(c++)})); + } + + // Validate value of allOf, anyOf, and oneOf "MUST be a non-empty array" + } + + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const final + { + size_t count = 0; + + collecting_error_reporter local_reporter; + for (auto& s : subschemas_) + { + std::size_t mark = local_reporter.errors.size(); + s->validate(instance, instance_location, local_reporter, patch); + if (mark == local_reporter.errors.size()) + count++; + + if (Criterion::is_complete(instance, instance_location, reporter, local_reporter, count)) + return; + } + + if (count == 0) + { + reporter.error(validation_output("combined", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "No schema matched, but one of them is required to match", + local_reporter.errors)); + } + } + }; + + template + T get_number(const Json& val, const string_view& keyword) + { + if (!val.is_number()) + { + std::string message(keyword); + message.append(" must be a number value"); + JSONCONS_THROW(schema_error(message)); + } + return val.template as(); + } + + template + class numeric_validator_base : public keyword_validator + { + jsoncons::optional maximum_; + std::string absolute_maximum_location_; + jsoncons::optional minimum_; + std::string absolute_minimum_location_; + jsoncons::optional exclusive_maximum_; + std::string absolute_exclusive_maximum_location_; + jsoncons::optional exclusive_minimum_; + std::string absolute_exclusive_minimum_location_; + jsoncons::optional multiple_of_; + std::string absolute_multiple_of_location_; + + public: + numeric_validator_base(const Json& sch, + const std::vector& uris, + std::set& keywords) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), + maximum_(), minimum_(),exclusive_maximum_(), exclusive_minimum_(), multiple_of_() + { + auto it = sch.find("maximum"); + if (it != sch.object_range().end()) + { + maximum_ = get_number(it->value(), "maximum"); + absolute_maximum_location_ = make_absolute_keyword_location(uris,"maximum"); + keywords.insert("maximum"); + } + + it = sch.find("minimum"); + if (it != sch.object_range().end()) + { + minimum_ = get_number(it->value(), "minimum"); + absolute_minimum_location_ = make_absolute_keyword_location(uris,"minimum"); + keywords.insert("minimum"); + } + + it = sch.find("exclusiveMaximum"); + if (it != sch.object_range().end()) + { + exclusive_maximum_ = get_number(it->value(), "exclusiveMaximum"); + absolute_exclusive_maximum_location_ = make_absolute_keyword_location(uris,"exclusiveMaximum"); + keywords.insert("exclusiveMaximum"); + } + + it = sch.find("exclusiveMinimum"); + if (it != sch.object_range().end()) + { + exclusive_minimum_ = get_number(it->value(), "exclusiveMinimum"); + absolute_exclusive_minimum_location_ = make_absolute_keyword_location(uris,"exclusiveMinimum"); + keywords.insert("exclusiveMinimum"); + } + + it = sch.find("multipleOf"); + if (it != sch.object_range().end()) + { + multiple_of_ = get_number(it->value(), "multipleOf"); + absolute_multiple_of_location_ = make_absolute_keyword_location(uris,"multipleOf"); + keywords.insert("multipleOf"); + } + } + + protected: + + void apply_kewords(T value, + const jsonpointer::json_pointer& instance_location, + const Json& instance, + error_reporter& reporter) const + { + if (multiple_of_ && value != 0) // exclude zero + { + if (!is_multiple_of(value, *multiple_of_)) + { + reporter.error(validation_output("multipleOf", + absolute_multiple_of_location_, + instance_location.to_uri_fragment(), + instance.template as() + " is not a multiple of " + std::to_string(*multiple_of_))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (maximum_) + { + if (value > *maximum_) + { + reporter.error(validation_output("maximum", + absolute_maximum_location_, + instance_location.to_uri_fragment(), + instance.template as() + " exceeds maximum of " + std::to_string(*maximum_))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (minimum_) + { + if (value < *minimum_) + { + reporter.error(validation_output("minimum", + absolute_minimum_location_, + instance_location.to_uri_fragment(), + instance.template as() + " is below minimum of " + std::to_string(*minimum_))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (exclusive_maximum_) + { + if (value >= *exclusive_maximum_) + { + reporter.error(validation_output("exclusiveMaximum", + absolute_exclusive_maximum_location_, + instance_location.to_uri_fragment(), + instance.template as() + " exceeds maximum of " + std::to_string(*exclusive_maximum_))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (exclusive_minimum_) + { + if (value <= *exclusive_minimum_) + { + reporter.error(validation_output("exclusiveMinimum", + absolute_exclusive_minimum_location_, + instance_location.to_uri_fragment(), + instance.template as() + " is below minimum of " + std::to_string(*exclusive_minimum_))); + if (reporter.fail_early()) + { + return; + } + } + } + } + private: + static bool is_multiple_of(T x, double multiple_of) + { + double rem = std::remainder(x, multiple_of); + double eps = std::nextafter(x, 0) - x; + return std::fabs(rem) < std::fabs(eps); + } + }; + + template + class integer_validator : public numeric_validator_base + { + public: + integer_validator(const Json& sch, + const std::vector& uris, + std::set& keywords) + : numeric_validator_base(sch, uris, keywords) + { + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const + { + if (!(instance.template is_integer() || (instance.is_double() && static_cast(instance.template as()) == instance.template as()))) + { + reporter.error(validation_output("integer", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Instance is not an integer")); + if (reporter.fail_early()) + { + return; + } + } + int64_t value = instance.template as(); + this->apply_kewords(value, instance_location, instance, reporter); + } + }; + + template + class number_validator : public numeric_validator_base + { + public: + number_validator(const Json& sch, + const std::vector& uris, + std::set& keywords) + : numeric_validator_base(sch, uris, keywords) + { + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const + { + if (!(instance.template is_integer() || instance.is_double())) + { + reporter.error(validation_output("number", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Instance is not a number")); + if (reporter.fail_early()) + { + return; + } + } + double value = instance.template as(); + this->apply_kewords(value, instance_location, instance, reporter); + } + }; + + // null_validator + + template + class null_validator : public keyword_validator + { + public: + null_validator(const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const override + { + if (!instance.is_null()) + { + reporter.error(validation_output("null", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Expected to be null")); + } + } + }; + + template + class boolean_validator : public keyword_validator + { + public: + boolean_validator(const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + } + private: + void do_validate(const Json&, + const jsonpointer::json_pointer&, + error_reporter&, + Json&) const override + { + } + + }; + + template + class true_validator : public keyword_validator + { + public: + true_validator(const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + } + private: + void do_validate(const Json&, + const jsonpointer::json_pointer&, + error_reporter&, + Json&) const override + { + } + }; + + template + class false_validator : public keyword_validator + { + public: + false_validator(const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "") + { + } + private: + void do_validate(const Json&, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const override + { + reporter.error(validation_output("false", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "False schema always fails")); + } + }; + + template + class required_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + std::vector items_; + + public: + required_validator(const std::vector& uris, + const std::vector& items) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), items_(items) {} + required_validator(const std::string& absolute_keyword_location, const std::vector& items) + : keyword_validator(absolute_keyword_location), items_(items) {} + + required_validator(const required_validator&) = delete; + required_validator(required_validator&&) = default; + required_validator& operator=(const required_validator&) = delete; + required_validator& operator=(required_validator&&) = default; + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const override final + { + for (const auto& key : items_) + { + if (instance.find(key) == instance.object_range().end()) + { + reporter.error(validation_output("required", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Required property \"" + key + "\" not found")); + if (reporter.fail_early()) + { + return; + } + } + } + } + }; + + template + class object_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + jsoncons::optional max_properties_; + std::string absolute_max_properties_location_; + jsoncons::optional min_properties_; + std::string absolute_min_properties_location_; + jsoncons::optional> required_; + + std::map properties_; + #if defined(JSONCONS_HAS_STD_REGEX) + std::vector> pattern_properties_; + #endif + validator_pointer additional_properties_; + + std::map dependencies_; + + validator_pointer property_name_validator_; + + public: + object_validator(abstract_keyword_validator_factory* builder, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), + max_properties_(), min_properties_(), + additional_properties_(nullptr), + property_name_validator_(nullptr) + { + auto it = sch.find("maxProperties"); + if (it != sch.object_range().end()) + { + max_properties_ = it->value().template as(); + absolute_max_properties_location_ = make_absolute_keyword_location(uris, "maxProperties"); + } + + it = sch.find("minProperties"); + if (it != sch.object_range().end()) + { + min_properties_ = it->value().template as(); + absolute_min_properties_location_ = make_absolute_keyword_location(uris, "minProperties"); + } + + it = sch.find("required"); + if (it != sch.object_range().end()) + { + auto location = make_absolute_keyword_location(uris, "required"); + required_ = required_validator(location, + it->value().template as>()); + } + + it = sch.find("properties"); + if (it != sch.object_range().end()) + { + for (const auto& prop : it->value().object_range()) + properties_.emplace( + std::make_pair( + prop.key(), + builder->make_keyword_validator(prop.value(), uris, {"properties", prop.key()}))); + } + + #if defined(JSONCONS_HAS_STD_REGEX) + it = sch.find("patternProperties"); + if (it != sch.object_range().end()) + { + for (const auto& prop : it->value().object_range()) + pattern_properties_.emplace_back( + std::make_pair( + std::regex(prop.key(), std::regex::ECMAScript), + builder->make_keyword_validator(prop.value(), uris, {prop.key()}))); + } + #endif + + it = sch.find("additionalProperties"); + if (it != sch.object_range().end()) + { + additional_properties_ = builder->make_keyword_validator(it->value(), uris, {"additionalProperties"}); + } + + it = sch.find("dependencies"); + if (it != sch.object_range().end()) + { + for (const auto& dep : it->value().object_range()) + { + switch (dep.value().type()) + { + case json_type::array_value: + { + auto location = make_absolute_keyword_location(uris, "dependencies"); + dependencies_.emplace(dep.key(), + builder->make_required_validator({location}, + dep.value().template as>())); + break; + } + default: + { + dependencies_.emplace(dep.key(), + builder->make_keyword_validator(dep.value(), uris, {"dependencies", dep.key()})); + break; + } + } + } + } + + auto property_names_it = sch.find("propertyNames"); + if (property_names_it != sch.object_range().end()) + { + property_name_validator_ = builder->make_keyword_validator(property_names_it->value(), uris, {"propertyNames"}); + } + } + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const override + { + if (max_properties_ && instance.size() > *max_properties_) + { + std::string message("Maximum properties: " + std::to_string(*max_properties_)); + message.append(", found: " + std::to_string(instance.size())); + reporter.error(validation_output("maxProperties", + absolute_max_properties_location_, + instance_location.to_uri_fragment(), + std::move(message))); + if (reporter.fail_early()) + { + return; + } + } + + if (min_properties_ && instance.size() < *min_properties_) + { + std::string message("Minimum properties: " + std::to_string(*min_properties_)); + message.append(", found: " + std::to_string(instance.size())); + reporter.error(validation_output("minProperties", + absolute_min_properties_location_, + instance_location.to_uri_fragment(), + std::move(message))); + if (reporter.fail_early()) + { + return; + } + } + + if (required_) + required_->validate(instance, instance_location, reporter, patch); + + for (const auto& property : instance.object_range()) + { + if (property_name_validator_) + property_name_validator_->validate(property.key(), instance_location, reporter, patch); + + bool a_prop_or_pattern_matched = false; + auto properties_it = properties_.find(property.key()); + + // check if it is in "properties" + if (properties_it != properties_.end()) + { + a_prop_or_pattern_matched = true; + jsonpointer::json_pointer pointer(instance_location); + pointer /= property.key(); + properties_it->second->validate(property.value(), pointer, reporter, patch); + } + + #if defined(JSONCONS_HAS_STD_REGEX) + + // check all matching "patternProperties" + for (auto& schema_pp : pattern_properties_) + if (std::regex_search(property.key(), schema_pp.first)) + { + a_prop_or_pattern_matched = true; + jsonpointer::json_pointer pointer(instance_location); + pointer /= property.key(); + schema_pp.second->validate(property.value(), pointer, reporter, patch); + } + #endif + + // finally, check "additionalProperties" + if (!a_prop_or_pattern_matched && additional_properties_) + { + collecting_error_reporter local_reporter; + + jsonpointer::json_pointer pointer(instance_location); + pointer /= property.key(); + additional_properties_->validate(property.value(), pointer, local_reporter, patch); + if (!local_reporter.errors.empty()) + { + reporter.error(validation_output("additionalProperties", + additional_properties_->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Additional property \"" + property.key() + "\" found but was invalid.")); + if (reporter.fail_early()) + { + return; + } + } + } + } + + // reverse search + for (auto const& prop : properties_) + { + const auto finding = instance.find(prop.first); + if (finding == instance.object_range().end()) + { + // If property is not in instance + auto default_value = prop.second->get_default_value(instance_location, instance, reporter); + if (default_value) + { + // If default value is available, update patch + jsonpointer::json_pointer pointer(instance_location); + pointer /= prop.first; + + update_patch(patch, pointer, std::move(*default_value)); + } + } + } + + for (const auto& dep : dependencies_) + { + auto prop = instance.find(dep.first); + if (prop != instance.object_range().end()) + { + // if dependency-property is present in instance + jsonpointer::json_pointer pointer(instance_location); + pointer /= dep.first; + dep.second->validate(instance, pointer, reporter, patch); // validate + } + } + } + + void update_patch(Json& patch, const jsonpointer::json_pointer& instance_location, Json&& default_value) const + { + Json j; + j.try_emplace("op", "add"); + j.try_emplace("path", instance_location.to_uri_fragment()); + j.try_emplace("value", std::forward(default_value)); + patch.push_back(std::move(j)); + } + }; + + // array_validator + + template + class array_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + jsoncons::optional max_items_; + std::string absolute_max_items_location_; + jsoncons::optional min_items_; + std::string absolute_min_items_location_; + bool unique_items_ = false; + validator_pointer items_validator_; + std::vector item_validators_; + validator_pointer additional_items_validator_; + validator_pointer contains_validator_; + + public: + array_validator(abstract_keyword_validator_factory* builder, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), + max_items_(), min_items_(), items_validator_(nullptr), additional_items_validator_(nullptr), contains_validator_(nullptr) + { + { + auto it = sch.find("maxItems"); + if (it != sch.object_range().end()) + { + max_items_ = it->value().template as(); + absolute_max_items_location_ = make_absolute_keyword_location(uris, "maxItems"); + } + } + + { + auto it = sch.find("minItems"); + if (it != sch.object_range().end()) + { + min_items_ = it->value().template as(); + absolute_min_items_location_ = make_absolute_keyword_location(uris, "minItems"); + } + } + + { + auto it = sch.find("uniqueItems"); + if (it != sch.object_range().end()) + { + unique_items_ = it->value().template as(); + } + } + + { + auto it = sch.find("items"); + if (it != sch.object_range().end()) + { + + if (it->value().type() == json_type::array_value) + { + size_t c = 0; + for (const auto& subsch : it->value().array_range()) + item_validators_.push_back(builder->make_keyword_validator(subsch, uris, {"items", std::to_string(c++)})); + + auto attr_add = sch.find("additionalItems"); + if (attr_add != sch.object_range().end()) + { + additional_items_validator_ = builder->make_keyword_validator(attr_add->value(), uris, {"additionalItems"}); + } + + } + else if (it->value().type() == json_type::object_value || + it->value().type() == json_type::bool_value) + { + items_validator_ = builder->make_keyword_validator(it->value(), uris, {"items"}); + } + + } + } + + { + auto it = sch.find("contains"); + if (it != sch.object_range().end()) + { + contains_validator_ = builder->make_keyword_validator(it->value(), uris, {"contains"}); + } + } + } + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const override + { + if (max_items_) + { + if (instance.size() > *max_items_) + { + std::string message("Expected maximum item count: " + std::to_string(*max_items_)); + message.append(", found: " + std::to_string(instance.size())); + reporter.error(validation_output("maxItems", + absolute_max_items_location_, + instance_location.to_uri_fragment(), + std::move(message))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (min_items_) + { + if (instance.size() < *min_items_) + { + std::string message("Expected minimum item count: " + std::to_string(*min_items_)); + message.append(", found: " + std::to_string(instance.size())); + reporter.error(validation_output("minItems", + absolute_min_items_location_, + instance_location.to_uri_fragment(), + std::move(message))); + if (reporter.fail_early()) + { + return; + } + } + } + + if (unique_items_) + { + if (!array_has_unique_items(instance)) + { + reporter.error(validation_output("uniqueItems", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Array items are not unique")); + if (reporter.fail_early()) + { + return; + } + } + } + + size_t index = 0; + if (items_validator_) + { + for (const auto& i : instance.array_range()) + { + jsonpointer::json_pointer pointer(instance_location); + pointer /= index; + items_validator_->validate(i, pointer, reporter, patch); + index++; + } + } + else + { + auto validator_it = item_validators_.cbegin(); + for (const auto& item : instance.array_range()) + { + validator_pointer item_validator = nullptr; + if (validator_it != item_validators_.cend()) + { + item_validator = *validator_it; + ++validator_it; + } + else if (additional_items_validator_ != nullptr) + { + item_validator = additional_items_validator_; + } + else + break; + + jsonpointer::json_pointer pointer(instance_location); + pointer /= index; + item_validator->validate(item, pointer, reporter, patch); + } + } + + if (contains_validator_) + { + bool contained = false; + collecting_error_reporter local_reporter; + for (const auto& item : instance.array_range()) + { + std::size_t mark = local_reporter.errors.size(); + contains_validator_->validate(item, instance_location, local_reporter, patch); + if (mark == local_reporter.errors.size()) + { + contained = true; + break; + } + } + if (!contained) + { + reporter.error(validation_output("contains", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Expected at least one array item to match \"contains\" schema", + local_reporter.errors)); + if (reporter.fail_early()) + { + return; + } + } + } + } + + static bool array_has_unique_items(const Json& a) + { + for (auto it = a.array_range().begin(); it != a.array_range().end(); ++it) + { + for (auto jt = it+1; jt != a.array_range().end(); ++jt) + { + if (*it == *jt) + { + return false; // contains duplicates + } + } + } + return true; // elements are unique + } + }; + + template + class conditional_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + validator_pointer if_validator_; + validator_pointer then_validator_; + validator_pointer else_validator_; + + public: + conditional_validator(abstract_keyword_validator_factory* builder, + const Json& sch_if, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), if_validator_(nullptr), then_validator_(nullptr), else_validator_(nullptr) + { + auto then_it = sch.find("then"); + auto else_it = sch.find("else"); + + if (then_it != sch.object_range().end() || else_it != sch.object_range().end()) + { + if_validator_ = builder->make_keyword_validator(sch_if, uris, {"if"}); + + if (then_it != sch.object_range().end()) + { + then_validator_ = builder->make_keyword_validator(then_it->value(), uris, {"then"}); + } + + if (else_it != sch.object_range().end()) + { + else_validator_ = builder->make_keyword_validator(else_it->value(), uris, {"else"}); + } + } + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const final + { + if (if_validator_) + { + collecting_error_reporter local_reporter; + + if_validator_->validate(instance, instance_location, local_reporter, patch); + if (local_reporter.errors.empty()) + { + if (then_validator_) + then_validator_->validate(instance, instance_location, reporter, patch); + } + else + { + if (else_validator_) + else_validator_->validate(instance, instance_location, reporter, patch); + } + } + } + }; + + // enum_validator + + template + class enum_validator : public keyword_validator + { + Json enum_validator_; + + public: + enum_validator(const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), enum_validator_(sch) + { + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const final + { + bool in_range = false; + for (const auto& item : enum_validator_.array_range()) + { + if (item == instance) + { + in_range = true; + break; + } + } + + if (!in_range) + { + reporter.error(validation_output("enum", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + instance.template as() + " is not a valid enum value")); + if (reporter.fail_early()) + { + return; + } + } + } + }; + + // const_keyword + + template + class const_keyword : public keyword_validator + { + Json const_validator_; + + public: + const_keyword(const Json& sch, const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), const_validator_(sch) + { + } + private: + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json&) const final + { + if (const_validator_ != instance) + reporter.error(validation_output("const", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Instance is not const")); + } + }; + + template + class type_validator : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + Json default_value_; + std::vector type_mapping_; + jsoncons::optional> enum_validator_; + jsoncons::optional> const_validator_; + std::vector combined_validators_; + jsoncons::optional> conditional_validator_; + std::vector expected_types_; + + public: + type_validator(const type_validator&) = delete; + type_validator& operator=(const type_validator&) = delete; + type_validator(type_validator&&) = default; + type_validator& operator=(type_validator&&) = default; + + type_validator(abstract_keyword_validator_factory* builder, + const Json& sch, + const std::vector& uris) + : keyword_validator((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), default_value_(jsoncons::null_type()), + type_mapping_((uint8_t)(json_type::object_value)+1), + enum_validator_(), const_validator_() + { + //std::cout << uris.size() << " uris: "; + //for (const auto& uri : uris) + //{ + // std::cout << uri.string() << ", "; + //} + //std::cout << "\n"; + std::set known_keywords; + + auto it = sch.find("type"); + if (it == sch.object_range().end()) + { + initialize_type_mapping(builder, "", sch, uris, known_keywords); + } + else + { + switch (it->value().type()) + { + case json_type::string_value: + { + auto type = it->value().template as(); + initialize_type_mapping(builder, type, sch, uris, known_keywords); + expected_types_.emplace_back(std::move(type)); + break; + } + + case json_type::array_value: // "type": ["type1", "type2"] + { + for (const auto& item : it->value().array_range()) + { + auto type = item.template as(); + initialize_type_mapping(builder, type, sch, uris, known_keywords); + expected_types_.emplace_back(std::move(type)); + } + break; + } + default: + break; + } + } + + const auto default_it = sch.find("default"); + if (default_it != sch.object_range().end()) + { + default_value_ = default_it->value(); + } + + it = sch.find("enum"); + if (it != sch.object_range().end()) + { + enum_validator_ = enum_validator(it->value(), uris); + } + + it = sch.find("const"); + if (it != sch.object_range().end()) + { + const_validator_ = const_keyword(it->value(), uris); + } + + it = sch.find("not"); + if (it != sch.object_range().end()) + { + combined_validators_.push_back(builder->make_not_validator(it->value(), uris)); + } + + it = sch.find("allOf"); + if (it != sch.object_range().end()) + { + combined_validators_.push_back(builder->make_all_of_validator(it->value(), uris)); + } + + it = sch.find("anyOf"); + if (it != sch.object_range().end()) + { + combined_validators_.push_back(builder->make_any_of_validator(it->value(), uris)); + } + + it = sch.find("oneOf"); + if (it != sch.object_range().end()) + { + combined_validators_.push_back(builder->make_one_of_validator(it->value(), uris)); + } + + it = sch.find("if"); + if (it != sch.object_range().end()) + { + conditional_validator_ = conditional_validator(builder, it->value(), sch, uris); + } + } + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const override final + { + auto type = type_mapping_[(uint8_t) instance.type()]; + + if (type) + type->validate(instance, instance_location, reporter, patch); + else + { + std::ostringstream ss; + ss << "Expected "; + for (std::size_t i = 0; i < expected_types_.size(); ++i) + { + if (i > 0) + { + ss << ", "; + if (i+1 == expected_types_.size()) + { + ss << "or "; + } + } + ss << expected_types_[i]; + } + ss << ", found " << instance.type(); + + reporter.error(validation_output("type", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + ss.str())); + if (reporter.fail_early()) + { + return; + } + } + + if (enum_validator_) + { + enum_validator_->validate(instance, instance_location, reporter, patch); + if (reporter.error_count() > 0 && reporter.fail_early()) + { + return; + } + } + + if (const_validator_) + { + const_validator_->validate(instance, instance_location, reporter, patch); + if (reporter.error_count() > 0 && reporter.fail_early()) + { + return; + } + } + + for (const auto& validator : combined_validators_) + { + validator->validate(instance, instance_location, reporter, patch); + if (reporter.error_count() > 0 && reporter.fail_early()) + { + return; + } + } + + + if (conditional_validator_) + { + conditional_validator_->validate(instance, instance_location, reporter, patch); + if (reporter.error_count() > 0 && reporter.fail_early()) + { + return; + } + } + } + + jsoncons::optional get_default_value(const jsonpointer::json_pointer&, + const Json&, + error_reporter&) const override + { + return default_value_; + } + + void initialize_type_mapping(abstract_keyword_validator_factory* builder, + const std::string& type, + const Json& sch, + const std::vector& uris, + std::set& keywords) + { + if (type == "null") + { + type_mapping_[(uint8_t)json_type::null_value] = builder->make_null_validator(uris); + } + else if (type == "object") + { + type_mapping_[(uint8_t)json_type::object_value] = builder->make_object_validator(sch, uris); + } + else if (type == "array") + { + type_mapping_[(uint8_t)json_type::array_value] = builder->make_array_validator(sch, uris); + } + else if (type == "string") + { + type_mapping_[(uint8_t)json_type::string_value] = builder->make_string_validator(sch, uris); + // For binary types + type_mapping_[(uint8_t) json_type::byte_string_value] = type_mapping_[(uint8_t) json_type::string_value]; + } + else if (type == "boolean") + { + type_mapping_[(uint8_t)json_type::bool_value] = builder->make_boolean_validator(uris); + } + else if (type == "integer") + { + type_mapping_[(uint8_t)json_type::int64_value] = builder->make_integer_validator(sch, uris, keywords); + type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::int64_value]; + type_mapping_[(uint8_t)json_type::double_value] = type_mapping_[(uint8_t)json_type::int64_value]; + } + else if (type == "number") + { + type_mapping_[(uint8_t)json_type::double_value] = builder->make_number_validator(sch, uris, keywords); + type_mapping_[(uint8_t)json_type::int64_value] = type_mapping_[(uint8_t)json_type::double_value]; + type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::double_value]; + } + else if (type.empty()) + { + type_mapping_[(uint8_t)json_type::null_value] = builder->make_null_validator(uris); + type_mapping_[(uint8_t)json_type::object_value] = builder->make_object_validator(sch, uris); + type_mapping_[(uint8_t)json_type::array_value] = builder->make_array_validator(sch, uris); + type_mapping_[(uint8_t)json_type::string_value] = builder->make_string_validator(sch, uris); + // For binary types + type_mapping_[(uint8_t) json_type::byte_string_value] = type_mapping_[(uint8_t) json_type::string_value]; + type_mapping_[(uint8_t)json_type::bool_value] = builder->make_boolean_validator(uris); + type_mapping_[(uint8_t)json_type::int64_value] = builder->make_integer_validator(sch, uris, keywords); + type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::int64_value]; + type_mapping_[(uint8_t)json_type::double_value] = type_mapping_[(uint8_t)json_type::int64_value]; + type_mapping_[(uint8_t)json_type::double_value] = builder->make_number_validator(sch, uris, keywords); + type_mapping_[(uint8_t)json_type::int64_value] = type_mapping_[(uint8_t)json_type::double_value]; + type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::double_value]; + } + } + }; + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_VALUE_RULES_HPP diff --git a/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp b/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp new file mode 100644 index 0000000..f538105 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp @@ -0,0 +1,556 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_FACTORY_HPP +#define JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_FACTORY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(JSONCONS_HAS_STD_REGEX) +#include +#endif + +namespace jsoncons { +namespace jsonschema { + + template + using uri_resolver = std::function; + + template + class reference_schema : public keyword_validator + { + using validator_pointer = typename keyword_validator::self_pointer; + + validator_pointer referred_schema_; + + public: + reference_schema(const std::string& id) + : keyword_validator(id), referred_schema_(nullptr) {} + + void set_referred_schema(validator_pointer target) { referred_schema_ = target; } + + private: + + void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const override + { + if (!referred_schema_) + { + reporter.error(validation_output("", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Unresolved schema reference " + this->absolute_keyword_location())); + return; + } + + referred_schema_->validate(instance, instance_location, reporter, patch); + } + + jsoncons::optional get_default_value(const jsonpointer::json_pointer& instance_location, + const Json& instance, + error_reporter& reporter) const override + { + if (!referred_schema_) + { + reporter.error(validation_output("", + this->absolute_keyword_location(), + instance_location.to_uri_fragment(), + "Unresolved schema reference " + this->absolute_keyword_location())); + return jsoncons::optional(); + } + + return referred_schema_->get_default_value(instance_location, instance, reporter); + } + }; + + template + class keyword_validator_factory; + + template + class json_schema + { + using validator_pointer = typename keyword_validator::self_pointer; + + friend class keyword_validator_factory; + + std::vector>> subschemas_; + validator_pointer root_; + public: + json_schema(std::vector>>&& subschemas, + validator_pointer root) + : subschemas_(std::move(subschemas)), root_(root) + { + if (root_ == nullptr) + JSONCONS_THROW(schema_error("There is no root schema to validate an instance against")); + } + + json_schema(const json_schema&) = delete; + json_schema(json_schema&&) = default; + json_schema& operator=(const json_schema&) = delete; + json_schema& operator=(json_schema&&) = default; + + void validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const + { + JSONCONS_ASSERT(root_ != nullptr); + root_->validate(instance, instance_location, reporter, patch); + } + }; + + template + struct default_uri_resolver + { + Json operator()(const jsoncons::uri& uri) + { + if (uri.path() == "/draft-07/schema") + { + return jsoncons::jsonschema::schema_draft7::get_schema(); + } + + JSONCONS_THROW(jsonschema::schema_error("Don't know how to load JSON Schema " + std::string(uri.base()))); + } + }; + + template + class keyword_validator_factory : public abstract_keyword_validator_factory + { + using validator_pointer = typename keyword_validator::self_pointer; + + struct subschema_registry + { + std::map schemas; // schemas + std::map*> unresolved; // unresolved references + std::map unprocessed_keywords; + }; + + uri_resolver resolver_; + validator_pointer root_; + + // Owns all schemas + std::vector>> subschemas_; + + // Map location to subschema_registry + std::map subschema_registries_; + + public: + keyword_validator_factory(uri_resolver&& resolver) noexcept + + : resolver_(std::move(resolver)) + { + } + + keyword_validator_factory(const keyword_validator_factory&) = delete; + keyword_validator_factory& operator=(const keyword_validator_factory&) = delete; + keyword_validator_factory(keyword_validator_factory&&) = default; + keyword_validator_factory& operator=(keyword_validator_factory&&) = default; + + std::shared_ptr> get_schema() + { + return std::make_shared>(std::move(subschemas_), root_); + } + + validator_pointer make_required_validator(const std::vector& uris, + const std::vector& r) override + { + auto sch_orig = jsoncons::make_unique>(uris, r); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_null_validator(const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_true_validator(const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_false_validator(const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_object_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_array_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_string_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_boolean_validator(const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_integer_validator(const Json& schema, + const std::vector& uris, + std::set& keywords) override + { + auto sch_orig = jsoncons::make_unique>(schema, uris, keywords); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_number_validator(const Json& schema, + const std::vector& uris, + std::set& keywords) override + { + auto sch_orig = jsoncons::make_unique>(schema, uris, keywords); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_not_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_all_of_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_any_of_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_one_of_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_type_validator(const Json& schema, + const std::vector& uris) override + { + auto sch_orig = jsoncons::make_unique>(this, schema, uris); + auto sch = sch_orig.get(); + subschemas_.emplace_back(std::move(sch_orig)); + return sch; + } + + validator_pointer make_keyword_validator(const Json& schema, + const std::vector& uris, + const std::vector& keys) override + { + std::vector new_uris = update_uris(schema, uris, keys); + + validator_pointer sch = nullptr; + + switch (schema.type()) + { + case json_type::bool_value: + if (schema.template as()) + { + sch = make_true_validator(new_uris); + } + else + { + sch = make_false_validator(new_uris); + } + break; + case json_type::object_value: + { + auto it = schema.find("definitions"); + if (it != schema.object_range().end()) + { + for (const auto& def : it->value().object_range()) + make_keyword_validator(def.value(), new_uris, {"definitions", def.key()}); + } + + it = schema.find("$ref"); + if (it != schema.object_range().end()) // this schema is a reference + { + schema_location relative(it->value().template as()); + schema_location id = relative.resolve(new_uris.back()); + sch = get_or_create_reference(id); + } + else + { + sch = make_type_validator(schema, new_uris); + } + break; + } + default: + JSONCONS_THROW(schema_error("invalid JSON-type for a schema for " + new_uris[0].string() + ", expected: boolean or object")); + break; + } + + for (const auto& uri : new_uris) + { + insert(uri, sch); + + if (schema.type() == json_type::object_value) + { + for (const auto& item : schema.object_range()) + insert_unknown_keyword(uri, item.key(), item.value()); // save unknown keywords for later reference + } + } + return sch; + } + + void load_root(const Json& sch) + { + if (sch.is_object()) + { + auto it = sch.find("$schema"); + if (it != sch.object_range().end()) + { + auto sv = it->value().as_string_view(); + if (!schema_version::contains(sv)) + { + std::string message("Unsupported schema version "); + message.append(sv.data(), sv.size()); + JSONCONS_THROW(schema_error(message)); + } + } + } + load(sch); + } + + void load(const Json& sch) + { + subschema_registries_.clear(); + root_ = make_keyword_validator(sch, {{"#"}}, {}); + + // load all external schemas that have not already been loaded + + std::size_t loaded_count = 0; + do + { + loaded_count = 0; + + std::vector locations; + for (const auto& item : subschema_registries_) + locations.push_back(item.first); + + for (const auto& loc : locations) + { + if (subschema_registries_[loc].schemas.empty()) // registry for this file is empty + { + if (resolver_) + { + Json external_schema = resolver_(loc); + make_keyword_validator(external_schema, {{loc}}, {}); + ++loaded_count; + } + else + { + JSONCONS_THROW(schema_error("External schema reference '" + loc + "' needs to be loaded, but no resolver provided")); + } + } + } + } + while (loaded_count > 0); + + for (const auto &file : subschema_registries_) + { + if (!file.second.unresolved.empty()) + { + JSONCONS_THROW(schema_error("after all files have been parsed, '" + + (file.first == "" ? "" : file.first) + + "' has still undefined references.")); + } + } + } + + private: + + void insert(const schema_location& uri, validator_pointer s) + { + auto& file = get_or_create_file(std::string(uri.base())); + auto schemas_it = file.schemas.find(std::string(uri.fragment())); + if (schemas_it != file.schemas.end()) + { + JSONCONS_THROW(schema_error("schema with " + uri.string() + " already inserted")); + return; + } + + file.schemas.insert({std::string(uri.fragment()), s}); + + // is there an unresolved reference to this newly inserted schema? + auto unresolved_it = file.unresolved.find(std::string(uri.fragment())); + if (unresolved_it != file.unresolved.end()) + { + unresolved_it->second->set_referred_schema(s); + file.unresolved.erase(unresolved_it); + + } + } + + void insert_unknown_keyword(const schema_location& uri, + const std::string& key, + const Json& value) + { + auto &file = get_or_create_file(std::string(uri.base())); + auto new_u = uri.append(key); + schema_location new_uri(new_u); + + if (new_uri.has_fragment() && !new_uri.has_identifier()) + { + auto fragment = std::string(new_uri.fragment()); + // is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema + auto unresolved = file.unresolved.find(fragment); + if (unresolved != file.unresolved.end()) + make_keyword_validator(value, {{new_uri}}, {}); + else // no, nothing ref'd it, keep for later + file.unprocessed_keywords[fragment] = value; + + // recursively add possible subschemas of unknown keywords + if (value.type() == json_type::object_value) + for (const auto& subsch : value.object_range()) + { + insert_unknown_keyword(new_uri, subsch.key(), subsch.value()); + } + } + } + + validator_pointer get_or_create_reference(const schema_location& uri) + { + auto &file = get_or_create_file(std::string(uri.base())); + + // a schema already exists + auto sch = file.schemas.find(std::string(uri.fragment())); + if (sch != file.schemas.end()) + return sch->second; + + // referencing an unknown keyword, turn it into schema + // + // an unknown keyword can only be referenced by a JSONPointer, + // not by a plain name identifier + if (uri.has_fragment() && !uri.has_identifier()) + { + std::string fragment = std::string(uri.fragment()); + auto unprocessed_keywords_it = file.unprocessed_keywords.find(fragment); + if (unprocessed_keywords_it != file.unprocessed_keywords.end()) + { + auto &subsch = unprocessed_keywords_it->second; + auto s = make_keyword_validator(subsch, {{uri}}, {}); // A JSON Schema MUST be an object or a boolean. + file.unprocessed_keywords.erase(unprocessed_keywords_it); + return s; + } + } + + // get or create a reference_schema + auto ref = file.unresolved.find(std::string(uri.fragment())); + if (ref != file.unresolved.end()) + { + return ref->second; // unresolved, use existing reference + } + else + { + auto orig = jsoncons::make_unique>(uri.string()); + auto p = file.unresolved.insert(ref, + {std::string(uri.fragment()), orig.get()}) + ->second; // unresolved, create new reference + + subschemas_.emplace_back(std::move(orig)); + return p; + } + } + + subschema_registry& get_or_create_file(const std::string& loc) + { + auto file = subschema_registries_.find(loc); + if (file != subschema_registries_.end()) + return file->second; + else + return subschema_registries_.insert(file, {loc, {}})->second; + } + + }; + + template + std::shared_ptr> make_schema(const Json& schema) + { + keyword_validator_factory loader{default_uri_resolver()}; + loader.load_root(schema); + + return loader.get_schema(); + } + + template + typename std::enable_if::value,std::shared_ptr>>::type + make_schema(const Json& schema, const URIResolver& resolver) + { + keyword_validator_factory loader(resolver); + loader.load_root(schema); + + return loader.get_schema(); + } + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_SCHEMA_LOADER_HPP diff --git a/include/jsoncons_ext/jsonschema/schema_draft7.hpp b/include/jsoncons_ext/jsonschema/schema_draft7.hpp new file mode 100644 index 0000000..c6f6fbc --- /dev/null +++ b/include/jsoncons_ext/jsonschema/schema_draft7.hpp @@ -0,0 +1,198 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_SCHEMA_DRAFT7_HPP +#define JSONCONS_JSONSCHEMA_SCHEMA_DRAFT7_HPP + +#include + +namespace jsoncons { +namespace jsonschema { + + template + struct schema_draft7 + { + static Json get_schema() + { + static Json schema = Json::parse(R"( + { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true + } + )"); + + return schema; + } + }; + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_SCHEMA_DRAFT7_HPP diff --git a/include/jsoncons_ext/jsonschema/schema_location.hpp b/include/jsoncons_ext/jsonschema/schema_location.hpp new file mode 100644 index 0000000..fd1a743 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/schema_location.hpp @@ -0,0 +1,200 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_SCHEMA_LOCATION_HPP +#define JSONCONS_JSONSCHEMA_SCHEMA_LOCATION_HPP + +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonschema { + + class schema_location + { + jsoncons::uri uri_; + std::string identifier_; + public: + schema_location() + { + } + + schema_location(const std::string& uri) + { + auto pos = uri.find('#'); + if (pos != std::string::npos) + { + identifier_ = uri.substr(pos + 1); + unescape_percent(identifier_); + } + uri_ = jsoncons::uri(uri); + } + + jsoncons::uri uri() const + { + return uri_; + } + + bool has_fragment() const + { + return !identifier_.empty(); + } + + bool has_identifier() const + { + return !identifier_.empty() && identifier_.front() != '/'; + } + + jsoncons::string_view base() const + { + return uri_.base(); + } + + jsoncons::string_view path() const + { + return uri_.path(); + } + + bool is_absolute() const + { + return uri_.is_absolute(); + } + + std::string identifier() const + { + return identifier_; + } + + std::string fragment() const + { + return identifier_; + } + + schema_location resolve(const schema_location& uri) const + { + schema_location new_uri; + new_uri.identifier_ = identifier_; + new_uri.uri_ = uri_.resolve(uri.uri_); + return new_uri; + } + + int compare(const schema_location& other) const + { + int result = uri_.compare(other.uri_); + if (result != 0) + { + return result; + } + return result; + } + + schema_location append(const std::string& field) const + { + if (has_identifier()) + return *this; + + jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.fragment())); + pointer /= field; + + jsoncons::uri new_uri(uri_.scheme(), + uri_.userinfo(), + uri_.host(), + uri_.port(), + uri_.path(), + uri_.query(), + pointer.to_string()); + + schema_location wrapper; + wrapper.uri_ = new_uri; + wrapper.identifier_ = pointer.to_string(); + + return wrapper; + } + + schema_location append(std::size_t index) const + { + if (has_identifier()) + return *this; + + jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.fragment())); + pointer /= index; + + jsoncons::uri new_uri(uri_.scheme(), + uri_.userinfo(), + uri_.host(), + uri_.port(), + uri_.path(), + uri_.query(), + pointer.to_string()); + + schema_location wrapper; + wrapper.uri_ = new_uri; + wrapper.identifier_ = pointer.to_string(); + + return wrapper; + } + + std::string string() const + { + std::string s = uri_.string(); + return s; + } + + friend bool operator==(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) == 0; + } + + friend bool operator!=(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) != 0; + } + + friend bool operator<(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) < 0; + } + + friend bool operator<=(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) <= 0; + } + + friend bool operator>(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) > 0; + } + + friend bool operator>=(const schema_location& lhs, const schema_location& rhs) + { + return lhs.compare(rhs) >= 0; + } + private: + static void unescape_percent(std::string& s) + { + if (s.size() >= 3) + { + std::size_t pos = s.size() - 2; + while (pos-- >= 1) + { + if (s[pos] == '%') + { + std::string hex = s.substr(pos + 1, 2); + char ch = (char) std::strtoul(hex.c_str(), nullptr, 16); + s.replace(pos, 3, 1, ch); + } + } + } + } + }; + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_RULE_HPP diff --git a/include/jsoncons_ext/jsonschema/schema_version.hpp b/include/jsoncons_ext/jsonschema/schema_version.hpp new file mode 100644 index 0000000..b804712 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/schema_version.hpp @@ -0,0 +1,35 @@ +// Copyright 2021 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_SCHEMA_VERSION_HPP +#define JSONCONS_JSONSCHEMA_SCHEMA_VERSION_HPP + +#include + +namespace jsoncons { +namespace jsonschema { + + class schema_version + { + public: + static bool contains(const string_view& url) + { + if (url.find("json-schema.org/draft-07/schema#") != string_view::npos) + { + return true; + } + else + { + return false; + } + } + }; + + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP diff --git a/include/jsoncons_ext/jsonschema/subschema.hpp b/include/jsoncons_ext/jsonschema/subschema.hpp new file mode 100644 index 0000000..cbe0af4 --- /dev/null +++ b/include/jsoncons_ext/jsonschema/subschema.hpp @@ -0,0 +1,144 @@ +// Copyright 2020 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONSCHEMA_SUBSCHEMA_HPP +#define JSONCONS_JSONSCHEMA_SUBSCHEMA_HPP + +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonschema { + + // Interface for validation error handlers + class error_reporter + { + bool fail_early_; + std::size_t error_count_; + public: + error_reporter(bool fail_early = false) + : fail_early_(fail_early), error_count_(0) + { + } + + virtual ~error_reporter() = default; + + void error(const validation_output& o) + { + ++error_count_; + do_error(o); + } + + std::size_t error_count() const + { + return error_count_; + } + + bool fail_early() const + { + return fail_early_; + } + + private: + virtual void do_error(const validation_output& /* e */) = 0; + }; + + template + class keyword_validator + { + std::string absolute_keyword_location_; + public: + using self_pointer = keyword_validator*; + + keyword_validator(const std::string& absolute_keyword_location) + : absolute_keyword_location_(absolute_keyword_location) + { + } + + keyword_validator(const keyword_validator&) = delete; + keyword_validator(keyword_validator&&) = default; + keyword_validator& operator=(const keyword_validator&) = delete; + keyword_validator& operator=(keyword_validator&&) = default; + + virtual ~keyword_validator() = default; + + const std::string& absolute_keyword_location() const + { + return absolute_keyword_location_; + } + + void validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const + { + do_validate(instance, + instance_location, + reporter, + patch); + } + + virtual jsoncons::optional get_default_value(const jsonpointer::json_pointer&, const Json&, error_reporter&) const + { + return jsoncons::optional(); + } + + private: + virtual void do_validate(const Json& instance, + const jsonpointer::json_pointer& instance_location, + error_reporter& reporter, + Json& patch) const = 0; + }; + + template + std::vector update_uris(const Json& schema, + const std::vector& uris, + const std::vector& keys) + { + // Exclude uri's that are not plain name identifiers + std::vector new_uris; + for (const auto& uri : uris) + { + if (!uri.has_identifier()) + new_uris.push_back(uri); + } + + // Append the keys for this sub-schema to the uri's + for (const auto& key : keys) + { + for (auto& uri : new_uris) + { + auto new_u = uri.append(key); + uri = schema_location(new_u); + } + } + if (schema.type() == json_type::object_value) + { + auto it = schema.find("$id"); // If $id is found, this schema can be referenced by the id + if (it != schema.object_range().end()) + { + std::string id = it->value().template as(); + // Add it to the list if it is not already there + if (std::find(new_uris.begin(), new_uris.end(), id) == new_uris.end()) + { + schema_location relative(id); + schema_location new_uri = relative.resolve(new_uris.back()); + new_uris.emplace_back(new_uri); + } + } + } + + return new_uris; + } + +} // namespace jsonschema +} // namespace jsoncons + +#endif // JSONCONS_JSONSCHEMA_RULE_HPP diff --git a/include/jsoncons_ext/mergepatch/mergepatch.hpp b/include/jsoncons_ext/mergepatch/mergepatch.hpp new file mode 100644 index 0000000..c5fdefe --- /dev/null +++ b/include/jsoncons_ext/mergepatch/mergepatch.hpp @@ -0,0 +1,103 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONMERGEPATCH_JSONMERGEPATCH_HPP +#define JSONCONS_JSONMERGEPATCH_JSONMERGEPATCH_HPP + +#include +#include +#include +#include // std::min +#include // std::move +#include + +namespace jsoncons { +namespace mergepatch { + + template + Json from_diff(const Json& source, const Json& target) + { + if (!source.is_object() || !target.is_object()) + { + return target; + } + Json result(json_object_arg); + + for (const auto& member : source.object_range()) + { + auto it = target.find(member.key()); + if (it != target.object_range().end()) + { + if (member.value() != it->value()) + { + result.try_emplace(member.key(), from_diff(member.value(), it->value())); + } + } + else + { + result.try_emplace(member.key(), Json::null()); + } + } + + for (const auto& member : target.object_range()) + { + auto it = source.find(member.key()); + if (it == source.object_range().end()) + { + result.try_emplace(member.key(), member.value()); + } + } + + return result; + } + + namespace detail { + template + Json apply_merge_patch_(Json& target, const Json& patch) + { + if (patch.is_object()) + { + if (!target.is_object()) + { + target = Json(json_object_arg); + } + for (auto& member : patch.object_range()) + { + auto it = target.find(member.key()); + if (it != target.object_range().end()) + { + Json item = it->value(); + target.erase(it); + if (!member.value().is_null()) + { + target.try_emplace(member.key(), apply_merge_patch_(item, member.value())); + } + } + else if (!member.value().is_null()) + { + Json item(json_object_arg); + target.try_emplace(member.key(), apply_merge_patch_(item, member.value())); + } + } + return target; + } + else + { + return patch; + } + } + } // namespace detail + + template + void apply_merge_patch(Json& target, const Json& patch) + { + target = detail::apply_merge_patch_(target, patch); + } + +} // namespace mergepatch +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/msgpack/decode_msgpack.hpp b/include/jsoncons_ext/msgpack/decode_msgpack.hpp new file mode 100644 index 0000000..614af3d --- /dev/null +++ b/include/jsoncons_ext/msgpack/decode_msgpack.hpp @@ -0,0 +1,202 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_DECODE_MSGPACK_HPP +#define JSONCONS_MSGPACK_DECODE_MSGPACK_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace msgpack { + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_msgpack(const Source& v, + const msgpack_decode_options& options = msgpack_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(v, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_msgpack(const Source& v, + const msgpack_decode_options& options = msgpack_decode_options()) + { + basic_msgpack_cursor cursor(v, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + msgpack_stream_reader reader(is, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) + { + basic_msgpack_cursor cursor(is, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(InputIt first, InputIt last, + const msgpack_decode_options& options = msgpack_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(InputIt first, InputIt last, + const msgpack_decode_options& options = msgpack_decode_options()) + { + basic_msgpack_cursor> cursor(binary_iterator_source(first, last), options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const msgpack_decode_options& options = msgpack_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(v, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const msgpack_decode_options& options = msgpack_decode_options()) + { + basic_msgpack_cursor cursor(v, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(is, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) + { + basic_msgpack_cursor cursor(is, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // msgpack +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/msgpack/encode_msgpack.hpp b/include/jsoncons_ext/msgpack/encode_msgpack.hpp new file mode 100644 index 0000000..10a61e0 --- /dev/null +++ b/include/jsoncons_ext/msgpack/encode_msgpack.hpp @@ -0,0 +1,142 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_ENCODE_MSGPACK_HPP +#define JSONCONS_MSGPACK_ENCODE_MSGPACK_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include + +namespace jsoncons { +namespace msgpack { + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_msgpack(const T& j, + Container& v, + const msgpack_encode_options& options = msgpack_encode_options()) + { + using char_type = typename T::char_type; + basic_msgpack_encoder> encoder(v, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_msgpack(const T& val, + Container& v, + const msgpack_encode_options& options = msgpack_encode_options()) + { + basic_msgpack_encoder> encoder(v, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_msgpack(const T& j, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) + { + using char_type = typename T::char_type; + msgpack_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_msgpack(const T& val, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) + { + msgpack_stream_encoder encoder(os, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // with temp_allocator_arg_t + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, const T& j, + Container& v, + const msgpack_encode_options& options = msgpack_encode_options()) + { + using char_type = typename T::char_type; + basic_msgpack_encoder,TempAllocator> encoder(v, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, Container& v, + const msgpack_encode_options& options = msgpack_encode_options()) + { + basic_msgpack_encoder,TempAllocator> encoder(v, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) + { + using char_type = typename T::char_type; + basic_msgpack_encoder encoder(os, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) + { + basic_msgpack_encoder encoder(os, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // msgpack +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack.hpp b/include/jsoncons_ext/msgpack/msgpack.hpp new file mode 100644 index 0000000..307aad2 --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack.hpp @@ -0,0 +1,24 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_HPP +#define JSONCONS_MSGPACK_MSGPACK_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +#endif + diff --git a/include/jsoncons_ext/msgpack/msgpack_cursor.hpp b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp new file mode 100644 index 0000000..a813429 --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp @@ -0,0 +1,343 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_CURSOR_HPP +#define JSONCONS_MSGPACK_MSGPACK_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace msgpack { + +template> +class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser_context +{ +public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; +private: + basic_msgpack_parser parser_; + basic_staj_visitor cursor_visitor_; + basic_json_visitor2_to_visitor_adaptor cursor_handler_adaptor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_msgpack_cursor(const basic_msgpack_cursor&) = delete; + basic_msgpack_cursor& operator=(const basic_msgpack_cursor&) = delete; + +public: + using string_view_type = string_view; + + template + basic_msgpack_cursor(Sourceable&& source, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_msgpack_cursor(Sourceable&& source, + std::error_code& ec) + : basic_msgpack_cursor(std::allocator_arg, Allocator(), + std::forward(source), + msgpack_decode_options(), + ec) + { + } + + template + basic_msgpack_cursor(Sourceable&& source, + const msgpack_decode_options& options, + std::error_code& ec) + : basic_msgpack_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_msgpack_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const msgpack_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + cursor_handler_adaptor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + const staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (cursor_visitor_.dump(visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj_filter_view operator|(basic_msgpack_cursor& cursor, + std::function pred) + { + return staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_msgpack_cursor(Sourceable&& source, + std::function filter, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(filter), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(); + } + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_msgpack_cursor(Sourceable&& source, + std::function filter, + std::error_code& ec) + : basic_msgpack_cursor(std::allocator_arg, Allocator(), + std::forward(source), filter, ec) + { + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_msgpack_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source), alloc), + cursor_visitor_(filter), + cursor_handler_adaptor_(cursor_visitor_, alloc), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&)") + void read(basic_json_visitor& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&, std::error_code&)") + void read(basic_json_visitor& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + static bool accept_all(const staj_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + if (cursor_visitor_.in_available()) + { + cursor_visitor_.send_available(ec); + } + else + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_handler_adaptor_, ec); + if (ec) return; + } + } + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + { + struct resource_wrapper + { + basic_json_visitor2_to_visitor_adaptor& adaptor; + basic_json_visitor& original; + + resource_wrapper(basic_json_visitor2_to_visitor_adaptor& adaptor, + basic_json_visitor& visitor) + : adaptor(adaptor), original(adaptor.destination()) + { + adaptor.destination(visitor); + } + + ~resource_wrapper() + { + adaptor.destination(original); + } + } wrapper(cursor_handler_adaptor_, visitor); + + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_handler_adaptor_, ec); + if (ec) return; + } + } + } +}; + +using msgpack_stream_cursor = basic_msgpack_cursor; +using msgpack_bytes_cursor = basic_msgpack_cursor; + +} // namespace msgpack +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp b/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp new file mode 100644 index 0000000..8fce1ca --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp @@ -0,0 +1,259 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_CURSOR2_HPP +#define JSONCONS_MSGPACK_MSGPACK_CURSOR2_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace msgpack { + + template> + class basic_msgpack_cursor2 : public basic_staj2_cursor, private virtual ser_context + { + public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; + private: + basic_msgpack_parser parser_; + basic_staj2_visitor cursor_visitor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_msgpack_cursor2(const basic_msgpack_cursor2&) = delete; + basic_msgpack_cursor2& operator=(const basic_msgpack_cursor2&) = delete; + + public: + using string_view_type = string_view; + + template + basic_msgpack_cursor2(Sourceable&& source, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_msgpack_cursor2(Sourceable&& source, + std::error_code& ec) + : basic_msgpack_cursor2(std::allocator_arg, Allocator(), + std::forward(source), + msgpack_decode_options(), + ec) + { + } + + template + basic_msgpack_cursor2(Sourceable&& source, + const msgpack_decode_options& options, + std::error_code& ec) + : basic_msgpack_cursor2(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_msgpack_cursor2(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const msgpack_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + const staj2_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor2& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor2& visitor, + std::error_code& ec) override + { + if (cursor_visitor_.dump(visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj2_filter_view operator|(basic_msgpack_cursor2& cursor, + std::function pred) + { + return staj2_filter_view(cursor, pred); + } + + private: + static bool accept_all(const staj2_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + if (cursor_visitor_.in_available()) + { + cursor_visitor_.send_available(ec); + } + else + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_visitor_, ec); + if (ec) return; + } + } + } + + void read_next(basic_json_visitor2& visitor, std::error_code& ec) + { + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(visitor, ec); + if (ec) return; + } + } + } + }; + + using msgpack_stream_cursor2 = basic_msgpack_cursor2; + using msgpack_bytes_cursor2 = basic_msgpack_cursor2; + +} // namespace msgpack +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/msgpack/msgpack_encoder.hpp b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp new file mode 100644 index 0000000..34e882b --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp @@ -0,0 +1,753 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP +#define JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP + +#include +#include +#include // std::numeric_limits +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace msgpack { + + enum class msgpack_container_type {object, array}; + + template> + class basic_msgpack_encoder final : public basic_json_visitor + { + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; + + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; + public: + using allocator_type = Allocator; + using char_type = char; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + + private: + struct stack_item + { + msgpack_container_type type_; + std::size_t length_; + std::size_t count_; + + stack_item(msgpack_container_type type, std::size_t length = 0) noexcept + : type_(type), length_(length), count_(0) + { + } + + std::size_t length() const + { + return length_; + } + + std::size_t count() const + { + return count_; + } + + bool is_object() const + { + return type_ == msgpack_container_type::object; + } + }; + + Sink sink_; + const msgpack_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_msgpack_encoder(const basic_msgpack_encoder&) = delete; + basic_msgpack_encoder& operator=(const basic_msgpack_encoder&) = delete; + public: + explicit basic_msgpack_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_msgpack_encoder(std::forward(sink), msgpack_encode_options(), alloc) + { + } + + explicit basic_msgpack_encoder(Sink&& sink, + const msgpack_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + ~basic_msgpack_encoder() noexcept + { + sink_.flush(); + } + + void reset() + { + stack_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + + private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + ec = msgpack_errc::object_length_required; + return false; + } + + bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(msgpack_container_type::object, length); + + if (length <= 15) + { + // fixmap + sink_.push_back(jsoncons::msgpack::msgpack_type::fixmap_base_type | (length & 0xf)); + } + else if (length <= 65535) + { + // map 16 + sink_.push_back(jsoncons::msgpack::msgpack_type::map16_type); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + else if (length <= 4294967295) + { + // map 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::map32_type); + binary::native_to_big(static_cast(length), + std::back_inserter(sink_)); + } + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().count() < stack_.back().length()) + { + ec = msgpack_errc::too_few_items; + return false; + } + else if (stack_.back().count() > stack_.back().length()) + { + ec = msgpack_errc::too_many_items; + return false; + } + + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + ec = msgpack_errc::array_length_required; + return false; + } + + bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(msgpack_container_type::array, length); + if (length <= 15) + { + // fixarray + sink_.push_back(jsoncons::msgpack::msgpack_type::fixarray_base_type | (length & 0xf)); + } + else if (length <= (std::numeric_limits::max)()) + { + // array 16 + sink_.push_back(jsoncons::msgpack::msgpack_type::array16_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits::max)()) + { + // array 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::array32_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + return true; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + + --nesting_depth_; + + if (stack_.back().count() < stack_.back().length()) + { + ec = msgpack_errc::too_few_items; + return false; + } + else if (stack_.back().count() > stack_.back().length()) + { + ec = msgpack_errc::too_many_items; + return false; + } + + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + write_string_value(name); + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + // nil + sink_.push_back(jsoncons::msgpack::msgpack_type::nil_type); + end_value(); + return true; + } + + void write_timestamp(int64_t seconds, int64_t nanoseconds) + { + if ((seconds >> 34) == 0) + { + uint64_t data64 = (nanoseconds << 34) | seconds; + if ((data64 & 0xffffffff00000000L) == 0) + { + // timestamp 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type); + sink_.push_back(0xff); + binary::native_to_big(static_cast(data64), std::back_inserter(sink_)); + } + else + { + // timestamp 64 + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type); + sink_.push_back(0xff); + binary::native_to_big(static_cast(data64), std::back_inserter(sink_)); + } + } + else + { + // timestamp 96 + sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type); + sink_.push_back(0x0c); // 12 + sink_.push_back(0xff); + binary::native_to_big(static_cast(nanoseconds), std::back_inserter(sink_)); + binary::native_to_big(static_cast(seconds), std::back_inserter(sink_)); + } + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::epoch_second: + { + int64_t seconds; + auto result = jsoncons::detail::to_integer(sv.data(), sv.length(), seconds); + if (!result) + { + ec = msgpack_errc::invalid_timestamp; + return false; + } + write_timestamp(seconds, 0); + break; + } + case semantic_tag::epoch_milli: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + bigint q; + bigint rem; + n.divide(millis_in_second, q, rem, true); + int64_t seconds = static_cast(q); + int64_t nanoseconds = static_cast(rem) * nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + bigint q; + bigint rem; + n.divide(nanos_in_second, q, rem, true); + int64_t seconds = static_cast(q); + int64_t nanoseconds = static_cast(rem); + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + write_string_value(sv); + end_value(); + break; + } + } + return true; + } + + void write_string_value(const string_view_type& sv) + { + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(msgpack_errc::invalid_utf8_text_string)); + } + + const size_t length = sv.length(); + if (length <= 31) + { + // fixstr stores a byte array whose length is upto 31 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::fixstr_base_type | static_cast(length)); + } + else if (length <= (std::numeric_limits::max)()) + { + // str 8 stores a byte array whose length is upto (2^8)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str8_type); + sink_.push_back(static_cast(length)); + } + else if (length <= (std::numeric_limits::max)()) + { + // str 16 stores a byte array whose length is upto (2^16)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str16_type); + binary::native_to_big(static_cast(length), std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits::max)()) + { + // str 32 stores a byte array whose length is upto (2^32)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str32_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + + for (auto c : sv) + { + sink_.push_back(c); + } + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code&) override + { + + const std::size_t length = b.size(); + if (length <= (std::numeric_limits::max)()) + { + // bin 8 stores a byte array whose length is upto (2^8)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin8_type); + sink_.push_back(static_cast(length)); + } + else if (length <= (std::numeric_limits::max)()) + { + // bin 16 stores a byte array whose length is upto (2^16)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin16_type); + binary::native_to_big(static_cast(length), std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits::max)()) + { + // bin 32 stores a byte array whose length is upto (2^32)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin32_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + + for (auto c : b) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code&) override + { + const std::size_t length = b.size(); + switch (length) + { + case 1: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext1_type); + sink_.push_back(static_cast(ext_tag)); + break; + case 2: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext2_type); + sink_.push_back(static_cast(ext_tag)); + break; + case 4: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type); + sink_.push_back(static_cast(ext_tag)); + break; + case 8: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type); + sink_.push_back(static_cast(ext_tag)); + break; + case 16: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext16_type); + sink_.push_back(static_cast(ext_tag)); + break; + default: + if (length <= (std::numeric_limits::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type); + sink_.push_back(static_cast(length)); + sink_.push_back(static_cast(ext_tag)); + } + else if (length <= (std::numeric_limits::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext16_type); + binary::native_to_big(static_cast(length), std::back_inserter(sink_)); + sink_.push_back(static_cast(ext_tag)); + } + else if (length <= (std::numeric_limits::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext32_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + sink_.push_back(static_cast(ext_tag)); + } + break; + } + + for (auto c : b) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + float valf = (float)val; + if ((double)valf == val) + { + // float 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::float32_type); + binary::native_to_big(valf,std::back_inserter(sink_)); + } + else + { + // float 64 + sink_.push_back(jsoncons::msgpack::msgpack_type::float64_type); + binary::native_to_big(val,std::back_inserter(sink_)); + } + + // write double + + end_value(); + return true; + } + + bool visit_int64(int64_t val, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (tag) + { + case semantic_tag::epoch_second: + write_timestamp(val, 0); + break; + case semantic_tag::epoch_milli: + { + if (val != 0) + { + auto dv = std::div(val,millis_in_second); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem*nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + if (val != 0) + { + auto dv = std::div(val,static_cast(nanos_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + if (val >= 0) + { + if (val <= 0x7f) + { + // positive fixnum stores 7-bit positive integer + sink_.push_back(static_cast(val)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 8 stores a 8-bit unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); + sink_.push_back(static_cast(val)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 16 stores a 16-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 32 stores a 32-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + } + else + { + if (val >= -32) + { + // negative fixnum stores 5-bit negative integer + binary::native_to_big(static_cast(val), std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 8 stores a 8-bit signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int8_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 16 stores a 16-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 32 stores a 32-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + } + } + break; + } + end_value(); + return true; + } + + bool visit_uint64(uint64_t val, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (tag) + { + case semantic_tag::epoch_second: + write_timestamp(static_cast(val), 0); + break; + case semantic_tag::epoch_milli: + { + if (val != 0) + { + auto dv = std::div(static_cast(val), static_cast(millis_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem*nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + if (val != 0) + { + auto dv = std::div(static_cast(val), static_cast(nanos_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + if (val <= static_cast((std::numeric_limits::max)())) + { + // positive fixnum stores 7-bit positive integer + sink_.push_back(static_cast(val)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 8 stores a 8-bit unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); + sink_.push_back(static_cast(val)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 16 stores a 16-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 32 stores a 32-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 64 stores a 64-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + break; + } + } + end_value(); + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + // true and false + sink_.push_back(static_cast(val ? jsoncons::msgpack::msgpack_type::true_type : jsoncons::msgpack::msgpack_type::false_type)); + + end_value(); + return true; + } + + void end_value() + { + if (!stack_.empty()) + { + ++stack_.back().count_; + } + } + }; + + using msgpack_stream_encoder = basic_msgpack_encoder; + using msgpack_bytes_encoder = basic_msgpack_encoder>>; + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_bytes_serializer; + + template + using basic_msgpack_serializer = basic_msgpack_encoder; + + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_buffer_serializer; + #endif + +} // namespace msgpack +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack_error.hpp b/include/jsoncons_ext/msgpack/msgpack_error.hpp new file mode 100644 index 0000000..80c76b6 --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_error.hpp @@ -0,0 +1,94 @@ +/// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_ERROR_HPP +#define JSONCONS_MSGPACK_MSGPACK_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace msgpack { + +enum class msgpack_errc +{ + success = 0, + unexpected_eof = 1, + source_error, + invalid_utf8_text_string, + array_length_required, + object_length_required, + too_many_items, + too_few_items, + max_nesting_depth_exceeded, + length_is_negative, + invalid_timestamp, + unknown_type +}; + +class msgpack_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/msgpack"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case msgpack_errc::unexpected_eof: + return "Unexpected end of file"; + case msgpack_errc::source_error: + return "Source error"; + case msgpack_errc::invalid_utf8_text_string: + return "Illegal UTF-8 encoding in text string"; + case msgpack_errc::array_length_required: + return "MessagePack encoder requires array length"; + case msgpack_errc::object_length_required: + return "MessagePack encoder requires object length"; + case msgpack_errc::too_many_items: + return "Too many items were added to a MessagePack object or array"; + case msgpack_errc::too_few_items: + return "Too few items were added to a MessagePack object or array"; + case msgpack_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case msgpack_errc::length_is_negative: + return "Request for the length of an array, map or string returned a negative result"; + case msgpack_errc::invalid_timestamp: + return "Invalid timestamp"; + case msgpack_errc::unknown_type: + return "An unknown type was found in the stream"; + default: + return "Unknown MessagePack parser error"; + } + } +}; + +inline +const std::error_category& msgpack_error_category() +{ + static msgpack_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(msgpack_errc e) +{ + return std::error_code(static_cast(e),msgpack_error_category()); +} + + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack_options.hpp b/include/jsoncons_ext/msgpack/msgpack_options.hpp new file mode 100644 index 0000000..17bddf1 --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_options.hpp @@ -0,0 +1,74 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_OPTIONS_HPP +#define JSONCONS_MSGPACK_MSGPACK_OPTIONS_HPP + +#include +#include // std::numeric_limits +#include +#include + +namespace jsoncons { namespace msgpack { + +class msgpack_options; + +class msgpack_options_common +{ + friend class msgpack_options; + + int max_nesting_depth_; +protected: + virtual ~msgpack_options_common() = default; + + msgpack_options_common() + : max_nesting_depth_(1024) + { + } + + msgpack_options_common(const msgpack_options_common&) = default; + msgpack_options_common& operator=(const msgpack_options_common&) = default; + msgpack_options_common(msgpack_options_common&&) = default; + msgpack_options_common& operator=(msgpack_options_common&&) = default; +public: + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class msgpack_decode_options : public virtual msgpack_options_common +{ + friend class msgpack_options; +public: + msgpack_decode_options() + { + } +}; + +class msgpack_encode_options : public virtual msgpack_options_common +{ + friend class msgpack_options; +public: + msgpack_encode_options() + { + } +}; + +class msgpack_options final : public msgpack_decode_options, public msgpack_encode_options +{ +public: + using msgpack_options_common::max_nesting_depth; + + msgpack_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } +}; + +}} +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack_parser.hpp b/include/jsoncons_ext/msgpack/msgpack_parser.hpp new file mode 100644 index 0000000..cf2d507 --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_parser.hpp @@ -0,0 +1,748 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_PARSER_HPP +#define JSONCONS_MSGPACK_MSGPACK_PARSER_HPP + +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace msgpack { + +enum class parse_mode {root,accept,array,map_key,map_value}; + +struct parse_state +{ + parse_mode mode; + std::size_t length; + std::size_t index; + + parse_state(parse_mode mode, std::size_t length) noexcept + : mode(mode), length(length), index(0) + { + } + + parse_state(const parse_state&) = default; + parse_state(parse_state&&) = default; +}; + +template > +class basic_msgpack_parser : public ser_context +{ + using char_type = char; + using char_traits_type = std::char_traits; + using temp_allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using int64_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + static constexpr int64_t nanos_in_second = 1000000000; + + Source source_; + msgpack_decode_options options_; + bool more_; + bool done_; + std::basic_string,char_allocator_type> text_buffer_; + std::vector bytes_buffer_; + std::vector state_stack_; + int nesting_depth_; + +public: + template + basic_msgpack_parser(Sourceable&& source, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator alloc = Allocator()) + : source_(std::forward(source)), + options_(options), + more_(true), + done_(false), + text_buffer_(alloc), + bytes_buffer_(alloc), + state_stack_(alloc), + nesting_depth_(0) + { + state_stack_.emplace_back(parse_mode::root,0); + } + + void restart() + { + more_ = true; + } + + void reset() + { + more_ = true; + done_ = false; + text_buffer_.clear(); + bytes_buffer_.clear(); + state_stack_.clear(); + state_stack_.emplace_back(parse_mode::root,0); + nesting_depth_ = 0; + } + + template + void reset(Sourceable&& source) + { + source_ = std::forward(source); + reset(); + } + + bool done() const + { + return done_; + } + + bool stopped() const + { + return !more_; + } + + std::size_t line() const override + { + return 0; + } + + std::size_t column() const override + { + return source_.position(); + } + + void parse(json_visitor2& visitor, std::error_code& ec) + { + while (!done_ && more_) + { + switch (state_stack_.back().mode) + { + case parse_mode::array: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_item(visitor, ec); + if (ec) + { + return; + } + } + else + { + end_array(visitor, ec); + } + break; + } + case parse_mode::map_key: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + state_stack_.back().mode = parse_mode::map_value; + read_item(visitor, ec); + if (ec) + { + return; + } + } + else + { + end_object(visitor, ec); + } + break; + } + case parse_mode::map_value: + { + state_stack_.back().mode = parse_mode::map_key; + read_item(visitor, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::root: + { + state_stack_.back().mode = parse_mode::accept; + read_item(visitor, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::accept: + { + JSONCONS_ASSERT(state_stack_.size() == 1); + state_stack_.clear(); + more_ = false; + done_ = true; + visitor.flush(); + break; + } + } + } + } +private: + + void read_item(json_visitor2& visitor, std::error_code& ec) + { + if (source_.is_error()) + { + ec = msgpack_errc::source_error; + more_ = false; + return; + } + + uint8_t type; + if (source_.read(&type, 1) == 0) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + if (type <= 0xbf) + { + if (type <= 0x7f) + { + // positive fixint + more_ = visitor.uint64_value(type, semantic_tag::none, *this, ec); + } + else if (type <= 0x8f) + { + begin_object(visitor,type,ec); // fixmap + } + else if (type <= 0x9f) + { + begin_array(visitor,type,ec); // fixarray + } + else + { + // fixstr + const size_t len = type & 0x1f; + + text_buffer_.clear(); + + if (source_reader::read(source_,text_buffer_,len) != static_cast(len)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = msgpack_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec); + } + } + else if (type >= 0xe0) + { + // negative fixint + more_ = visitor.int64_value(static_cast(type), semantic_tag::none, *this, ec); + } + else + { + switch (type) + { + case jsoncons::msgpack::msgpack_type::nil_type: + { + more_ = visitor.null_value(semantic_tag::none, *this, ec); + break; + } + case jsoncons::msgpack::msgpack_type::true_type: + { + more_ = visitor.bool_value(true, semantic_tag::none, *this, ec); + break; + } + case jsoncons::msgpack::msgpack_type::false_type: + { + more_ = visitor.bool_value(false, semantic_tag::none, *this, ec); + break; + } + case jsoncons::msgpack::msgpack_type::float32_type: + { + uint8_t buf[sizeof(float)]; + if (source_.read(buf, sizeof(float)) != sizeof(float)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + float val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.double_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::float64_type: + { + uint8_t buf[sizeof(double)]; + if (source_.read(buf, sizeof(double)) != sizeof(double)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + double val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.double_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::uint8_type: + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + more_ = visitor.uint64_value(b, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::uint16_type: + { + uint8_t buf[sizeof(uint16_t)]; + if (source_.read(buf, sizeof(uint16_t)) !=sizeof(uint16_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint16_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::uint32_type: + { + uint8_t buf[sizeof(uint32_t)]; + if (source_.read(buf, sizeof(uint32_t)) != sizeof(uint32_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint32_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::uint64_type: + { + uint8_t buf[sizeof(uint64_t)]; + if (source_.read(buf, sizeof(uint64_t)) != sizeof(uint64_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint64_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::int8_type: + { + uint8_t buf[sizeof(int8_t)]; + if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + int8_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::int16_type: + { + uint8_t buf[sizeof(int16_t)]; + if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + int16_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::int32_type: + { + uint8_t buf[sizeof(int32_t)]; + if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + int32_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::int64_type: + { + uint8_t buf[sizeof(int64_t)]; + if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + int64_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::str8_type: + case jsoncons::msgpack::msgpack_type::str16_type: + case jsoncons::msgpack::msgpack_type::str32_type: + { + std::size_t len = get_size(type, ec); + if (!more_) + { + return; + } + + text_buffer_.clear(); + if (source_reader::read(source_,text_buffer_,len) != static_cast(len)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = msgpack_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec); + break; + } + + case jsoncons::msgpack::msgpack_type::bin8_type: + case jsoncons::msgpack::msgpack_type::bin16_type: + case jsoncons::msgpack::msgpack_type::bin32_type: + { + std::size_t len = get_size(type,ec); + if (!more_) + { + return; + } + bytes_buffer_.clear(); + if (source_reader::read(source_,bytes_buffer_,len) != static_cast(len)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + more_ = visitor.byte_string_value(byte_string_view(bytes_buffer_.data(),bytes_buffer_.size()), + semantic_tag::none, + *this, + ec); + break; + } + case jsoncons::msgpack::msgpack_type::fixext1_type: + case jsoncons::msgpack::msgpack_type::fixext2_type: + case jsoncons::msgpack::msgpack_type::fixext4_type: + case jsoncons::msgpack::msgpack_type::fixext8_type: + case jsoncons::msgpack::msgpack_type::fixext16_type: + case jsoncons::msgpack::msgpack_type::ext8_type: + case jsoncons::msgpack::msgpack_type::ext16_type: + case jsoncons::msgpack::msgpack_type::ext32_type: + { + std::size_t len = get_size(type,ec); + if (!more_) + { + return; + } + + // type + uint8_t buf[sizeof(int8_t)]; + if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + int8_t ext_type = binary::big_to_native(buf, sizeof(buf)); + + bool is_timestamp = false; + if (ext_type == -1) + { + is_timestamp = true;; + } + + // payload + if (is_timestamp && len == 4) + { + uint8_t buf32[sizeof(uint32_t)]; + if (source_.read(buf32, sizeof(uint32_t)) != sizeof(uint32_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint32_t val = binary::big_to_native(buf32, sizeof(buf32)); + more_ = visitor.uint64_value(val, semantic_tag::epoch_second, *this, ec); + } + else if (is_timestamp && len == 8) + { + uint8_t buf64[sizeof(uint64_t)]; + if (source_.read(buf64, sizeof(uint64_t)) != sizeof(uint64_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint64_t data64 = binary::big_to_native(buf64, sizeof(buf64)); + uint64_t sec = data64 & 0x00000003ffffffffL; + uint64_t nsec = data64 >> 34; + + bigint nano(sec); + nano *= uint64_t(nanos_in_second); + nano += nsec; + text_buffer_.clear(); + nano.write_string(text_buffer_); + more_ = visitor.string_value(text_buffer_, semantic_tag::epoch_nano, *this, ec); + if (!more_) return; + } + else if (is_timestamp && len == 12) + { + uint8_t buf1[sizeof(uint32_t)]; + if (source_.read(buf1, sizeof(uint32_t)) != sizeof(uint32_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + uint32_t nsec = binary::big_to_native(buf1, sizeof(buf1)); + + uint8_t buf2[sizeof(int64_t)]; + if (source_.read(buf2, sizeof(int64_t)) != sizeof(int64_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + int64_t sec = binary::big_to_native(buf2, sizeof(buf2)); + + bigint nano(sec); + + nano *= uint64_t(nanos_in_second); + + if (nano < 0) + { + nano -= nsec; + } + else + { + nano += nsec; + } + + text_buffer_.clear(); + nano.write_string(text_buffer_); + more_ = visitor.string_value(text_buffer_, semantic_tag::epoch_nano, *this, ec); + if (!more_) return; + } + else + { + bytes_buffer_.clear(); + if (source_reader::read(source_,bytes_buffer_,len) != static_cast(len)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return; + } + + more_ = visitor.byte_string_value(byte_string_view(bytes_buffer_.data(),bytes_buffer_.size()), + static_cast(ext_type), + *this, + ec); + } + break; + } + + case jsoncons::msgpack::msgpack_type::array16_type: + case jsoncons::msgpack::msgpack_type::array32_type: + { + begin_array(visitor,type,ec); + break; + } + + case jsoncons::msgpack::msgpack_type::map16_type : + case jsoncons::msgpack::msgpack_type::map32_type : + { + begin_object(visitor, type, ec); + break; + } + + default: + { + ec = msgpack_errc::unknown_type; + more_ = false; + return; + } + } + } + } + + void begin_array(json_visitor2& visitor, uint8_t type, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + std::size_t length = get_size(type, ec); + if (!more_) + { + return; + } + state_stack_.emplace_back(parse_mode::array,length); + more_ = visitor.begin_array(length, semantic_tag::none, *this, ec); + } + + void end_array(json_visitor2& visitor, std::error_code& ec) + { + --nesting_depth_; + + more_ = visitor.end_array(*this, ec); + state_stack_.pop_back(); + } + + void begin_object(json_visitor2& visitor, uint8_t type, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + std::size_t length = get_size(type, ec); + if (!more_) + { + return; + } + state_stack_.emplace_back(parse_mode::map_key,length); + more_ = visitor.begin_object(length, semantic_tag::none, *this, ec); + } + + void end_object(json_visitor2& visitor, std::error_code& ec) + { + --nesting_depth_; + more_ = visitor.end_object(*this, ec); + state_stack_.pop_back(); + } + + std::size_t get_size(uint8_t type, std::error_code& ec) + { + switch (type) + { + case jsoncons::msgpack::msgpack_type::str8_type: + case jsoncons::msgpack::msgpack_type::bin8_type: + case jsoncons::msgpack::msgpack_type::ext8_type: + { + uint8_t buf[sizeof(int8_t)]; + if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return 0; + } + uint8_t len = binary::big_to_native(buf, sizeof(buf)); + return static_cast(len); + } + + case jsoncons::msgpack::msgpack_type::str16_type: + case jsoncons::msgpack::msgpack_type::bin16_type: + case jsoncons::msgpack::msgpack_type::ext16_type: + case jsoncons::msgpack::msgpack_type::array16_type: + case jsoncons::msgpack::msgpack_type::map16_type: + { + uint8_t buf[sizeof(int16_t)]; + if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return 0; + } + uint16_t len = binary::big_to_native(buf, sizeof(buf)); + return static_cast(len); + } + + case jsoncons::msgpack::msgpack_type::str32_type: + case jsoncons::msgpack::msgpack_type::bin32_type: + case jsoncons::msgpack::msgpack_type::ext32_type: + case jsoncons::msgpack::msgpack_type::array32_type: + case jsoncons::msgpack::msgpack_type::map32_type : + { + uint8_t buf[sizeof(int32_t)]; + if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t)) + { + ec = msgpack_errc::unexpected_eof; + more_ = false; + return 0; + } + uint32_t len = binary::big_to_native(buf, sizeof(buf)); + return static_cast(len); + } + case jsoncons::msgpack::msgpack_type::fixext1_type: + return 1; + case jsoncons::msgpack::msgpack_type::fixext2_type: + return 2; + case jsoncons::msgpack::msgpack_type::fixext4_type: + return 4; + case jsoncons::msgpack::msgpack_type::fixext8_type: + return 8; + case jsoncons::msgpack::msgpack_type::fixext16_type: + return 16; + default: + if ((type > 0x8f && type <= 0x9f) // fixarray + || (type > 0x7f && type <= 0x8f) // fixmap + ) + { + return type & 0x0f; + } + else + { + ec = msgpack_errc::unknown_type; + more_ = false; + return 0; + } + break; + } + } +}; + +}} + +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack_reader.hpp b/include/jsoncons_ext/msgpack/msgpack_reader.hpp new file mode 100644 index 0000000..c0d788a --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_reader.hpp @@ -0,0 +1,116 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_READER_HPP +#define JSONCONS_MSGPACK_MSGPACK_READER_HPP + +#include +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace msgpack { + +template > +class basic_msgpack_reader +{ + using char_type = char; + + basic_msgpack_parser parser_; + basic_json_visitor2_to_visitor_adaptor adaptor_; + json_visitor2& visitor_; +public: + template + basic_msgpack_reader(Sourceable&& source, + json_visitor& visitor, + const Allocator alloc) + : basic_msgpack_reader(std::forward(source), + visitor, + msgpack_decode_options(), + alloc) + { + } + + template + basic_msgpack_reader(Sourceable&& source, + json_visitor& visitor, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + adaptor_(visitor, alloc), visitor_(adaptor_) + { + } + template + basic_msgpack_reader(Sourceable&& source, + json_visitor2& visitor, + const Allocator alloc) + : basic_msgpack_reader(std::forward(source), + visitor, + msgpack_decode_options(), + alloc) + { + } + + template + basic_msgpack_reader(Sourceable&& source, + json_visitor2& visitor, + const msgpack_decode_options& options = msgpack_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + visitor_(visitor) + { + } + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line(),column())); + } + } + + void read(std::error_code& ec) + { + parser_.reset(); + parser_.parse(visitor_, ec); + if (ec) + { + return; + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } +}; + +using msgpack_stream_reader = basic_msgpack_reader; + +using msgpack_bytes_reader = basic_msgpack_reader; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_reader") typedef msgpack_stream_reader msgpack_reader; +JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_reader") typedef msgpack_bytes_reader msgpack_buffer_reader; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/msgpack/msgpack_type.hpp b/include/jsoncons_ext/msgpack/msgpack_type.hpp new file mode 100644 index 0000000..aa9f8fd --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_type.hpp @@ -0,0 +1,63 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_TYPE_HPP +#define JSONCONS_MSGPACK_MSGPACK_TYPE_HPP + +#include +#include +#include + +namespace jsoncons { namespace msgpack { + + namespace msgpack_type + { + const uint8_t positive_fixint_base_type = 0x00; + const uint8_t nil_type = 0xc0; + const uint8_t false_type = 0xc2; + const uint8_t true_type = 0xc3; + const uint8_t float32_type = 0xca; + const uint8_t float64_type = 0xcb; + const uint8_t uint8_type = 0xcc; + const uint8_t uint16_type = 0xcd; + const uint8_t uint32_type = 0xce; + const uint8_t uint64_type = 0xcf; + const uint8_t int8_type = 0xd0; + const uint8_t int16_type = 0xd1; + const uint8_t int32_type = 0xd2; + const uint8_t int64_type = 0xd3; + + const uint8_t fixmap_base_type = 0x80; + const uint8_t fixarray_base_type = 0x90; + const uint8_t fixstr_base_type = 0xa0; + const uint8_t str8_type = 0xd9; + const uint8_t str16_type = 0xda; + const uint8_t str32_type = 0xdb; + + const uint8_t bin8_type = 0xc4; // 0xC4 + const uint8_t bin16_type = 0xc5; + const uint8_t bin32_type = 0xc6; + + const uint8_t fixext1_type = 0xd4; + const uint8_t fixext2_type = 0xd5; + const uint8_t fixext4_type = 0xd6; + const uint8_t fixext8_type = 0xd7; + const uint8_t fixext16_type = 0xd8; + const uint8_t ext8_type = 0xc7; // 0xC4 + const uint8_t ext16_type = 0xc8; + const uint8_t ext32_type = 0xc9; + + const uint8_t array16_type = 0xdc; + const uint8_t array32_type = 0xdd; + const uint8_t map16_type = 0xde; + const uint8_t map32_type = 0xdf; + const uint8_t negative_fixint_base_type = 0xe0; + } + +} // namespace msgpack +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/ubjson/decode_ubjson.hpp b/include/jsoncons_ext/ubjson/decode_ubjson.hpp new file mode 100644 index 0000000..91c9c0f --- /dev/null +++ b/include/jsoncons_ext/ubjson/decode_ubjson.hpp @@ -0,0 +1,201 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_DECODE_UBJSON_HPP +#define JSONCONS_UBJSON_DECODE_UBJSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include + +namespace jsoncons { +namespace ubjson { + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_ubjson(const Source& v, + const ubjson_decode_options& options = ubjson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(v, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_ubjson(const Source& v, + const ubjson_decode_options& options = ubjson_decode_options()) + { + basic_ubjson_cursor cursor(v, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + ubjson_stream_reader reader(is, adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) + { + basic_ubjson_cursor cursor(is, options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(InputIt first, InputIt last, + const ubjson_decode_options& options = ubjson_decode_options()) + { + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(InputIt first, InputIt last, + const ubjson_decode_options& options = ubjson_decode_options()) + { + basic_ubjson_cursor> cursor(binary_iterator_source(first, last), options); + json_decoder> decoder{}; + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + // With leading allocator parameter + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const ubjson_decode_options& options = ubjson_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(v, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value && + type_traits::is_byte_sequence::value,T>::type + decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const Source& v, + const ubjson_decode_options& options = ubjson_decode_options()) + { + basic_ubjson_cursor cursor(v, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) + { + json_decoder decoder(temp_alloc); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(is, adaptor, options, temp_alloc); + reader.read(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); + } + return decoder.get_result(); + } + + template + typename std::enable_if::value,T>::type + decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) + { + basic_ubjson_cursor cursor(is, options, temp_alloc); + json_decoder,TempAllocator> decoder(temp_alloc, temp_alloc); + + std::error_code ec; + T val = decode_traits::decode(cursor, decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + return val; + } + +} // ubjson +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/ubjson/encode_ubjson.hpp b/include/jsoncons_ext/ubjson/encode_ubjson.hpp new file mode 100644 index 0000000..e8a244b --- /dev/null +++ b/include/jsoncons_ext/ubjson/encode_ubjson.hpp @@ -0,0 +1,142 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_ENCODE_UBJSON_HPP +#define JSONCONS_UBJSON_ENCODE_UBJSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include + +namespace jsoncons { +namespace ubjson { + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_ubjson(const T& j, + Container& v, + const ubjson_encode_options& options = ubjson_encode_options()) + { + using char_type = typename T::char_type; + basic_ubjson_encoder> encoder(v, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_ubjson(const T& val, + Container& v, + const ubjson_encode_options& options = ubjson_encode_options()) + { + basic_ubjson_encoder> encoder(v, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_ubjson(const T& j, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) + { + using char_type = typename T::char_type; + ubjson_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_ubjson(const T& val, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) + { + ubjson_stream_encoder encoder(os, options); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + // with temp_allocator_arg_t + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,const T& j, + Container& v, + const ubjson_encode_options& options = ubjson_encode_options()) + { + using char_type = typename T::char_type; + basic_ubjson_encoder,TempAllocator> encoder(v, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value && + type_traits::is_back_insertable_byte_container::value,void>::type + encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,const T& val, + Container& v, + const ubjson_encode_options& options = ubjson_encode_options()) + { + basic_ubjson_encoder,TempAllocator> encoder(v, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value,void>::type + encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& j, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) + { + using char_type = typename T::char_type; + basic_ubjson_encoder encoder(os, options, temp_alloc); + auto adaptor = make_json_visitor_adaptor>(encoder); + j.dump(adaptor); + } + + template + typename std::enable_if::value,void>::type + encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc, + const T& val, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) + { + basic_ubjson_encoder encoder(os, options, temp_alloc); + std::error_code ec; + encode_traits::encode(val, encoder, json(), ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec)); + } + } + +} // ubjson +} // jsoncons + +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson.hpp b/include/jsoncons_ext/ubjson/ubjson.hpp new file mode 100644 index 0000000..c2729bd --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson.hpp @@ -0,0 +1,23 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_HPP +#define JSONCONS_UBJSON_UBJSON_HPP + +#include +#include +#include +#include // std::enable_if +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp new file mode 100644 index 0000000..f60825e --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp @@ -0,0 +1,307 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_CURSOR_HPP +#define JSONCONS_UBJSON_UBJSON_CURSOR_HPP + +#include // std::allocator +#include +#include +#include +#include +#include +#include // std::basic_istream +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace ubjson { + +template> +class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_context +{ +public: + using source_type = Source; + using char_type = char; + using allocator_type = Allocator; +private: + basic_ubjson_parser parser_; + basic_staj_visitor cursor_visitor_; + bool eof_; + + // Noncopyable and nonmoveable + basic_ubjson_cursor(const basic_ubjson_cursor&) = delete; + basic_ubjson_cursor& operator=(const basic_ubjson_cursor&) = delete; + +public: + using string_view_type = string_view; + + template + basic_ubjson_cursor(Sourceable&& source, + const ubjson_decode_options& options = ubjson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(); + } + } + + // Constructors that set parse error codes + + template + basic_ubjson_cursor(Sourceable&& source, + std::error_code& ec) + : basic_ubjson_cursor(std::allocator_arg, Allocator(), + std::forward(source), + ubjson_decode_options(), + ec) + { + } + + template + basic_ubjson_cursor(Sourceable&& source, + const ubjson_decode_options& options, + std::error_code& ec) + : basic_ubjson_cursor(std::allocator_arg, Allocator(), + std::forward(source), + options, + ec) + { + } + + template + basic_ubjson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const ubjson_decode_options& options, + std::error_code& ec) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(accept_all), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + void reset() + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + template + void reset(Sourceable&& source) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(); + } + } + + void reset(std::error_code& ec) + { + parser_.reset(); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + template + void reset(Sourceable&& source, std::error_code& ec) + { + parser_.reset(std::forward(source)); + cursor_visitor_.reset(); + eof_ = false; + if (!done()) + { + next(ec); + } + } + + bool done() const override + { + return parser_.done(); + } + + const staj_event& current() const override + { + return cursor_visitor_.event(); + } + + void read_to(basic_json_visitor& visitor) override + { + std::error_code ec; + read_to(visitor, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void read_to(basic_json_visitor& visitor, + std::error_code& ec) override + { + if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec)) + { + read_next(visitor, ec); + } + } + + void next() override + { + std::error_code ec; + next(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + + void next(std::error_code& ec) override + { + read_next(ec); + } + + const ser_context& context() const override + { + return *this; + } + + bool eof() const + { + return eof_; + } + + std::size_t line() const override + { + return parser_.line(); + } + + std::size_t column() const override + { + return parser_.column(); + } + + friend + staj_filter_view operator|(basic_ubjson_cursor& cursor, + std::function pred) + { + return staj_filter_view(cursor, pred); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_ubjson_cursor(Sourceable&& source, + std::function filter, + const ubjson_decode_options& options = ubjson_decode_options(), + const Allocator& alloc = Allocator()) + : parser_(std::forward(source), options, alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(); + } + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_ubjson_cursor(Sourceable&& source, + std::function filter, + std::error_code& ec) + : basic_ubjson_cursor(std::allocator_arg, Allocator(), + std::forward(source), filter, ec) + { + } + + template + JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter") + basic_ubjson_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + std::function filter, + std::error_code& ec) + : parser_(std::forward(source), alloc), + cursor_visitor_(filter), + eof_(false) + { + if (!done()) + { + next(ec); + } + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&)") + void read(basic_json_visitor& visitor) + { + read_to(visitor); + } + + JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor&, std::error_code&)") + void read(basic_json_visitor& visitor, + std::error_code& ec) + { + read_to(visitor, ec); + } +#endif +private: + static bool accept_all(const staj_event&, const ser_context&) + { + return true; + } + + void read_next(std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(cursor_visitor_, ec); + if (ec) return; + } + } + + void read_next(basic_json_visitor& visitor, std::error_code& ec) + { + parser_.restart(); + while (!parser_.stopped()) + { + parser_.parse(visitor, ec); + if (ec) return; + } + } +}; + +using ubjson_stream_cursor = basic_ubjson_cursor; +using ubjson_bytes_cursor = basic_ubjson_cursor; + +} // namespace ubjson +} // namespace jsoncons + +#endif + diff --git a/include/jsoncons_ext/ubjson/ubjson_encoder.hpp b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp new file mode 100644 index 0000000..2d90e4a --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp @@ -0,0 +1,502 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_ENCODER_HPP +#define JSONCONS_UBJSON_UBJSON_ENCODER_HPP + +#include +#include +#include // std::numeric_limits +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace ubjson { + +enum class ubjson_container_type {object, indefinite_length_object, array, indefinite_length_array}; + +template> +class basic_ubjson_encoder final : public basic_json_visitor +{ + + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; +public: + using allocator_type = Allocator; + using typename basic_json_visitor::string_view_type; + using sink_type = Sink; + +private: + struct stack_item + { + ubjson_container_type type_; + std::size_t length_; + std::size_t count_; + + stack_item(ubjson_container_type type, std::size_t length = 0) noexcept + : type_(type), length_(length), count_(0) + { + } + + std::size_t length() const + { + return length_; + } + + std::size_t count() const + { + return count_; + } + + bool is_object() const + { + return type_ == ubjson_container_type::object || type_ == ubjson_container_type::indefinite_length_object; + } + + bool is_indefinite_length() const + { + return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object; + } + + }; + + Sink sink_; + const ubjson_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_ubjson_encoder(const basic_ubjson_encoder&) = delete; + basic_ubjson_encoder& operator=(const basic_ubjson_encoder&) = delete; +public: + basic_ubjson_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_ubjson_encoder(std::forward(sink), ubjson_encode_options(), alloc) + { + } + + explicit basic_ubjson_encoder(Sink&& sink, + const ubjson_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + void reset() + { + stack_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + + ~basic_ubjson_encoder() noexcept + { + JSONCONS_TRY + { + sink_.flush(); + } + JSONCONS_CATCH(...) + { + } + } + +private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(ubjson_container_type::indefinite_length_object); + sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker); + + return true; + } + + bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(ubjson_container_type::object, length); + sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker); + sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker); + put_length(length); + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().is_indefinite_length()) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::end_object_marker); + } + else + { + if (stack_.back().count() < stack_.back().length()) + { + ec = ubjson_errc::too_few_items; + return false; + } + if (stack_.back().count() > stack_.back().length()) + { + ec = ubjson_errc::too_many_items; + return false; + } + } + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(ubjson_container_type::indefinite_length_array); + sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker); + + return true; + } + + bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(ubjson_container_type::array, length); + sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker); + sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker); + put_length(length); + + return true; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().is_indefinite_length()) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::end_array_marker); + } + else + { + if (stack_.back().count() < stack_.back().length()) + { + ec = ubjson_errc::too_few_items; + return false; + } + if (stack_.back().count() > stack_.back().length()) + { + ec = ubjson_errc::too_many_items; + return false; + } + } + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code& ec) override + { + auto sink = unicode_traits::validate(name.data(), name.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + ec = ubjson_errc::invalid_utf8_text_string; + return false; + } + + put_length(name.length()); + + for (auto c : name) + { + sink_.push_back(c); + } + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + // nil + binary::native_to_big(static_cast(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_)); + end_value(); + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::bigint: + case semantic_tag::bigdec: + { + sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type); + break; + } + default: + { + sink_.push_back(jsoncons::ubjson::ubjson_type::string_type); + break; + } + } + + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + ec = ubjson_errc::invalid_utf8_text_string; + return false; + } + + put_length(sv.length()); + + for (auto c : sv) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + void put_length(std::size_t length) + { + if (length <= (std::numeric_limits::max)()) + { + sink_.push_back(ubjson_type::uint8_type); + binary::native_to_big(static_cast(length), std::back_inserter(sink_)); + } + else if (length <= (std::size_t)(std::numeric_limits::max)()) + { + sink_.push_back(ubjson_type::int16_type); + binary::native_to_big(static_cast(length), std::back_inserter(sink_)); + } + else if (length <= (std::size_t)(std::numeric_limits::max)()) + { + sink_.push_back(ubjson_type::int32_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + else if (length <= (std::size_t)(std::numeric_limits::max)()) + { + sink_.push_back(ubjson_type::int64_type); + binary::native_to_big(static_cast(length),std::back_inserter(sink_)); + } + else + { + JSONCONS_THROW(ser_error(ubjson_errc::too_many_items)); + } + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code&) override + { + + const size_t length = b.size(); + sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker); + binary::native_to_big(static_cast(jsoncons::ubjson::ubjson_type::type_marker), std::back_inserter(sink_)); + binary::native_to_big(static_cast(jsoncons::ubjson::ubjson_type::uint8_type), std::back_inserter(sink_)); + put_length(length); + + for (auto c : b) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + float valf = (float)val; + if ((double)valf == val) + { + // float 32 + sink_.push_back(static_cast(jsoncons::ubjson::ubjson_type::float32_type)); + binary::native_to_big(valf,std::back_inserter(sink_)); + } + else + { + // float 64 + sink_.push_back(static_cast(jsoncons::ubjson::ubjson_type::float64_type)); + binary::native_to_big(val,std::back_inserter(sink_)); + } + + // write double + + end_value(); + return true; + } + + bool visit_int64(int64_t val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (val >= 0) + { + if (val <= (std::numeric_limits::max)()) + { + // uint 8 stores a 8-bit unsigned integer + sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 16 stores a 16-bit big-endian unsigned integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // uint 32 stores a 32-bit big-endian unsigned integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits::max)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else + { + // big integer + } + } + else + { + if (val >= (std::numeric_limits::lowest)()) + { + // int 8 stores a 8-bit signed integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int8_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 16 stores a 16-bit big-endian signed integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 32 stores a 32-bit big-endian signed integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits::lowest)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + } + end_value(); + return true; + } + + bool visit_uint64(uint64_t val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + if (val <= (std::numeric_limits::max)()) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= static_cast((std::numeric_limits::max)())) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= static_cast((std::numeric_limits::max)())) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + else if (val <= static_cast((std::numeric_limits::max)())) + { + sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type); + binary::native_to_big(static_cast(val),std::back_inserter(sink_)); + } + end_value(); + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + // true and false + sink_.push_back(static_cast(val ? jsoncons::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type)); + + end_value(); + return true; + } + + void end_value() + { + if (!stack_.empty()) + { + ++stack_.back().count_; + } + } +}; + +using ubjson_stream_encoder = basic_ubjson_encoder; +using ubjson_bytes_encoder = basic_ubjson_encoder>>; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +using basic_ubjson_serializer = basic_ubjson_encoder; + +JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use ubjson_bytes_encoder") typedef ubjson_bytes_encoder ubjson_buffer_serializer; +#endif + +}} +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_error.hpp b/include/jsoncons_ext/ubjson/ubjson_error.hpp new file mode 100644 index 0000000..cc7f5b8 --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_error.hpp @@ -0,0 +1,100 @@ +/// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_ERROR_HPP +#define JSONCONS_UBJSON_UBJSON_ERROR_HPP + +#include +#include + +namespace jsoncons { namespace ubjson { + +enum class ubjson_errc +{ + success = 0, + unexpected_eof = 1, + source_error, + count_required_after_type, + length_is_negative, + length_must_be_integer, + unknown_type, + invalid_utf8_text_string, + too_many_items, + too_few_items, + number_too_large, + max_nesting_depth_exceeded, + key_expected, + max_items_exceeded +}; + +class ubjson_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/ubjson"; + } + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case ubjson_errc::unexpected_eof: + return "Unexpected end of file"; + case ubjson_errc::source_error: + return "Source error"; + case ubjson_errc::count_required_after_type: + return "Type is specified for container, but count is not specified"; + case ubjson_errc::length_is_negative: + return "Request for the length of an array, map or string returned a negative result"; + case ubjson_errc::length_must_be_integer: + return "Length must be a integer numeric type (int8, uint8, int16, int32, int64)"; + case ubjson_errc::unknown_type: + return "Unknown type"; + case ubjson_errc::invalid_utf8_text_string: + return "Illegal UTF-8 encoding in text string"; + case ubjson_errc::too_many_items: + return "Too many items were added to a UBJSON object or array of known length"; + case ubjson_errc::too_few_items: + return "Too few items were added to a UBJSON object or array of known length"; + case ubjson_errc::number_too_large: + return "Number exceeds implementation limits"; + case ubjson_errc::max_nesting_depth_exceeded: + return "Data item nesting exceeds limit in options"; + case ubjson_errc::key_expected: + return "Text string key in a map expected"; + case ubjson_errc::max_items_exceeded: + return "Number of items in UBJSON object or array exceeds limit set in options"; + default: + return "Unknown UBJSON parser error"; + } + } +}; + +inline +const std::error_category& ubjson_error_category() +{ + static ubjson_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(ubjson_errc e) +{ + return std::error_code(static_cast(e),ubjson_error_category()); +} + + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_options.hpp b/include/jsoncons_ext/ubjson/ubjson_options.hpp new file mode 100644 index 0000000..1498d50 --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_options.hpp @@ -0,0 +1,87 @@ +// Copyright 2019 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_OPTIONS_HPP +#define JSONCONS_UBJSON_UBJSON_OPTIONS_HPP + +#include +#include // std::numeric_limits +#include +#include + +namespace jsoncons { namespace ubjson { + +class ubjson_options; + +class ubjson_options_common +{ + friend class ubjson_options; + + int max_nesting_depth_; +protected: + virtual ~ubjson_options_common() = default; + + ubjson_options_common() + : max_nesting_depth_(1024) + { + } + + ubjson_options_common(const ubjson_options_common&) = default; + ubjson_options_common& operator=(const ubjson_options_common&) = default; + ubjson_options_common(ubjson_options_common&&) = default; + ubjson_options_common& operator=(ubjson_options_common&&) = default; +public: + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class ubjson_decode_options : public virtual ubjson_options_common +{ + friend class ubjson_options; + std::size_t max_items_; +public: + ubjson_decode_options() : + max_items_(1 << 24) + { + } + + std::size_t max_items() const + { + return max_items_; + } +}; + +class ubjson_encode_options : public virtual ubjson_options_common +{ + friend class ubjson_options; +public: + ubjson_encode_options() + { + } +}; + +class ubjson_options final : public ubjson_decode_options, public ubjson_encode_options +{ +public: + using ubjson_options_common::max_nesting_depth; + + ubjson_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } + + ubjson_options& max_items(std::size_t value) + { + this->max_items_ = value; + return *this; + } +}; + +}} +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_parser.hpp b/include/jsoncons_ext/ubjson/ubjson_parser.hpp new file mode 100644 index 0000000..468f139 --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_parser.hpp @@ -0,0 +1,880 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_PARSER_HPP +#define JSONCONS_UBJSON_UBJSON_PARSER_HPP + +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace ubjson { + +enum class parse_mode {root,accept,array,indefinite_array,strongly_typed_array,map_key,map_value,strongly_typed_map_key,strongly_typed_map_value,indefinite_map_key,indefinite_map_value}; + +struct parse_state +{ + parse_mode mode; + std::size_t length; + uint8_t type; + std::size_t index; + + parse_state(parse_mode mode, std::size_t length, uint8_t type = 0) noexcept + : mode(mode), length(length), type(type), index(0) + { + } + + parse_state(const parse_state&) = default; + parse_state(parse_state&&) = default; +}; + +template > +class basic_ubjson_parser : public ser_context +{ + using char_type = char; + using char_traits_type = std::char_traits; + using temp_allocator_type = Allocator; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + Source source_; + ubjson_decode_options options_; + bool more_; + bool done_; + std::basic_string,char_allocator_type> text_buffer_; + std::vector state_stack_; + int nesting_depth_; +public: + template + basic_ubjson_parser(Sourceable&& source, + const ubjson_decode_options& options = ubjson_decode_options(), + const Allocator alloc = Allocator()) + : source_(std::forward(source)), + options_(options), + more_(true), + done_(false), + text_buffer_(alloc), + state_stack_(alloc), + nesting_depth_(0) + { + state_stack_.emplace_back(parse_mode::root,0); + } + + void restart() + { + more_ = true; + } + + void reset() + { + more_ = true; + done_ = false; + text_buffer_.clear(); + state_stack_.clear(); + state_stack_.emplace_back(parse_mode::root,0,0); + nesting_depth_ = 0; + } + + template + void reset(Sourceable&& source) + { + source_ = std::forward(source); + reset(); + } + + bool done() const + { + return done_; + } + + bool stopped() const + { + return !more_; + } + + std::size_t line() const override + { + return 0; + } + + std::size_t column() const override + { + return source_.position(); + } + + void parse(json_visitor& visitor, std::error_code& ec) + { + while (!done_ && more_) + { + switch (state_stack_.back().mode) + { + case parse_mode::array: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_type_and_value(visitor, ec); + if (ec) + { + return; + } + } + else + { + end_array(visitor, ec); + } + break; + } + case parse_mode::strongly_typed_array: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_value(visitor, state_stack_.back().type, ec); + if (ec) + { + return; + } + } + else + { + end_array(visitor, ec); + } + break; + } + case parse_mode::indefinite_array: + { + auto c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::end_array_marker) + { + source_.ignore(1); + end_array(visitor, ec); + if (ec) + { + return; + } + } + else + { + if (++state_stack_.back().index > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + read_type_and_value(visitor, ec); + if (ec) + { + return; + } + } + break; + } + case parse_mode::map_key: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_key(visitor, ec); + if (ec) + { + return; + } + state_stack_.back().mode = parse_mode::map_value; + } + else + { + end_object(visitor, ec); + } + break; + } + case parse_mode::map_value: + { + state_stack_.back().mode = parse_mode::map_key; + read_type_and_value(visitor, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::strongly_typed_map_key: + { + if (state_stack_.back().index < state_stack_.back().length) + { + ++state_stack_.back().index; + read_key(visitor, ec); + if (ec) + { + return; + } + state_stack_.back().mode = parse_mode::strongly_typed_map_value; + } + else + { + end_object(visitor, ec); + } + break; + } + case parse_mode::strongly_typed_map_value: + { + state_stack_.back().mode = parse_mode::strongly_typed_map_key; + read_value(visitor, state_stack_.back().type, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::indefinite_map_key: + { + auto c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::end_object_marker) + { + source_.ignore(1); + end_object(visitor, ec); + if (ec) + { + return; + } + } + else + { + if (++state_stack_.back().index > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + read_key(visitor, ec); + if (ec) + { + return; + } + state_stack_.back().mode = parse_mode::indefinite_map_value; + } + break; + } + case parse_mode::indefinite_map_value: + { + state_stack_.back().mode = parse_mode::indefinite_map_key; + read_type_and_value(visitor, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::root: + { + state_stack_.back().mode = parse_mode::accept; + read_type_and_value(visitor, ec); + if (ec) + { + return; + } + break; + } + case parse_mode::accept: + { + JSONCONS_ASSERT(state_stack_.size() == 1); + state_stack_.clear(); + more_ = false; + done_ = true; + visitor.flush(); + break; + } + } + } + } +private: + void read_type_and_value(json_visitor& visitor, std::error_code& ec) + { + if (source_.is_error()) + { + ec = ubjson_errc::source_error; + more_ = false; + return; + } + + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + read_value(visitor, b, ec); + } + + void read_value(json_visitor& visitor, uint8_t type, std::error_code& ec) + { + switch (type) + { + case jsoncons::ubjson::ubjson_type::null_type: + { + more_ = visitor.null_value(semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::no_op_type: + { + break; + } + case jsoncons::ubjson::ubjson_type::true_type: + { + more_ = visitor.bool_value(true, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::false_type: + { + more_ = visitor.bool_value(false, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::int8_type: + { + uint8_t buf[sizeof(int8_t)]; + if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + int8_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::uint8_type: + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + more_ = visitor.uint64_value(b, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::int16_type: + { + uint8_t buf[sizeof(int16_t)]; + if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + int16_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::int32_type: + { + uint8_t buf[sizeof(int32_t)]; + if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + int32_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::int64_type: + { + uint8_t buf[sizeof(int64_t)]; + if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + int64_t val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.int64_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::float32_type: + { + uint8_t buf[sizeof(float)]; + if (source_.read(buf, sizeof(float)) != sizeof(float)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + float val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.double_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::float64_type: + { + uint8_t buf[sizeof(double)]; + if (source_.read(buf, sizeof(double)) != sizeof(double)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + double val = binary::big_to_native(buf, sizeof(buf)); + more_ = visitor.double_value(val, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::char_type: + { + text_buffer_.clear(); + if (source_reader::read(source_,text_buffer_,1) != 1) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = ubjson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(text_buffer_, semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::string_type: + { + std::size_t length = get_length(ec); + if (ec) + { + return; + } + text_buffer_.clear(); + if (source_reader::read(source_,text_buffer_,length) != length) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = ubjson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec); + break; + } + case jsoncons::ubjson::ubjson_type::high_precision_number_type: + { + std::size_t length = get_length(ec); + if (ec) + { + return; + } + text_buffer_.clear(); + if (source_reader::read(source_,text_buffer_,length) != length) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (jsoncons::detail::is_base10(text_buffer_.data(),text_buffer_.length())) + { + more_ = visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::bigint, *this, ec); + } + else + { + more_ = visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::bigdec, *this, ec); + } + break; + } + case jsoncons::ubjson::ubjson_type::start_array_marker: + { + begin_array(visitor,ec); + break; + } + case jsoncons::ubjson::ubjson_type::start_object_marker: + { + begin_object(visitor, ec); + break; + } + default: + { + ec = ubjson_errc::unknown_type; + break; + } + } + if (ec) + { + more_ = false; + } + } + + void begin_array(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + + auto c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::type_marker) + { + source_.ignore(1); + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::count_marker) + { + source_.ignore(1); + std::size_t length = get_length(ec); + if (ec) + { + return; + } + if (length > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + state_stack_.emplace_back(parse_mode::strongly_typed_array,length,b); + more_ = visitor.begin_array(length, semantic_tag::none, *this, ec); + } + else + { + ec = ubjson_errc::count_required_after_type; + more_ = false; + return; + } + } + else if (c.value == jsoncons::ubjson::ubjson_type::count_marker) + { + source_.ignore(1); + std::size_t length = get_length(ec); + if (ec) + { + return; + } + if (length > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + state_stack_.emplace_back(parse_mode::array,length); + more_ = visitor.begin_array(length, semantic_tag::none, *this, ec); + } + else + { + state_stack_.emplace_back(parse_mode::indefinite_array,0); + more_ = visitor.begin_array(semantic_tag::none, *this, ec); + } + } + + void end_array(json_visitor& visitor, std::error_code& ec) + { + --nesting_depth_; + + more_ = visitor.end_array(*this, ec); + state_stack_.pop_back(); + } + + void begin_object(json_visitor& visitor, std::error_code& ec) + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = ubjson_errc::max_nesting_depth_exceeded; + more_ = false; + return; + } + + auto c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::type_marker) + { + source_.ignore(1); + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::count_marker) + { + source_.ignore(1); + std::size_t length = get_length(ec); + if (ec) + { + return; + } + if (length > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + state_stack_.emplace_back(parse_mode::strongly_typed_map_key,length,b); + more_ = visitor.begin_object(length, semantic_tag::none, *this, ec); + } + else + { + ec = ubjson_errc::count_required_after_type; + more_ = false; + return; + } + } + else + { + c = source_.peek(); + if (c.eof) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + if (c.value == jsoncons::ubjson::ubjson_type::count_marker) + { + source_.ignore(1); + std::size_t length = get_length(ec); + if (ec) + { + return; + } + if (length > options_.max_items()) + { + ec = ubjson_errc::max_items_exceeded; + more_ = false; + return; + } + state_stack_.emplace_back(parse_mode::map_key,length); + more_ = visitor.begin_object(length, semantic_tag::none, *this, ec); + } + else + { + state_stack_.emplace_back(parse_mode::indefinite_map_key,0); + more_ = visitor.begin_object(semantic_tag::none, *this, ec); + } + } + } + + void end_object(json_visitor& visitor, std::error_code& ec) + { + --nesting_depth_; + more_ = visitor.end_object(*this, ec); + state_stack_.pop_back(); + } + + std::size_t get_length(std::error_code& ec) + { + std::size_t length = 0; + uint8_t type; + if (source_.read(&type, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + switch (type) + { + case jsoncons::ubjson::ubjson_type::int8_type: + { + uint8_t buf[sizeof(int8_t)]; + if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + int8_t val = binary::big_to_native(buf, sizeof(buf)); + if (val >= 0) + { + length = val; + } + else + { + ec = ubjson_errc::length_is_negative; + more_ = false; + return length; + } + break; + } + case jsoncons::ubjson::ubjson_type::uint8_type: + { + uint8_t b; + if (source_.read(&b, 1) == 0) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + length = b; + break; + } + case jsoncons::ubjson::ubjson_type::int16_type: + { + uint8_t buf[sizeof(int16_t)]; + if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + int16_t val = binary::big_to_native(buf, sizeof(buf)); + if (val >= 0) + { + length = val; + } + else + { + ec = ubjson_errc::length_is_negative; + more_ = false; + return length; + } + break; + } + case jsoncons::ubjson::ubjson_type::int32_type: + { + uint8_t buf[sizeof(int32_t)]; + if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + int32_t val = binary::big_to_native(buf, sizeof(buf)); + if (val >= 0) + { + length = static_cast(val); + } + else + { + ec = ubjson_errc::length_is_negative; + more_ = false; + return length; + } + break; + } + case jsoncons::ubjson::ubjson_type::int64_type: + { + uint8_t buf[sizeof(int64_t)]; + if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t)) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return length; + } + int64_t val = binary::big_to_native(buf, sizeof(buf)); + if (val >= 0) + { + length = (std::size_t)val; + if (length != (uint64_t)val) + { + ec = ubjson_errc::number_too_large; + more_ = false; + return length; + } + } + else + { + ec = ubjson_errc::length_is_negative; + more_ = false; + return length; + } + break; + } + default: + { + ec = ubjson_errc::length_must_be_integer; + more_ = false; + return length; + } + } + return length; + } + + void read_key(json_visitor& visitor, std::error_code& ec) + { + std::size_t length = get_length(ec); + if (ec) + { + ec = ubjson_errc::key_expected; + more_ = false; + return; + } + text_buffer_.clear(); + if (source_reader::read(source_,text_buffer_,length) != length) + { + ec = ubjson_errc::unexpected_eof; + more_ = false; + return; + } + + auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size()); + if (result.ec != unicode_traits::conv_errc()) + { + ec = ubjson_errc::invalid_utf8_text_string; + more_ = false; + return; + } + more_ = visitor.key(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), *this, ec); + } +}; + +}} + +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_reader.hpp b/include/jsoncons_ext/ubjson/ubjson_reader.hpp new file mode 100644 index 0000000..210403a --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_reader.hpp @@ -0,0 +1,92 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_READER_HPP +#define JSONCONS_UBJSON_UBJSON_READER_HPP + +#include +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace ubjson { + +template > +class basic_ubjson_reader +{ + basic_ubjson_parser parser_; + json_visitor& visitor_; +public: + template + basic_ubjson_reader(Sourceable&& source, + json_visitor& visitor, + const Allocator alloc) + : basic_ubjson_reader(std::forward(source), + visitor, + ubjson_decode_options(), + alloc) + { + } + + template + basic_ubjson_reader(Sourceable&& source, + json_visitor& visitor, + const ubjson_decode_options& options = ubjson_decode_options(), + const Allocator alloc=Allocator()) + : parser_(std::forward(source), options, alloc), + visitor_(visitor) + { + } + + void read() + { + std::error_code ec; + read(ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec,line(),column())); + } + } + + void read(std::error_code& ec) + { + parser_.reset(); + parser_.parse(visitor_, ec); + if (ec) + { + return; + } + } + + std::size_t line() const + { + return parser_.line(); + } + + std::size_t column() const + { + return parser_.column(); + } +}; + +using ubjson_stream_reader = basic_ubjson_reader; + +using ubjson_bytes_reader = basic_ubjson_reader; + +#if !defined(JSONCONS_NO_DEPRECATED) +JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_reader") typedef ubjson_stream_reader ubjson_reader; +JSONCONS_DEPRECATED_MSG("Instead, use ubjson_bytes_reader") typedef ubjson_bytes_reader ubjson_buffer_reader; +#endif + +}} + +#endif diff --git a/include/jsoncons_ext/ubjson/ubjson_type.hpp b/include/jsoncons_ext/ubjson/ubjson_type.hpp new file mode 100644 index 0000000..ef219ce --- /dev/null +++ b/include/jsoncons_ext/ubjson/ubjson_type.hpp @@ -0,0 +1,43 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UBJSON_UBJSON_TYPE_HPP +#define JSONCONS_UBJSON_UBJSON_TYPE_HPP + +#include +#include +#include + +namespace jsoncons { namespace ubjson { + + namespace ubjson_type + { + const uint8_t null_type = 'Z'; + const uint8_t no_op_type = 'N'; + const uint8_t true_type = 'T'; + const uint8_t false_type = 'F'; + const uint8_t int8_type = 'i'; + const uint8_t uint8_type = 'U'; + const uint8_t int16_type = 'I'; + const uint8_t int32_type = 'l'; + const uint8_t int64_type = 'L'; + const uint8_t float32_type = 'd'; + const uint8_t float64_type = 'D'; + const uint8_t high_precision_number_type = 'H'; + const uint8_t char_type = 'C'; + const uint8_t string_type = 'S'; + const uint8_t start_array_marker = '['; + const uint8_t end_array_marker = ']'; + const uint8_t start_object_marker = '{'; + const uint8_t end_object_marker = '}'; + const uint8_t type_marker = '$'; + const uint8_t count_marker = '#'; + } + +} // namespace ubjson +} // namespace jsoncons + +#endif diff --git a/src/opcodes.cpp b/src/opcodes.cpp new file mode 100644 index 0000000..6991051 --- /dev/null +++ b/src/opcodes.cpp @@ -0,0 +1,976 @@ +/* + opcodes.cpp + Copyright (C) 2022 Richard Knight + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "handling.h" + +#define ARGT static constexpr char const + +struct JSONSession { + jsoncons::json data; +}; + +const char* badHandle = "cannot obtain data from handle"; +const char* handleName = "jsonsession"; + + +/* + Initialise an array and return STRINDAT pointer in case it is required + */ +STRINGDAT* arrayInit(csnd::Csound* csound, ARRAYDAT* array, int rows, int cols) { + int totalResults = rows * cols; + size_t totalAllocated; + + //if (array->data == NULL) { + array->sizes = (int32_t*) csound->calloc(sizeof(int32_t) * 2); + array->sizes[0] = rows; + if (cols != 1) { + array->sizes[1] = cols; + } + array->dimensions = cols; + CS_VARIABLE *var = array->arrayType->createVariable(csound->get_csound(), NULL); + array->arrayMemberSize = var->memBlockSize; + totalAllocated = array->arrayMemberSize * totalResults; + array->data = (MYFLT*) csound->calloc(totalAllocated); + // } else + if ((totalAllocated = array->arrayMemberSize * totalResults) > array->allocated) { + array->data = (MYFLT*) csound->realloc(array->data, totalAllocated); + memset((char*)(array->data)+array->allocated, '\0', totalAllocated - array->allocated); + array->allocated = totalAllocated; + } + + // convenience return to be used if it is a string array + return (STRINGDAT*) array->data; +} + + +/* + Insert a string to an array + */ +void insertArrayStringItem(csnd::Csound* csound, STRINGDAT* strings, int index, char* item) { + strings[index].size = strlen(item) + 1; + if (strings[index].data != NULL) { + csound->free(strings[index].data); + } + strings[index].data = csound->strdup(item); +} + + +/* + Convert JSON array to Csound array either as string or numeric + */ +void jsonArrayToCSArray(csnd::Csound* csound, jsoncons::json* jdatap, ARRAYDAT* array, bool asString) { + jsoncons::json jdata = *jdatap; + std::vector vals = jdata.as>(); + + STRINGDAT* strings = arrayInit(csound, array, vals.size(), 1); + + char* value; + for (std::size_t index = 0; index < vals.size(); index++) { + value = (char*) vals[index].c_str(); + if (asString) { + insertArrayStringItem(csound, strings, index, value); + } else { + array->data[index] = (MYFLT) atof(value); + } + } +} + + +/* + Get the JSON type of a session object + */ +int getJsonType(JSONSession* jsonSession) { + jsoncons::json j = jsonSession->data; + int outtype = -1; + if (j.is_null()) { + outtype = 0; + } else if (j.is_string()) { + outtype = 1; + } else if (j.is_number()) { + outtype = 2; + } else if (j.is_bool()) { + outtype = 3; + } else if (j.is_array()) { + outtype = 4; + } else if (j.is_object()) { + outtype = 5; + } + return outtype; +} + +// cs AppendOpcode mallocs struct so virtual functions cannot be used. +// Macro workaround to fake struct derivation type model, just chuck it all in a macro... + +#define _PLUGINSESSIONBASE(idInArgs)\ + JSONSession* jsonSession;\ + void getSession() {\ + if (!(jsonSession = getHandle(csound, idInArgs[0], handleName))) {\ + throw std::runtime_error(badHandle);\ + }\ + }\ + void getSession(MYFLT handle, JSONSession** returnSession) {\ + if (!(*returnSession = getHandle(csound, handle, handleName))) {\ + throw std::runtime_error(badHandle);\ + }\ + } + +#define _PLUGINITBASE(votypes, vitypes, doGetSession) \ + ARGT* otypes = votypes;\ + ARGT* itypes = vitypes;\ + int init() {\ + try {\ + if (doGetSession) getSession();\ + irun();\ + } catch (const std::exception &ex) {\ + return csound->init_error(ex.what());\ + }\ + return OK;\ + } + +// extension of above for k-rate opcodes +#define _PLUGINITBASEK(votypes, vitypes, doGetSession) \ + _PLUGINITBASE(votypes, vitypes, doGetSession)\ + void irun() {}\ + int kperf() {\ + try {\ + krun();\ + } catch (const std::exception &ex) {\ + return csound->perf_error(ex.what(), this);\ + }\ + return OK;\ + } + +#define PLUGINSESSION \ + _PLUGINSESSIONBASE(inargs) + +#define INPLUGSESSION \ + _PLUGINSESSIONBASE(args) + +#define PLUGINIT(votypes, vitypes, doGetSession) \ + _PLUGINITBASE(votypes, vitypes, doGetSession) + +#define INPLUGINIT(vitypes) \ + _PLUGINITBASE("", vitypes, true) + +#define PLUGINITK(votypes, vitypes, doGetSession) \ + _PLUGINITBASEK(votypes, vitypes, doGetSession) + +#define INPLUGINITK(vitypes) \ + _PLUGINITBASEK("", vitypes, true) + +#define INPLUGCHILD(vitypes) \ + INPLUGINIT(vitypes)\ + void irun() { run(); } + +#define INPLUGCHILDK(vitypes) \ + INPLUGINITK(vitypes)\ + void krun() { run(); } + +#define PLUGINCHILD(votypes, vitypes, doGetSession) \ + PLUGINIT(votypes, vitypes, doGetSession)\ + void irun() { run(); } + +#define PLUGINCHILDK(votypes, vitypes, doGetSession) \ + PLUGINITK(votypes, vitypes, doGetSession)\ + void krun() { run(); } + + + +template +struct inplug : csnd::InPlug { + using csnd::InPlug::args; + using csnd::InPlug::csound; + INPLUGSESSION +}; + +template +struct plugin : csnd::Plugin { + using csnd::Plugin::inargs; + using csnd::Plugin::outargs; + using csnd::Plugin::csound; + PLUGINSESSION +}; + + +/* + Parse JSON object from a provided string + */ +struct jsonloads : plugin<1, 1> { + PLUGINIT("i", "S", false) + void irun() { + outargs[0] = createHandle(csound, &jsonSession, handleName); + jsonSession->data = jsoncons::json::parse(std::string(inargs.str_data(0).data)); + } +}; + + +/* + Initialise an empty JSON object + */ +struct jsoninit : plugin<1, 0> { + PLUGINIT("i", "", false) + void irun() { + outargs[0] = createHandle(csound, &jsonSession, handleName); + jsonSession->data = jsoncons::json::parse("{}"); + } +}; + + +/* + Merge two JSON objects + */ +struct jsonmerge : inplug<3> { + INPLUGINIT("iio") + int irun() { + JSONSession* jsonSession2; + getSession(args[1], &jsonSession2); + if (args[2] == 1) { + jsonSession->data.merge_or_update(jsonSession2->data); + } else { + jsonSession->data.merge(jsonSession2->data); + } + + } +}; + + +/* + Insert a JSON object to another JSON object with specified key + */ +struct jsoninsert : inplug<3> { + INPLUGINIT("iSi") + void irun() { + JSONSession* jsonSession2; + getSession(args[2], &jsonSession2); + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + jsonSession2->data + ); + } +}; + + +/* +Insert an array of JSON objects to another JSON object with specified key +*/ +struct jsoninsertArray : inplug<3> { + INPLUGINIT("iSi[]") + void irun() { + JSONSession* jsonSession2; + ARRAYDAT* values = (ARRAYDAT*) args(2); + std::vector valuesVector; + + for (int i = 0; i < values->sizes[0]; i++) { + getSession(values->data[i], &jsonSession2); + valuesVector.push_back(jsonSession2->data); + } + + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + valuesVector + ); + } +}; + + +/* + Insert a string value to a JSON object with specified key + */ +struct jsoninsertvalStringBase : inplug<3> { + void run() { + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + std::string(args.str_data(2).data) + ); + } +}; +struct jsoninsertvalString : jsoninsertvalStringBase { + INPLUGCHILD("iSS") +}; +struct jsoninsertvalStringK : jsoninsertvalStringBase { + INPLUGCHILDK("iSS") +}; + + +/* + Insert a numeric value to a JSON object with specified key + */ +struct jsoninsertvalNumericBase : inplug<3> { + void run() { + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + args[2] + ); + } +}; +struct jsoninsertvalNumeric : jsoninsertvalNumericBase { + INPLUGCHILD("iSi") +}; +struct jsoninsertvalNumericK : jsoninsertvalNumericBase { + INPLUGCHILDK("iSk") +}; + + + +/* + Insert a numeric array to a JSON object with specified key + */ +struct jsoninsertvalNumericArrayBase : inplug<3> { + void run() { + ARRAYDAT* values = (ARRAYDAT*) args(2); + std::vector valuesVector(values->data, values->data + values->sizes[0]); + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + valuesVector + ); + } +}; +struct jsoninsertvalNumericArray : jsoninsertvalNumericArrayBase { + INPLUGCHILD("iSi[]") +}; +struct jsoninsertvalNumericArrayK : jsoninsertvalNumericArrayBase { + INPLUGCHILDK("iSk[]") +}; + + + +/* + Insert a string array to a JSON object with specified key + */ +struct jsoninsertvalStringArrayBase : inplug<3> { + void run() { + ARRAYDAT* values = (ARRAYDAT*) args(2); + STRINGDAT* strings = (STRINGDAT*) values->data; + std::vector valuesVector; + for (int i = 0; i < values->sizes[0]; i++) { + valuesVector.push_back(std::string(strings[i].data)); + } + jsonSession->data.insert_or_assign( + std::string(args.str_data(1).data), + valuesVector + ); + } +}; +struct jsoninsertvalStringArray : jsoninsertvalStringArrayBase { + INPLUGCHILD("iSS[]") +}; +struct jsoninsertvalStringArrayK : jsoninsertvalStringArrayBase { + INPLUGCHILDK("iSS[]") +}; + + +/* + Insert string key, string value pairs to a JSON object with specified key + */ +struct jsoninsertvalStringStringArrayBase : inplug<3> { + void run() { + ARRAYDAT* rawKeys = (ARRAYDAT*) args(1); + STRINGDAT* keys = (STRINGDAT*) rawKeys->data; + ARRAYDAT* rawValues = (ARRAYDAT*) args(2); + STRINGDAT* values = (STRINGDAT*) rawValues->data; + if (rawKeys->sizes[0] != rawValues->sizes[0]) { + throw std::runtime_error("key and value arrays are not the same size"); + } + for (int i = 0; i < rawKeys->sizes[0]; i++) { + jsonSession->data.insert_or_assign( + std::string(keys[i].data), + std::string(values[i].data) + ); + } + } +}; +struct jsoninsertvalStringStringArray : jsoninsertvalStringStringArrayBase { + INPLUGCHILD("iS[]S[]") +}; +struct jsoninsertvalStringStringArrayK : jsoninsertvalStringStringArrayBase { + INPLUGCHILDK("iS[]S[]") +}; + + +/* + Insert string key, numeric value pairs to a JSON object with specified key + */ +struct jsoninsertvalStringNumericArrayBase : inplug<3> { + void run() { + ARRAYDAT* rawKeys = (ARRAYDAT*) args(1); + STRINGDAT* keys = (STRINGDAT*) rawKeys->data; + ARRAYDAT* rawValues = (ARRAYDAT*) args(2); + if (rawKeys->sizes[0] != rawValues->sizes[0]) { + throw std::runtime_error("key and value arrays are not the same size"); + } + for (int i = 0; i < rawKeys->sizes[0]; i++) { + jsonSession->data.insert_or_assign( + std::string(keys[i].data), + rawValues->data[i] // not like doubles? + ); + } + } +}; +struct jsoninsertvalStringNumericArray : jsoninsertvalStringNumericArrayBase { + INPLUGCHILD("iS[]i[]") +}; +struct jsoninsertvalStringNumericArrayK : jsoninsertvalStringNumericArrayBase { + INPLUGCHILDK("iS[]k[]") +}; + + +/* + Get JSON type as number + */ +struct jsontype : plugin<1, 1> { + PLUGINIT("i", "i", true) + void irun() { + outargs[0] = (MYFLT) getJsonType(jsonSession); + } +}; + +/* + Get JSON type as string + */ +struct jsontypeString : plugin<1, 1> { + PLUGINIT("S", "i", true) + void irun() { + STRINGDAT &sdoutput = outargs.str_data(0); + int type = getJsonType(jsonSession); + std::string output; + switch (type) { + case -1: + output.assign("unknown"); + break; + case 0: + output.assign("null"); + break; + case 1: + output.assign("string"); + break; + case 2: + output.assign("number"); + break; + case 3: + output.assign("boolean"); + break; + case 4: + output.assign("array"); + break; + case 5: + output.assign("object"); + break; + } + sdoutput.size = output.length(); + sdoutput.data = csound->strdup((char*) output.c_str()); + } +}; + +/* + Get the key names from an object + */ +struct jsonkeysBase : plugin<1, 1> { + void run() { + std::map map = + jsonSession->data.as>(); + ARRAYDAT* array = (ARRAYDAT*) outargs(0); + STRINGDAT* strings = arrayInit(csound, array, map.size(), 1); + + char* value; + int index = 0; + for (auto const& x : map) { + value = (char*) x.first.c_str(); + insertArrayStringItem(csound, strings, index, value); + index ++; + } + } +}; +struct jsonkeys : jsonkeysBase { + PLUGINCHILD("S[]", "i", true) +}; +struct jsonkeysK : jsonkeysBase { + PLUGINCHILDK("S[]", "i", true) +}; + + +/* + Get the size of a JSON object + */ +struct jsonsizeBase : plugin<1, 1> { + void run() { + outargs[0] = (MYFLT) jsonSession->data.size(); + } +}; +struct jsonsize : jsonsizeBase { + PLUGINCHILD("i", "i", true) +}; +struct jsonsizeK : jsonsizeBase { + PLUGINCHILDK("k", "i", true) +}; + + +/* + Get object from an object by key name + */ +struct jsongetvalString : plugin<1, 2> { + PLUGINIT("i", "iS", true) + void irun() { + STRINGDAT &input = inargs.str_data(1); + jsoncons::json selected = jsonSession->data[std::string(input.data)]; + JSONSession* jsonSessionOutput; + outargs[0] = createHandle(csound, &jsonSessionOutput, handleName); + jsonSessionOutput->data = selected; + } +}; + + +/* + Get object from an array by index + */ +struct jsongetvalNumeric : plugin<1, 2> { + PLUGINIT("i", "ii", true) + void irun() { + jsoncons::json selected = jsonSession->data[(int) inargs[1]]; + JSONSession* jsonSessionOutput; + outargs[0] = createHandle(csound, &jsonSessionOutput, handleName); + jsonSessionOutput->data = selected; + } +}; + + +/* + Query by JSONPath + */ +struct jsonpath : plugin<1, 2> { + PLUGINIT("i", "iS", true) + void irun() { + jsoncons::json queried = jsoncons::jsonpath::json_query( + jsonSession->data, std::string(inargs.str_data(1).data) + ); + JSONSession* jsonSessionOutput; + outargs[0] = createHandle(csound, &jsonSessionOutput, handleName); + jsonSessionOutput->data = queried; + } +}; + + +/* + Replace string value by JSONPath + */ +struct jsonpathrplvalStringBase : inplug<3> { + void run() { + jsoncons::jsonpath::json_replace( + jsonSession->data, + std::string(args.str_data(1).data), + std::string(args.str_data(2).data) + ); + } +}; +struct jsonpathrplvalString : jsonpathrplvalStringBase { + INPLUGCHILD("iSS") +}; +struct jsonpathrplvalStringK : jsonpathrplvalStringBase { + INPLUGCHILDK("iSS") +}; + + +/* + Replace numeric value by JSONPath + */ +struct jsonpathrplvalNumericBase : inplug<3> { + void run() { + jsoncons::jsonpath::json_replace( + jsonSession->data, + std::string(args.str_data(1).data), + (float) args[2] // doesn't like double ?? + ); + } +}; +struct jsonpathrplvalNumeric : jsonpathrplvalNumericBase { + INPLUGCHILD("iSi") +}; +struct jsonpathrplvalNumericK : jsonpathrplvalNumericBase { + INPLUGCHILDK("iSi") +}; + + +/* + Replace element with object by JSONPath + */ +struct jsonpathrpl : inplug<3> { + INPLUGINIT("iSi") + void irun() { + JSONSession* jsonSession2; + getSession(args[2], &jsonSession2); + jsoncons::jsonpath::json_replace( + jsonSession->data, + std::string(args.str_data(1).data), + std::string("DAMN") //jsonSession2->data + ); + } +}; + + +/* + Query by JSON Pointer + */ +struct jsonptr : plugin<1, 2> { + PLUGINIT("i", "iS", true) + void irun() { + jsoncons::json queried = jsoncons::jsonpointer::get( + jsonSession->data, std::string(inargs.str_data(1).data) + ); + JSONSession* jsonSessionOutput; + outargs[0] = createHandle(csound, &jsonSessionOutput, handleName); + jsonSessionOutput->data = queried; + } +}; + +/* + Check for existence by JSON Pointer + */ +struct jsonptrhasBase : plugin<1, 2> { + void run() { + outargs[0] = (int) jsoncons::jsonpointer::contains( + jsonSession->data, std::string(inargs.str_data(1).data) + ); + } +}; +struct jsonptrhas : jsonptrhasBase { + PLUGINCHILD("i", "iS", true) +}; +struct jsonptrhasK : jsonptrhasBase { + PLUGINCHILDK("k", "iS", true) +}; + + +/* + Add string value by JSON Pointer + */ +struct jsonptraddvalStringBase : inplug<3> { + void run() { + jsoncons::jsonpointer::add( + jsonSession->data, + std::string(args.str_data(1).data), + std::string(args.str_data(2).data), + true // create if not exists + ); + } +}; +struct jsonptraddvalString : jsonptraddvalStringBase { + INPLUGCHILD("iSS") +}; +struct jsonptraddvalStringK : jsonptraddvalStringBase { + INPLUGCHILDK("iSS") +}; + + +/* + Add numeric value by JSON Pointer + */ +struct jsonptraddvalNumericBase : inplug<3> { + void run() { + jsoncons::jsonpointer::add( + jsonSession->data, + std::string(args.str_data(1).data), + args[2], + true // create if not exists + ); + } +}; +struct jsonptraddvalNumeric : jsonptraddvalNumericBase { + INPLUGCHILD("iSi") +}; +struct jsonptraddvalNumericK : jsonptraddvalNumericBase { + INPLUGCHILDK("iSk") +}; + + +/* + Add object by JSON Pointer + */ +struct jsonptradd : inplug<3> { + INPLUGINIT("iSi") + void irun() { + JSONSession* jsonSession2; + getSession(args[1], &jsonSession2); + + jsoncons::jsonpointer::add( + jsonSession->data, + std::string(args.str_data(1).data), + jsonSession2->data, + true // create if not exists + ); + } +}; + + +/* + Remove by JSON Pointer + */ +struct jsonptrrmBase : inplug<2> { + void run() { + char* query = args.str_data(1).data; + jsoncons::jsonpointer::remove(jsonSession->data, std::string(query)); + } +}; +struct jsonptrrm : jsonptrrmBase { + INPLUGCHILD("iS") +}; +struct jsonptrrmK : jsonptrrmBase { + INPLUGCHILDK("iS") +}; + + +/* + Replace string value by JSON Pointer + */ +struct jsonptrrplvalStringBase : inplug<3> { + void run() { + jsoncons::jsonpointer::replace( + jsonSession->data, + std::string(args.str_data(1).data), + std::string(args.str_data(2).data), + true // create if missing + ); + } +}; +struct jsonptrrplvalString : jsonptrrplvalStringBase { + INPLUGCHILD("iSS") +}; +struct jsonptrrplvalStringK : jsonptrrplvalStringBase { + INPLUGCHILDK("iSS") +}; + + +/* + Replace numeric value by JSON Pointer + */ +struct jsonptrrplvalNumericBase : inplug<3> { + void run() { + jsoncons::jsonpointer::replace( + jsonSession->data, + std::string(args.str_data(1).data), + args[2], + true // create if missing + ); + } +}; +struct jsonptrrplvalNumeric : jsonptrrplvalNumericBase { + INPLUGCHILD("iSi") +}; +struct jsonptrrplvalNumericK : jsonptrrplvalNumericBase { + INPLUGCHILDK("iSk") +}; + + +/* + Replace object by JSON Pointer + */ +struct jsonptrrpl : csnd::InPlug<3> { + INPLUGSESSION + INPLUGINIT("iSi") + void irun() { + JSONSession* jsonSession2; + getSession(args[2], &jsonSession2); + + jsoncons::jsonpointer::replace( + jsonSession->data, + std::string(args.str_data(1).data), + jsonSession2->data, + true // create if missing + ); + } +}; + + +/* + Get numeric array from object + */ +struct jsonarrvalNumericBase : plugin<1, 1> { + void run() { + jsonArrayToCSArray(csound, &(jsonSession->data), (ARRAYDAT*) outargs(0), false); + } +}; +struct jsonarrvalNumeric : jsonarrvalNumericBase { + PLUGINCHILD("i[]", "i", true) +}; +struct jsonarrvalNumericK : jsonarrvalNumericBase { + PLUGINCHILDK("k[]", "i", true) +}; + + +/* + Get string array from object + */ +struct jsonarrvalStringBase : plugin<1, 1> { + void run() { + jsonArrayToCSArray(csound, &(jsonSession->data), (ARRAYDAT*) outargs(0), true); + } +}; +struct jsonarrvalString : jsonarrvalStringBase { + PLUGINCHILD("S[]", "i", true) +}; +struct jsonarrvalStringK : jsonarrvalStringBase { + PLUGINCHILDK("S[]", "i", true) +}; + + +/* + Get array of object handles from object + */ +struct jsonarr : plugin<1, 1> { + PLUGINIT("i[]", "i", true) + void irun() { + JSONSession* jsonSession2; + std::vector vals = + jsonSession->data.as>(); + ARRAYDAT* array = (ARRAYDAT*) outargs(0); + arrayInit(csound, array, vals.size(), 1); + MYFLT handle; + for (std::size_t index = 0; index < vals.size(); index++) { + handle = createHandle(csound, &jsonSession2, handleName); + jsonSession2->data = vals[index]; + array->data[index] = handle; + } + } +}; + + +/* + Dump to string + */ +struct jsondumpsBase : plugin<1, 2> { + void run() { + STRINGDAT &output = outargs.str_data(0); + std::ostringstream stream; + if (inargs[1] == FL(1)) { + stream << jsoncons::pretty_print(jsonSession->data); + } else { + stream << jsonSession->data; + } + char* text = csound->strdup((char*) stream.str().c_str()); + output.size = strlen(text); + output.data = text; + } +}; +struct jsondumps : jsondumpsBase { + PLUGINCHILD("S", "ip", true) +}; +struct jsondumpsK : jsondumpsBase { + PLUGINCHILDK("S", "ip", true) +}; + + +struct jsonload : csnd::Plugin<1, 1> { + PLUGINSESSION + PLUGINIT("i", "S", false) + void irun() { + std::ifstream fileStream(inargs.str_data(0).data); + jsoncons::json parsed = jsoncons::json::parse(fileStream); + outargs[0] = createHandle(csound, &jsonSession, handleName); + jsonSession->data = parsed; + } +}; + + +struct jsondump : csnd::InPlug<3> { + INPLUGSESSION + INPLUGINIT("iSp") + void irun() { + std::ofstream fileStream; + fileStream.open(args.str_data(1).data, std::ios::in | std::ios::trunc); + if (!fileStream.is_open()) { + throw std::runtime_error("could not open file for writing"); + } + if (args[2] == FL(1)) { + fileStream << jsoncons::pretty_print(jsonSession->data); + } else { + fileStream << jsonSession->data; + } + fileStream.close(); + } +}; + +#include +void csnd::on_load(csnd::Csound *csound) { + csnd::plugin(csound, "jsoninit", csnd::thread::i); + csnd::plugin(csound, "jsonloads", csnd::thread::i); + csnd::plugin(csound, "jsondumps", csnd::thread::i); + csnd::plugin(csound, "jsondumpsk", csnd::thread::ik); + csnd::plugin(csound, "jsonload", csnd::thread::i); + csnd::plugin(csound, "jsondump", csnd::thread::i); + csnd::plugin(csound, "jsonmerge", csnd::thread::i); + csnd::plugin(csound, "jsontype", csnd::thread::i); + csnd::plugin(csound, "jsontype.S", csnd::thread::i); + csnd::plugin(csound, "jsonkeys", csnd::thread::i); + csnd::plugin(csound, "jsonkeysk", csnd::thread::ik); + csnd::plugin(csound, "jsongetval.S", csnd::thread::i); + csnd::plugin(csound, "jsongetval.i", csnd::thread::i); + csnd::plugin(csound, "jsonsize", csnd::thread::i); + csnd::plugin(csound, "jsonsizek", csnd::thread::ik); + + csnd::plugin(csound, "jsoninsert", csnd::thread::i); + csnd::plugin(csound, "jsoninsert.a", csnd::thread::i); + csnd::plugin(csound, "jsoninsertval.S", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk.S", csnd::thread::ik); + csnd::plugin(csound, "jsoninsertval.i", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk", csnd::thread::ik); + csnd::plugin(csound, "jsoninsertval.Sa", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk.Sa", csnd::thread::ik); + csnd::plugin(csound, "jsoninsertval.ia", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk.a", csnd::thread::ik); + csnd::plugin(csound, "jsoninsertval.SaSa", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk.SaSa", csnd::thread::ik); + csnd::plugin(csound, "jsoninsertval.Saia", csnd::thread::i); + csnd::plugin(csound, "jsoninsertvalk.Saka", csnd::thread::ik); + + csnd::plugin(csound, "jsonpath", csnd::thread::i); + csnd::plugin(csound, "jsonpathrplval.S", csnd::thread::i); + csnd::plugin(csound, "jsonpathrplvalk.S", csnd::thread::ik); + csnd::plugin(csound, "jsonpathrplval.i", csnd::thread::i); + csnd::plugin(csound, "jsonpathrplvalk.i", csnd::thread::ik); + csnd::plugin(csound, "jsonpathrpl", csnd::thread::i); + + csnd::plugin(csound, "jsonptr", csnd::thread::i); + csnd::plugin(csound, "jsonptrhas", csnd::thread::i); + csnd::plugin(csound, "jsonptrhask", csnd::thread::ik); + csnd::plugin(csound, "jsonptraddval.S", csnd::thread::i); + csnd::plugin(csound, "jsonptraddvalk.S", csnd::thread::ik); + csnd::plugin(csound, "jsonptraddval.i", csnd::thread::i); + csnd::plugin(csound, "jsonptraddvalk.i", csnd::thread::ik); + csnd::plugin(csound, "jsonptradd", csnd::thread::i); + csnd::plugin(csound, "jsonptrrm", csnd::thread::i); + csnd::plugin(csound, "jsonptrrmk", csnd::thread::ik); + csnd::plugin(csound, "jsonptrrplval.S", csnd::thread::i); + csnd::plugin(csound, "jsonptrrplvalk.S", csnd::thread::ik); + csnd::plugin(csound, "jsonptrrplval.i", csnd::thread::i); + csnd::plugin(csound, "jsonptrrplvalk.i", csnd::thread::ik); + csnd::plugin(csound, "jsonptrrpl", csnd::thread::i); + + csnd::plugin(csound, "jsonarrval.S", csnd::thread::i); + csnd::plugin(csound, "jsonarrvalk.S", csnd::thread::ik); + csnd::plugin(csound, "jsonarrval.i", csnd::thread::i); + csnd::plugin(csound, "jsonarrval.k", csnd::thread::ik); + csnd::plugin(csound, "jsonarr", csnd::thread::i); +} -- cgit v1.2.3