mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 06:00:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
438530a538 | ||
|
|
168aa3723c | ||
|
|
4289a38275 | ||
|
|
73b8a6a9c8 | ||
|
|
9df8de47a5 | ||
|
|
57371b0977 | ||
|
|
2c65caa5bf | ||
|
|
38528b4a38 | ||
|
|
9cbe62efb0 | ||
|
|
471c7678cf | ||
|
|
4fabfb27f7 | ||
|
|
71b4eaf743 | ||
|
|
2790175d79 | ||
|
|
cc9fafa0e1 | ||
|
|
8d6d1264a7 | ||
|
|
d75f9ab161 | ||
|
|
c3774be4e2 | ||
|
|
91365ca3b2 | ||
|
|
605cc6c81d | ||
|
|
f012b85d06 | ||
|
|
243b6e20e3 | ||
|
|
0a0d5623be | ||
|
|
22c9ce3f21 | ||
|
|
0d2240ad1e |
@@ -31,6 +31,48 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_test_gcc
|
run: cmake --build build --target ci_test_gcc
|
||||||
|
|
||||||
|
ci_meson_install:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
|
- name: Check that Meson and pkg-config offer the CMake options
|
||||||
|
run: make check_build_options
|
||||||
|
- name: Install Meson
|
||||||
|
run: pip install meson
|
||||||
|
- name: Install with Meson
|
||||||
|
run: |
|
||||||
|
meson setup build-meson --prefix=${{ github.workspace }}/install
|
||||||
|
meson install -C build-meson
|
||||||
|
- name: Use the installed package with find_package
|
||||||
|
run: |
|
||||||
|
cmake -S tests/cmake_import/project -B build-import -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install
|
||||||
|
cmake --build build-import
|
||||||
|
- name: Install with Meson and non-default options
|
||||||
|
run: |
|
||||||
|
meson setup build-meson-options --prefix=${{ github.workspace }}/install-options -DMultipleHeaders=true -DDiagnostics=true -DGlobalUDLs=false -DDisableTupleReferenceConversion=true
|
||||||
|
meson install -C build-meson-options
|
||||||
|
- name: Check that the options reach the installed files
|
||||||
|
run: |
|
||||||
|
test -d install-options/include/nlohmann/detail
|
||||||
|
cflags=$(PKG_CONFIG_PATH=${{ github.workspace }}/install-options/share/pkgconfig pkg-config --cflags nlohmann_json)
|
||||||
|
echo "$cflags"
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_DIAGNOSTICS=1'
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_USE_GLOBAL_UDLS=0'
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
|
||||||
|
grep -q 'JSON_USE_GLOBAL_UDLS=0;JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1;JSON_DIAGNOSTICS=1' install-options/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake
|
||||||
|
cmake -S tests/cmake_import/project -B build-import-options -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-options
|
||||||
|
cmake --build build-import-options
|
||||||
|
- name: Install with Meson and the include directory outside the prefix
|
||||||
|
run: |
|
||||||
|
meson setup build-meson-split --prefix=${{ github.workspace }}/install-split --includedir=${{ github.workspace }}/install-split-dev/include
|
||||||
|
meson install -C build-meson-split
|
||||||
|
cmake -S tests/cmake_import/project -B build-import-split -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-split
|
||||||
|
cmake --build build-import-split
|
||||||
|
|
||||||
ci_infer:
|
ci_infer:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+27
-1
@@ -180,7 +180,33 @@ if (MSVC)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install a pkg-config file, so other tools can find this.
|
# Install a pkg-config file, so other tools can find this. It carries the same
|
||||||
|
# compile definitions as the target above.
|
||||||
|
set(NLOHMANN_JSON_PKGCONFIG_CFLAGS "")
|
||||||
|
if (NOT JSON_GlobalUDLs)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_GLOBAL_UDLS=0")
|
||||||
|
endif()
|
||||||
|
if (NOT JSON_ImplicitConversions)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_IMPLICIT_CONVERSIONS=0")
|
||||||
|
endif()
|
||||||
|
if (JSON_DisableEnumSerialization)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_ENUM_SERIALIZATION=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_DisableTupleReferenceConversion)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_Diagnostics)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTICS=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_Diagnostic_Positions)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTIC_POSITIONS=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_LegacyDiscardedValueComparison)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_StrictNulHandling)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_STRICT_NUL_HANDLING=1")
|
||||||
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
||||||
|
|||||||
@@ -249,9 +249,31 @@ make BUILD.bazel
|
|||||||
|
|
||||||
The "Check amalgamation" workflow fails if the file is out of date.
|
The "Check amalgamation" workflow fails if the file is out of date.
|
||||||
|
|
||||||
### `meson.build`
|
### `meson.build` and `meson_options.txt`
|
||||||
|
|
||||||
The build definition for the [Meson](https://mesonbuild.com) build system.
|
Meson build definitions suitable for use as a subproject ("wrap" in Meson terminology).
|
||||||
|
|
||||||
|
Projects wishing to use the wrap can execute:
|
||||||
|
```sh
|
||||||
|
meson wrap install nlohmann_json
|
||||||
|
```
|
||||||
|
|
||||||
|
Which allows Meson to build from source when a system provided dependency isn't available.
|
||||||
|
|
||||||
|
To build directly:
|
||||||
|
```sh
|
||||||
|
meson setup builddir
|
||||||
|
ninja -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
|
||||||
|
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
|
||||||
|
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
|
||||||
|
stay in sync with the CMake options.
|
||||||
|
|
||||||
|
When installing, `meson.build` installs the headers, a pkg-config file, and the CMake package config files, so that
|
||||||
|
`find_package(nlohmann_json)` works. As Meson cannot generate `nlohmann_jsonTargets.cmake` itself, it is created from
|
||||||
|
the template `cmake/nlohmann_jsonTargets.cmake.in`, which is only used by Meson.
|
||||||
|
|
||||||
### `Package.swift`
|
### `Package.swift`
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# configuration
|
# configuration
|
||||||
@@ -124,6 +124,10 @@ macro_builder_check:
|
|||||||
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
||||||
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
||||||
|
|
||||||
|
# check that the Meson build and the pkg-config files offer the options of the CMake target
|
||||||
|
check_build_options:
|
||||||
|
python3 tools/check_build_options/check_build_options.py .
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
@@ -181,7 +185,7 @@ json.tar.xz:
|
|||||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||||
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
||||||
include.zip: BUILD.bazel
|
include.zip: BUILD.bazel
|
||||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build meson_options.txt LICENSE.MIT
|
||||||
|
|
||||||
# Create the files for a release and add signatures and hashes.
|
# Create the files for a release and add signatures and hashes.
|
||||||
release: include.zip json.tar.xz
|
release: include.zip json.tar.xz
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Imported target for installations made with Meson (see meson.build).
|
||||||
|
#
|
||||||
|
# CMake installations generate this file with install(EXPORT ...). Meson cannot
|
||||||
|
# do that, but as the library is header-only, the target only needs an include
|
||||||
|
# directory, the C++ standard, and the compile definitions of the options that
|
||||||
|
# differ from their defaults. Paths are computed relative to this file so that
|
||||||
|
# the installation can be relocated (e.g., into a sysroot), unless includedir or
|
||||||
|
# datadir is outside the prefix.
|
||||||
|
|
||||||
|
if(TARGET @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_DIR}/@NLOHMANN_JSON_CONFIG_TO_PREFIX@" ABSOLUTE)
|
||||||
|
# As in CMake's generated file: avoid "//include" for an installation to "/".
|
||||||
|
if(_IMPORT_PREFIX STREQUAL "/")
|
||||||
|
set(_IMPORT_PREFIX "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ INTERFACE IMPORTED)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "@NLOHMANN_JSON_INCLUDE_DIR@"
|
||||||
|
)
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.8)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_FEATURES cxx_range_for
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_FEATURES cxx_std_11
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_NLOHMANN_JSON_COMPILE_DEFINITIONS "@NLOHMANN_JSON_COMPILE_DEFINITIONS@")
|
||||||
|
if(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "${_NLOHMANN_JSON_COMPILE_DEFINITIONS}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
unset(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
|
||||||
|
unset(_IMPORT_PREFIX)
|
||||||
@@ -4,4 +4,4 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
|||||||
Name: @PROJECT_NAME@
|
Name: @PROJECT_NAME@
|
||||||
Description: JSON for Modern C++
|
Description: JSON for Modern C++
|
||||||
Version: @PROJECT_VERSION@
|
Version: @PROJECT_VERSION@
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}@NLOHMANN_JSON_PKGCONFIG_CFLAGS@
|
||||||
|
|||||||
@@ -141,14 +141,8 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
parsed back as a regular array,
|
parsed back as a regular array,
|
||||||
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
||||||
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
||||||
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, a
|
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
|
||||||
value that fits the named width; for `double`, any value; for `single`, a value that survives narrowing to
|
`single` and `double`, an integer otherwise).
|
||||||
`float` and back without change (for instance, `0.1` does not, since it is not exactly representable as
|
|
||||||
`float`).
|
|
||||||
|
|
||||||
An annotated object is always read back with its keys in the order shown above, `"_ArrayType_"`, `"_ArraySize_"`,
|
|
||||||
`"_ArrayData_"`, regardless of the order the ND-array's header stores them in on the wire. This matters for
|
|
||||||
`ordered_json`, whose comparison takes key order into account.
|
|
||||||
|
|
||||||
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
||||||
array input to a BJData ND-array.
|
array input to a BJData ND-array.
|
||||||
|
|||||||
@@ -125,10 +125,23 @@ meson wrap install nlohmann_json
|
|||||||
Please see the Meson project for any issues regarding the packaging.
|
Please see the Meson project for any issues regarding the packaging.
|
||||||
|
|
||||||
The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in
|
The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in
|
||||||
which case a [pkg-config](pkg-config.md) file is installed. To use it, have your build system require the
|
which case a [pkg-config](pkg-config.md) file and the CMake package config files are installed. To use it, have your build system require
|
||||||
`nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the
|
the `nlohmann_json` pkg-config dependency, or use [`find_package(nlohmann_json)`](cmake.md#external) in CMake. In Meson,
|
||||||
[`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than
|
it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a
|
||||||
using the subproject directly.
|
subproject fallback, rather than using the subproject directly.
|
||||||
|
|
||||||
|
The options that change the library's configuration are available in Meson as well, named like the
|
||||||
|
[CMake options](cmake.md#cmake-options) without the `JSON_` prefix: `MultipleHeaders`, `GlobalUDLs`,
|
||||||
|
`ImplicitConversions`, `DisableEnumSerialization`, `DisableTupleReferenceConversion`, `Diagnostics`,
|
||||||
|
`Diagnostic_Positions`, `LegacyDiscardedValueComparison`, and `StrictNulHandling`. They have the same defaults as in CMake, except that
|
||||||
|
`MultipleHeaders` is `false`. Set them with `-D` when setting up the build, or with the subproject name as prefix when
|
||||||
|
the library is used as a subproject:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
meson setup build -Dnlohmann_json:Diagnostics=true
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting compile definitions are part of the Meson dependency, the pkg-config file, and the CMake target.
|
||||||
|
|
||||||
??? example "Example: Wrap"
|
??? example "Example: Wrap"
|
||||||
|
|
||||||
|
|||||||
@@ -2701,15 +2701,10 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
|
||||||
already known (it precedes the dimension vector read here),
|
|
||||||
otherwise 0; used to emit the "_ArrayType_" annotation key
|
|
||||||
before "_ArraySize_" if a dimension vector turns out to
|
|
||||||
describe an ndarray
|
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -2822,34 +2817,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
|
||||||
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
|
||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
|
||||||
if (ndarray_dtype != 0)
|
|
||||||
{
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2957,7 +2926,7 @@ class binary_reader
|
|||||||
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -3150,17 +3119,27 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
const char* type_name = bjd_type_name(size_and_type.second);
|
||||||
|
string_t key = "_ArrayType_";
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
|
||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t key = "_ArrayData_";
|
key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1817,31 +1817,16 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value survives narrowing to float and back without any change
|
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
||||||
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
|
||||||
@a dry_run is false
|
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
return !std::isfinite(dval) ||
|
||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
||||||
#endif
|
|
||||||
// a value that would be rounded (rather than exactly represented) by the
|
|
||||||
// narrowing to float is treated like an out-of-range integer element; this
|
|
||||||
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
|
||||||
const bool in_range = std::isnan(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
|
||||||
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
|
||||||
std::isinf(dval);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
||||||
#endif
|
|
||||||
return in_range;
|
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+99
-9
@@ -2,24 +2,114 @@ project('nlohmann_json',
|
|||||||
'cpp',
|
'cpp',
|
||||||
version : '3.12.0',
|
version : '3.12.0',
|
||||||
license : 'MIT',
|
license : 'MIT',
|
||||||
|
meson_version : '>= 0.64',
|
||||||
|
default_options: ['cpp_std=c++11'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if get_option('MultipleHeaders')
|
||||||
|
incdir = 'include'
|
||||||
|
else
|
||||||
|
incdir = 'single_include'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# The same compile definitions as the CMake target (see target_compile_definitions
|
||||||
|
# in CMakeLists.txt): only an option that differs from its default adds one.
|
||||||
|
json_defines = []
|
||||||
|
if not get_option('GlobalUDLs')
|
||||||
|
json_defines += 'JSON_USE_GLOBAL_UDLS=0'
|
||||||
|
endif
|
||||||
|
if not get_option('ImplicitConversions')
|
||||||
|
json_defines += 'JSON_USE_IMPLICIT_CONVERSIONS=0'
|
||||||
|
endif
|
||||||
|
if get_option('DisableEnumSerialization')
|
||||||
|
json_defines += 'JSON_DISABLE_ENUM_SERIALIZATION=1'
|
||||||
|
endif
|
||||||
|
if get_option('DisableTupleReferenceConversion')
|
||||||
|
json_defines += 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
|
||||||
|
endif
|
||||||
|
if get_option('Diagnostics')
|
||||||
|
json_defines += 'JSON_DIAGNOSTICS=1'
|
||||||
|
endif
|
||||||
|
if get_option('Diagnostic_Positions')
|
||||||
|
json_defines += 'JSON_DIAGNOSTIC_POSITIONS=1'
|
||||||
|
endif
|
||||||
|
if get_option('LegacyDiscardedValueComparison')
|
||||||
|
json_defines += 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1'
|
||||||
|
endif
|
||||||
|
if get_option('StrictNulHandling')
|
||||||
|
json_defines += 'JSON_STRICT_NUL_HANDLING=1'
|
||||||
|
endif
|
||||||
|
|
||||||
|
cpp_args = []
|
||||||
|
foreach define : json_defines
|
||||||
|
cpp_args += '-D' + define
|
||||||
|
endforeach
|
||||||
|
|
||||||
nlohmann_json_dep = declare_dependency(
|
nlohmann_json_dep = declare_dependency(
|
||||||
include_directories: include_directories('single_include')
|
compile_args: cpp_args,
|
||||||
|
include_directories: include_directories(incdir)
|
||||||
)
|
)
|
||||||
|
meson.override_dependency('nlohmann_json', nlohmann_json_dep)
|
||||||
|
|
||||||
|
# The multi-header version under the name earlier versions of this file used
|
||||||
nlohmann_json_multiple_headers = declare_dependency(
|
nlohmann_json_multiple_headers = declare_dependency(
|
||||||
|
compile_args: cpp_args,
|
||||||
include_directories: include_directories('include')
|
include_directories: include_directories('include')
|
||||||
)
|
)
|
||||||
|
|
||||||
if not meson.is_subproject()
|
if not meson.is_subproject()
|
||||||
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
install_subdir(
|
||||||
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
incdir / 'nlohmann',
|
||||||
install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann')
|
install_dir: get_option('includedir'),
|
||||||
|
install_tag: 'devel',
|
||||||
|
)
|
||||||
|
|
||||||
pkgc = import('pkgconfig')
|
pkgc = import('pkgconfig')
|
||||||
pkgc.generate(name: 'nlohmann_json',
|
pkgc.generate(name: 'nlohmann_json',
|
||||||
version: meson.project_version(),
|
version: meson.project_version(),
|
||||||
description: 'JSON for Modern C++'
|
description: 'JSON for Modern C++',
|
||||||
)
|
extra_cflags: cpp_args,
|
||||||
|
install_dir: get_option('datadir') / 'pkgconfig',
|
||||||
|
)
|
||||||
|
|
||||||
|
# CMake package config files, so that find_package(nlohmann_json) works. The
|
||||||
|
# include directory is given relative to the config files, so that the
|
||||||
|
# installation can be relocated. This is not possible if includedir or datadir
|
||||||
|
# is an absolute path outside the prefix (e.g., with the separate outputs of
|
||||||
|
# Nix); then the absolute include directory is used, as CMake does.
|
||||||
|
fs = import('fs')
|
||||||
|
cmake_install_dir = get_option('datadir') / 'cmake' / meson.project_name()
|
||||||
|
cmake_to_prefix = []
|
||||||
|
if fs.is_absolute(get_option('includedir')) or fs.is_absolute(cmake_install_dir)
|
||||||
|
cmake_include_dir = (get_option('prefix') / get_option('includedir')).replace('\\', '/')
|
||||||
|
else
|
||||||
|
foreach component : cmake_install_dir.split('/')
|
||||||
|
cmake_to_prefix += '..'
|
||||||
|
endforeach
|
||||||
|
cmake_include_dir = '${_IMPORT_PREFIX}/' + get_option('includedir')
|
||||||
|
endif
|
||||||
|
|
||||||
|
cmake_conf = configuration_data()
|
||||||
|
cmake_conf.set('PROJECT_NAME', meson.project_name())
|
||||||
|
cmake_conf.set('PROJECT_VERSION', meson.project_version())
|
||||||
|
cmake_conf.set('PROJECT_VERSION_MAJOR', meson.project_version().split('.')[0])
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_TARGET_NAME', meson.project_name())
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_TARGETS_EXPORT_NAME', meson.project_name() + 'Targets')
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_INCLUDE_DIR', cmake_include_dir)
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_CONFIG_TO_PREFIX', '/'.join(cmake_to_prefix))
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_COMPILE_DEFINITIONS', ';'.join(json_defines))
|
||||||
|
|
||||||
|
foreach cmake_file : [
|
||||||
|
['cmake/config.cmake.in', 'nlohmann_jsonConfig.cmake'],
|
||||||
|
['cmake/nlohmann_jsonConfigVersion.cmake.in', 'nlohmann_jsonConfigVersion.cmake'],
|
||||||
|
['cmake/nlohmann_jsonTargets.cmake.in', 'nlohmann_jsonTargets.cmake'],
|
||||||
|
]
|
||||||
|
configure_file(
|
||||||
|
input: cmake_file[0],
|
||||||
|
output: cmake_file[1],
|
||||||
|
configuration: cmake_conf,
|
||||||
|
format: 'cmake@',
|
||||||
|
install_dir: cmake_install_dir,
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
option(
|
||||||
|
'MultipleHeaders',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Use non-amalgamated version of the library',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'GlobalUDLs',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true,
|
||||||
|
description: 'Place user-defined string literals in the global namespace',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'ImplicitConversions',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true,
|
||||||
|
description: 'Enable implicit conversions',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'DisableEnumSerialization',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Disable default integer enum serialization',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'DisableTupleReferenceConversion',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Disable conversion from a one-element tuple of a JSON reference',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'Diagnostics',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Use extended diagnostic messages',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'Diagnostic_Positions',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable diagnostic positions',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'LegacyDiscardedValueComparison',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable legacy discarded value comparison',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'StrictNulHandling',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable strict NUL-byte handling',
|
||||||
|
)
|
||||||
@@ -16287,15 +16287,10 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
|
||||||
already known (it precedes the dimension vector read here),
|
|
||||||
otherwise 0; used to emit the "_ArrayType_" annotation key
|
|
||||||
before "_ArraySize_" if a dimension vector turns out to
|
|
||||||
describe an ndarray
|
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -16408,34 +16403,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
|
||||||
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
|
||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
|
||||||
if (ndarray_dtype != 0)
|
|
||||||
{
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -16543,7 +16512,7 @@ class binary_reader
|
|||||||
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -16736,17 +16705,27 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
const char* type_name = bjd_type_name(size_and_type.second);
|
||||||
|
string_t key = "_ArrayType_";
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
|
||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t key = "_ArrayData_";
|
key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -23067,31 +23046,16 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value survives narrowing to float and back without any change
|
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
||||||
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
|
||||||
@a dry_run is false
|
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
return !std::isfinite(dval) ||
|
||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
||||||
#endif
|
|
||||||
// a value that would be rounded (rather than exactly represented) by the
|
|
||||||
// narrowing to float is treated like an out-of-range integer element; this
|
|
||||||
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
|
||||||
const bool in_range = std::isnan(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
|
||||||
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
|
||||||
std::isinf(dval);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
||||||
#endif
|
|
||||||
return in_range;
|
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#define JSON_TESTS_PRIVATE
|
#define JSON_TESTS_PRIVATE
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
using ordered_json = nlohmann::ordered_json;
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@@ -2176,33 +2175,29 @@ TEST_CASE("BJData")
|
|||||||
|
|
||||||
SECTION("start_array() in ndarray _ArraySize_")
|
SECTION("start_array() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
// _ArrayType_ (2 events: key + string) is now emitted before
|
|
||||||
// _ArraySize_ (see GitHub issue #5661), which shifts the events
|
|
||||||
// below later by the same 2 events
|
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(4);
|
SaxCountdown scp(2);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("number_integer() in ndarray _ArraySize_")
|
SECTION("number_integer() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(5);
|
SaxCountdown scp(3);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("key() in ndarray _ArrayType_")
|
SECTION("key() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
// _ArrayType_ is emitted right after start_object(), before _ArraySize_
|
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(1);
|
SaxCountdown scp(6);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string() in ndarray _ArrayType_")
|
SECTION("string() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(2);
|
SaxCountdown scp(7);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2805,22 +2800,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK(out_single.at(0) == '{');
|
CHECK(out_single.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_single) == j_single);
|
CHECK(json::from_bjdata(out_single) == j_single);
|
||||||
|
|
||||||
// a double element that is finite and within the range of "single"
|
|
||||||
// but is not exactly representable as a float, so narrowing it would
|
|
||||||
// silently round it (0.1 is read back as 0.10000000149011612); this,
|
|
||||||
// like the overflow case above, falls back to a plain object (see
|
|
||||||
// GitHub issue #5661)
|
|
||||||
json const j_single_rounded = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 0.1}}});
|
|
||||||
const auto out_single_rounded = json::to_bjdata(j_single_rounded);
|
|
||||||
CHECK(out_single_rounded.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_single_rounded) == j_single_rounded);
|
|
||||||
|
|
||||||
// a double element that underflows to 0 when narrowed to "single"
|
|
||||||
json const j_single_underflow = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 1e-300}}});
|
|
||||||
const auto out_single_underflow = json::to_bjdata(j_single_underflow);
|
|
||||||
CHECK(out_single_underflow.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_single_underflow) == j_single_underflow);
|
|
||||||
|
|
||||||
// in-range boundary values still use the compact ndarray encoding
|
// in-range boundary values still use the compact ndarray encoding
|
||||||
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
||||||
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
||||||
@@ -2834,23 +2813,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ndarray annotation keys are read back in the documented order")
|
|
||||||
{
|
|
||||||
// from_bjdata() must emit the annotation object's keys in the order
|
|
||||||
// used throughout the documentation, _ArrayType_, _ArraySize_,
|
|
||||||
// _ArrayData_: the type marker precedes the dimension vector on the
|
|
||||||
// wire (see get_ubjson_size_type()), so it is known, and emitted,
|
|
||||||
// before _ArraySize_. For a plain json this key order is invisible
|
|
||||||
// (its comparison ignores it), but for an ordered_json it is not (see
|
|
||||||
// GitHub issue #5661).
|
|
||||||
const ordered_json o = ordered_json::parse(R"({"_ArrayType_":"uint8","_ArraySize_":[2,2],"_ArrayData_":[1,2,3,4]})");
|
|
||||||
const auto packed = ordered_json::to_bjdata(o);
|
|
||||||
CHECK(packed.at(0) == '[');
|
|
||||||
const ordered_json o_back = ordered_json::from_bjdata(packed);
|
|
||||||
CHECK(o_back == o);
|
|
||||||
CHECK(o_back.dump() == o.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
||||||
{
|
{
|
||||||
// the reader only restores an annotated object from an ND-array
|
// the reader only restores an annotated object from an ND-array
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# check_build_options
|
||||||
|
|
||||||
|
Checks that the Meson build and the pkg-config files offer the same options as the CMake target, so that a new CMake
|
||||||
|
option is not forgotten in one of them.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (`target_compile_definitions` in [`CMakeLists.txt`](../../CMakeLists.txt))
|
||||||
|
are the reference. When you add an option there, also add it to
|
||||||
|
|
||||||
|
- the pkg-config block in `CMakeLists.txt` (`NLOHMANN_JSON_PKGCONFIG_CFLAGS`),
|
||||||
|
- [`meson_options.txt`](../../meson_options.txt), named without the `JSON_` prefix and with the same default,
|
||||||
|
- [`meson.build`](../../meson.build) (`json_defines`), and
|
||||||
|
- the list of Meson options in
|
||||||
|
[`docs/mkdocs/docs/integration/package_managers.md`](../../docs/mkdocs/docs/integration/package_managers.md).
|
||||||
|
|
||||||
|
Run the check with
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make check_build_options
|
||||||
|
```
|
||||||
|
|
||||||
|
It needs only Python 3 and runs in the `ci_meson_install` CI job.
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Check that the Meson build and the pkg-config files offer the CMake options.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (target_compile_definitions in
|
||||||
|
CMakeLists.txt) are the reference. For every option used there, the script
|
||||||
|
checks that
|
||||||
|
|
||||||
|
- the CMake pkg-config file adds the same definition under the same condition,
|
||||||
|
- meson_options.txt has a boolean option of the same name without the "JSON_"
|
||||||
|
prefix and with the same default,
|
||||||
|
- meson.build adds the same definition under the same condition, and
|
||||||
|
- the Meson section of the package manager documentation lists the option.
|
||||||
|
|
||||||
|
Meson's MultipleHeaders option selects the include directory and adds no
|
||||||
|
definition; it is the only Meson option without a definition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
||||||
|
DOCS = os.path.join('docs', 'mkdocs', 'docs', 'integration', 'package_managers.md')
|
||||||
|
|
||||||
|
# Meson options that add no compile definition, with their default
|
||||||
|
MESON_ONLY = {'MultipleHeaders': 'false'}
|
||||||
|
|
||||||
|
|
||||||
|
def read(root, path):
|
||||||
|
with open(os.path.join(root, path), encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_target_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from target_compile_definitions."""
|
||||||
|
block = re.search(r'target_compile_definitions\(\s*\$\{NLOHMANN_JSON_TARGET_NAME\}\s*INTERFACE(.*?)\n\)', cmake, re.S)
|
||||||
|
if not block:
|
||||||
|
sys.exit('CMakeLists.txt: target_compile_definitions of the target not found')
|
||||||
|
result = {}
|
||||||
|
for line in block.group(1).split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<NOT:\$<BOOL:\$\{JSON_(\w+)\}>>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), False)
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<BOOL:\$\{JSON_(\w+)\}>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), True)
|
||||||
|
continue
|
||||||
|
sys.exit(f'CMakeLists.txt: unexpected line in target_compile_definitions: {line}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_defaults(cmake):
|
||||||
|
"""Return {option: 'true'/'false'} for the option() calls of the form JSON_<name>."""
|
||||||
|
return {m.group(1): 'true' if m.group(2) == 'ON' else 'false'
|
||||||
|
for m in re.finditer(r'^option\(JSON_(\w+)\s+"[^"]*"\s+(ON|OFF)\)', cmake, re.M)}
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_pkgconfig_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from the pkg-config block."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r'if \((NOT )?JSON_(\w+)\)\s*\n\s*string\(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -D(\w+=\w+)"\)', cmake)}
|
||||||
|
|
||||||
|
|
||||||
|
def meson_options(options):
|
||||||
|
"""Return {option: default} for the boolean options in meson_options.txt."""
|
||||||
|
result = {}
|
||||||
|
for block in re.findall(r'option\((.*?)\)', options, re.S):
|
||||||
|
name = re.search(r"'(\w+)'", block).group(1)
|
||||||
|
kind = re.search(r"type\s*:\s*'(\w+)'", block)
|
||||||
|
value = re.search(r'value\s*:\s*(\w+)', block)
|
||||||
|
result[name] = value.group(1) if kind and kind.group(1) == 'boolean' and value else None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def meson_definitions(meson):
|
||||||
|
"""Return {option: (definition, add_if_on)} from meson.build."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r"if (not )?get_option\('(\w+)'\)\s*\n\s*json_defines \+= '(\w+=\w+)'", meson)}
|
||||||
|
|
||||||
|
|
||||||
|
def describe(definition):
|
||||||
|
name, add_if_on = definition
|
||||||
|
return f'{name} if {"enabled" if add_if_on else "disabled"}'
|
||||||
|
|
||||||
|
|
||||||
|
def compare(errors, where, expected, actual):
|
||||||
|
for option, definition in expected.items():
|
||||||
|
if option not in actual:
|
||||||
|
errors.append(f'{where}: no definition for option {option} (expected {describe(definition)})')
|
||||||
|
elif actual[option] != definition:
|
||||||
|
errors.append(f'{where}: option {option} adds {describe(actual[option])}, expected {describe(definition)}')
|
||||||
|
for option in actual.keys() - expected.keys():
|
||||||
|
errors.append(f'{where}: definition for option {option}, which the CMake target does not have')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__.split('\n')[0])
|
||||||
|
parser.add_argument('root', nargs='?', default=REPO_ROOT, help='repository root (default: %(default)s)')
|
||||||
|
root = parser.parse_args().root
|
||||||
|
|
||||||
|
cmake = read(root, 'CMakeLists.txt')
|
||||||
|
reference = cmake_target_definitions(cmake)
|
||||||
|
defaults = cmake_defaults(cmake)
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
compare(errors, 'CMakeLists.txt (pkg-config)', reference, cmake_pkgconfig_definitions(cmake))
|
||||||
|
compare(errors, 'meson.build', reference, meson_definitions(read(root, 'meson.build')))
|
||||||
|
|
||||||
|
options = meson_options(read(root, 'meson_options.txt'))
|
||||||
|
for option in reference:
|
||||||
|
if option not in defaults:
|
||||||
|
errors.append(f'CMakeLists.txt: no option(JSON_{option} ... ON|OFF)')
|
||||||
|
elif option not in options:
|
||||||
|
errors.append(f'meson_options.txt: option {option} missing')
|
||||||
|
elif options[option] != defaults[option]:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {defaults[option]} as in CMake')
|
||||||
|
for option, default in MESON_ONLY.items():
|
||||||
|
if options.get(option) != default:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {default}')
|
||||||
|
for option in options.keys() - reference.keys() - MESON_ONLY.keys():
|
||||||
|
errors.append(f'meson_options.txt: option {option} has no counterpart in the CMake target')
|
||||||
|
|
||||||
|
docs = read(root, DOCS)
|
||||||
|
for option in sorted(set(options) & (reference.keys() | MESON_ONLY.keys())):
|
||||||
|
if f'`{option}`' not in docs:
|
||||||
|
errors.append(f'{DOCS}: Meson option {option} not listed')
|
||||||
|
|
||||||
|
for error in errors:
|
||||||
|
print(error, file=sys.stderr)
|
||||||
|
if errors:
|
||||||
|
print('The Meson build and the pkg-config files must offer the options of the CMake target; see '
|
||||||
|
'tools/check_build_options/README.md.', file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
print(f'OK: {len(reference)} options with compile definitions agree between CMake, pkg-config, and Meson.')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user