Merge branch 'develop' of https://github.com/nlohmann/json into bon8

 Conflicts:
	docs/mkdocs/docs/api/basic_json/index.md
	docs/mkdocs/docs/features/binary_formats/index.md
	docs/mkdocs/mkdocs.yml
	tests/src/unit-binary_formats.cpp
diff --git a/.clang-tidy b/.clang-tidy
index 3673930..b981348 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -43,6 +43,7 @@
          -readability-identifier-length,
          -readability-magic-numbers,
          -readability-redundant-access-specifiers,
+         -readability-simplify-boolean-expr
          -readability-uppercase-literal-suffix'
 
 CheckOptions:
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 03c652a..28e0b74 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -10,7 +10,7 @@
   workflow_dispatch:
 
 jobs:
-  xcode:
+  xcode_1:
     runs-on: macos-10.15
     strategy:
       matrix:
@@ -27,8 +27,25 @@
       - name: test
         run: cd build ; ctest -j 10 --output-on-failure
 
+  xcode_2:
+    runs-on: macos-12
+    strategy:
+      matrix:
+        xcode: [13.3.1, 13.3, 13.2.1, 13.2, 13.1]
+    env:
+      DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: cmake
+        run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON
+      - name: build
+        run: cmake --build build --parallel 10
+      - name: test
+        run: cd build ; ctest -j 10 --output-on-failure
+
   xcode_standards:
-    runs-on: macos-10.15
+    runs-on: macos-latest
     strategy:
       matrix:
         standard: [11, 14, 17, 20]
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index b5f5833..4407104 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -12,7 +12,7 @@
 jobs:
   ci_test_clang:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     steps:
       - uses: actions/checkout@v3
       - name: cmake
@@ -22,7 +22,7 @@
 
   ci_test_gcc:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     steps:
       - uses: actions/checkout@v3
       - name: cmake
@@ -32,7 +32,7 @@
 
   ci_static_analysis:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     strategy:
       matrix:
         target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_infer]
@@ -43,12 +43,22 @@
       - name: build
         run: cmake --build build --target ${{ matrix.target }}
 
+  ci_test_single_header:
+    runs-on: ubuntu-latest
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
+    steps:
+      - uses: actions/checkout@v3
+      - name: cmake
+        run: cmake -S . -B build -DJSON_CI=On
+      - name: build
+        run: cmake --build build --target ci_test_single_header
+
   ci_cmake_options:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     strategy:
       matrix:
-        target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions]
+        target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison]
     steps:
       - uses: actions/checkout@v3
       - name: cmake
@@ -58,7 +68,7 @@
 
   ci_test_coverage:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     steps:
       - uses: actions/checkout@v3
       - name: cmake
@@ -78,10 +88,10 @@
 
   ci_test_compilers:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     strategy:
       matrix:
-        compiler: [g++-4.8, g++-4.9, g++-5, g++-6, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12, clang++-13, clang++-14]
+        compiler: [g++-4.8, g++-4.9, g++-5, g++-6, g++-7, g++-8, g++-9, g++-10, g++-11, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12, clang++-13, clang++-14]
     steps:
       - uses: actions/checkout@v3
       - name: cmake
@@ -91,7 +101,7 @@
 
   ci_test_standards:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     strategy:
       matrix:
         standard: [11, 14, 17, 20]
@@ -105,10 +115,22 @@
 
   ci_cuda_example:
     runs-on: ubuntu-latest
-    container: ghcr.io/nlohmann/json-ci:v2.3.0
+    container: ghcr.io/nlohmann/json-ci:v2.4.0
     steps:
       - uses: actions/checkout@v3
       - name: cmake
         run: cmake -S . -B build -DJSON_CI=On
       - name: build
         run: cmake --build build --target ci_cuda_example
+
+  ci_icpc:
+    runs-on: ubuntu-latest
+    container: ghcr.io/nlohmann/json-ci:v2.2.0
+    steps:
+      - uses: actions/checkout@v2
+      - name: cmake
+        run: cmake -S . -B build -DJSON_CI=On
+      - name: build
+        run: |
+          . /opt/intel/oneapi/setvars.sh
+          cmake --build build --target ci_icpc
diff --git a/.gitignore b/.gitignore
index db6dcec..72eb2c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,10 @@
 # build directories (vscode-cmake-tools, user-defined, ...)
 /build*/
 
+# fuzzers
+/tests/parse_*_fuzzer
+/tests/corpus_*
+
 /docs/mkdocs/docs/examples/
 /docs/mkdocs/docs/__pycache__/
 /docs/mkdocs/site/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8f65d9a..f20d561 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,13 +37,15 @@
 else()
     set(JSON_BuildTests_INIT OFF)
 endif()
-option(JSON_BuildTests          "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
-option(JSON_CI                  "Enable CI build targets." OFF)
-option(JSON_Diagnostics         "Use extended diagnostic messages." OFF)
-option(JSON_ImplicitConversions "Enable implicit conversions." ON)
-option(JSON_Install             "Install CMake targets during install step." ${MAIN_PROJECT})
-option(JSON_MultipleHeaders     "Use non-amalgamated version of the library." OFF)
-option(JSON_SystemInclude       "Include as system headers (skip for clang-tidy)." OFF)
+option(JSON_BuildTests                     "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
+option(JSON_CI                             "Enable CI build targets." OFF)
+option(JSON_Diagnostics                    "Use extended diagnostic messages." OFF)
+option(JSON_ImplicitConversions            "Enable implicit conversions." ON)
+option(JSON_DisableEnumSerialization       "Disable default integer enum serialization." OFF)
+option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)           
+option(JSON_Install                        "Install CMake targets during install step." ${MAIN_PROJECT})
+option(JSON_MultipleHeaders                "Use non-amalgamated version of the library." ON)
+option(JSON_SystemInclude                  "Include as system headers (skip for clang-tidy)." OFF)
 
 if (JSON_CI)
     include(ci)
@@ -77,6 +79,14 @@
     message(STATUS "Implicit conversions are disabled")
 endif()
 
+if (JSON_DisableEnumSerialization)
+    message(STATUS "Enum integer serialization is disabled")
+endif()
+
+if (JSON_LegacyDiscardedValueComparison)
+    message(STATUS "Legacy discarded value comparison enabled")
+endif()
+
 if (JSON_Diagnostics)
     message(STATUS "Diagnostics enabled")
 endif()
@@ -100,8 +110,10 @@
 target_compile_definitions(
     ${NLOHMANN_JSON_TARGET_NAME}
     INTERFACE
-    JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
-    JSON_DIAGNOSTICS=$<BOOL:${JSON_Diagnostics}>
+    $<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
+    $<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
+    $<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
+    $<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
 )
 
 target_include_directories(
diff --git a/README.md b/README.md
index a46a856..220b3a9 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@
 
 - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
 
-- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
+- **Serious testing**. Our code is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
 
 Other aspects were not so important to us:
 
@@ -69,6 +69,10 @@
 
 You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nlohmann).
 
+### :office: Corporate Sponsor
+
+[![](https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Codacy-logo-black.svg/320px-Codacy-logo-black.svg.png)](https://github.com/codacy)
+
 ### :label: Named Sponsors
 
 - [Michael Hartmann](https://github.com/reFX-Mike)
@@ -1083,18 +1087,11 @@
 | Apple Clang 12.0.0 (clang-1200.0.32.27); Xcode 12.2                                                    | macOS 10.15.7      | GitHub Actions |
 | Apple Clang 12.0.0 (clang-1200.0.32.28); Xcode 12.3                                                    | macOS 10.15.7      | GitHub Actions |
 | Apple Clang 12.0.0 (clang-1200.0.32.29); Xcode 12.4                                                    | macOS 10.15.7      | GitHub Actions |
-| GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 4.9.3 (Ubuntu 4.9.3-13ubuntu2)                                                                     | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12)                                                             | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 6.4.0 (Ubuntu 6.4.0-17ubuntu1)                                                                     | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 7.5.0 (Ubuntu 7.5.0-6ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)                                          | Windows-10.0.17763 | GitHub Actions |
-| GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)                                          | Windows-10.0.17763 | GitHub Actions |
-| GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)                                                               | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04)                                                              | Ubuntu 20.04.3 LTS | GitHub Actions |
-| GCC 11.1.0                                                                                             | Ubuntu (aarch64)   | Drone CI       |
-| GCC 12.0.0 20211219 (experimental)                                                                     | Ubuntu 20.04.3 LTS | GitHub Actions |
+| Apple Clang 13.0.0 (clang-1300.0.29.3); Xcode 13.1                                                     | macOS 12.3.1       | GitHub Actions |
+| Apple Clang 13.0.0 (clang-1300.0.29.30); Xcode 13.2                                                    | macOS 12.3.1       | GitHub Actions |
+| Apple Clang 13.0.0 (clang-1300.0.29.30); Xcode 13.2.1                                                  | macOS 12.3.1       | GitHub Actions |
+| Apple Clang 13.1.6 (clang-1316.0.21.2); Xcode 13.3                                                     | macOS 12.3.1       | GitHub Actions |
+| Apple Clang 13.1.6 (clang-1316.0.21.2.3); Xcode 13.3.1                                                 | macOS 12.3.1       | GitHub Actions |
 | Clang 3.5.2 (3.5.2-3ubuntu1)                                                                           | Ubuntu 20.04.3 LTS | GitHub Actions |
 | Clang 3.6.2 (3.6.2-3ubuntu2)                                                                           | Ubuntu 20.04.3 LTS | GitHub Actions |
 | Clang 3.7.1 (3.7.1-2ubuntu2)                                                                           | Ubuntu 20.04.3 LTS | GitHub Actions |
@@ -1113,8 +1110,22 @@
 | Clang 11.0.0 (11.0.0-2~ubuntu20.04.1)                                                                  | Ubuntu 20.04.3 LTS | GitHub Actions |
 | Clang 12.0.0 (12.0.0-3ubuntu1~20.04.3)                                                                 | Ubuntu 20.04.3 LTS | GitHub Actions |
 | Clang 13.0.1 (13.0.1-++20211015123032+cf15ccdeb6d5-1exp120211015003613.5)                              | Ubuntu 20.04.3 LTS | GitHub Actions |
-| Clang 14.0.0 (14.0.0-++20211221052852+55c71c9eac9b-1exp120211221172954.95)                             | Ubuntu 20.04.3 LTS | GitHub Actions |
-| Clang 15.0.0 (15.0.0-++20220403052648+896770c9a92e-1~exp1~20220403172744.209)                          | Ubuntu 20.04.3 LTS | GitHub Actions |
+| Clang 14.0.5-++20220603124341+2f0a69c32a4c-1~exp1~20220603124352.149                                   | Ubuntu 20.04.3 LTS | GitHub Actions |
+| Clang 15.0.0 (15.0.0-++20220530052901+b7d2b160c3ba-1~exp1~20220530172952.268)                          | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 4.9.3 (Ubuntu 4.9.3-13ubuntu2)                                                                     | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12)                                                             | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 6.4.0 (Ubuntu 6.4.0-17ubuntu1)                                                                     | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 7.5.0 (Ubuntu 7.5.0-6ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)                                          | Windows-10.0.17763 | GitHub Actions |
+| GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)                                          | Windows-10.0.17763 | GitHub Actions |
+| GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2)                                                                      | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)                                                               | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04)                                                              | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 11.1.0                                                                                             | Ubuntu (aarch64)   | Drone CI       |
+| GCC 11.1.0 (Ubuntu 11.1.0-1ubuntu1~20.04)                                                              | Ubuntu 20.04.3 LTS | GitHub Actions |
+| GCC 13.0.0 13.0.0 20220605 (experimental)                                                              | Ubuntu 20.04.3 LTS | GitHub Actions |
+| Intel C++ Compiler 2021.5.0.20211109                                                                   | Ubuntu 20.04.3 LTS | GitHub Actions |
 | NVCC 11.0.221                                                                                          | Ubuntu 20.04.3 LTS | GitHub Actions |
 | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1)                            | Windows-6.3.9600   | AppVeyor       |
 | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor       |
@@ -1598,6 +1609,21 @@
 - [Dirk Stolle](https://github.com/striezel) fixed typos in documentation.
 - [Daniel Albuschat](https://github.com/daniel-kun) corrected the parameter name in the `parse` documentation.
 - [Prince Mendiratta](https://github.com/Prince-Mendiratta) fixed a link to the FAQ.
+- [Florian Albrechtskirchinger](https://github.com/falbrechtskirchinger) implemented `std::string_view` support for object keys and made dozens of other improvements.
+- [Qianqian Fang](https://github.com/fangq) implemented the Binary JData (BJData) format.
+- [pketelsen](https://github.com/pketelsen) added macros `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`.
+- [DarkZeros](https://github.com/DarkZeros) adjusted to code to not clash with Arduino defines.
+- [flagarde](https://github.com/flagarde) fixed the output of `meta()` for MSVC.
+- [Giovanni Cerretani](https://github.com/gcerretani) fixed a check for `std::filesystem`.
+- [Dimitris Apostolou](https://github.com/rex4539) fixed a typo.
+- [Ferry Huberts](https://github.com/fhuberts) fixed a typo.
+- [Michael Nosthoff](https://github.com/heinemml) fixed a typo.
+- [JungHoon Lee](https://github.com/jhnlee) fixed a typo.
+- [Faruk D.](https://github.com/fdiblen) fixed the CITATION.CFF file.
+- [Andrea Cocito](https://github.com/puffetto) added a clarification on macro usage to the documentation.
+- [Krzysiek Karbowiak](https://github.com/kkarbowiak) refactored the tests to use `CHECK_THROWS_WITH_AS`.
+- [Chaoqi Zhang](https://github.com/prncoprs) fixed a typo.
+- [ivanovmp](https://github.com/ivanovmp) fixed a whitespace error.
 
 Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
 
@@ -1612,7 +1638,7 @@
 - [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code indentation
 - [**Clang**](https://clang.llvm.org) for compilation with code sanitizers
 - [**CMake**](https://cmake.org) for build automation
-- [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json)
+- [**Codacy**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json)
 - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
 - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
 - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
@@ -1632,7 +1658,7 @@
 
 ## Projects using JSON for Modern C++
 
-The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices.
+The library is currently used in Apple macOS Sierra-Monterey and iOS 10-15. I am not sure what they are using the library for, but I am happy that it runs on so many devices.
 
 
 ## Notes
diff --git a/cmake/ci.cmake b/cmake/ci.cmake
index b2a2f47..4fcff86 100644
--- a/cmake/ci.cmake
+++ b/cmake/ci.cmake
@@ -111,7 +111,7 @@
     -Wno-reserved-identifier
 )
 
-# Warning flags determined for GCC 12.0 (experimental) with https://github.com/nlohmann/gcc_flags:
+# Warning flags determined for GCC 13.0 (experimental) with https://github.com/nlohmann/gcc_flags:
 # Ignored GCC warnings:
 # -Wno-abi-tag                    We do not care about ABI tags.
 # -Wno-aggregate-return           The library uses aggregate returns.
@@ -161,10 +161,13 @@
     -Wanalyzer-use-after-free
     -Wanalyzer-use-of-pointer-in-stale-stack-frame
     -Wanalyzer-use-of-uninitialized-value
+    -Wanalyzer-va-arg-type-mismatch
+    -Wanalyzer-va-list-exhausted
+    -Wanalyzer-va-list-leak
+    -Wanalyzer-va-list-use-after-va-end
     -Wanalyzer-write-to-const
     -Wanalyzer-write-to-string-literal
     -Warith-conversion
-    -Warray-bounds
     -Warray-bounds=2
     -Warray-compare
     -Warray-parameter=2
@@ -209,6 +212,7 @@
     -Wctad-maybe-unsupported
     -Wctor-dtor-privacy
     -Wdangling-else
+    -Wdangling-pointer=2
     -Wdate-time
     -Wdelete-incomplete
     -Wdelete-non-virtual-dtor
@@ -279,6 +283,7 @@
     -Wmissing-include-dirs
     -Wmissing-profile
     -Wmissing-requires
+    -Wmissing-template-keyword
     -Wmultichar
     -Wmultiple-inheritance
     -Wmultistatement-macros
@@ -340,11 +345,9 @@
     -Wsizeof-pointer-div
     -Wsizeof-pointer-memaccess
     -Wstack-protector
-    -Wstrict-aliasing
     -Wstrict-aliasing=3
     -Wstrict-null-sentinel
-    -Wstrict-overflow
-    -Wstrict-overflow=5
+    -Wno-strict-overflow
     -Wstring-compare
     -Wstringop-overflow=4
     -Wstringop-overread
@@ -373,6 +376,7 @@
     -Wterminate
     -Wtrampolines
     -Wtrigraphs
+    -Wtrivial-auto-var-init
     -Wtsan
     -Wtype-limits
     -Wundef
@@ -392,6 +396,7 @@
     -Wunused-result
     -Wunused-value
     -Wunused-variable
+    -Wuse-after-free=3
     -Wuseless-cast
     -Wvarargs
     -Wvariadic-macros
@@ -411,7 +416,7 @@
 add_custom_target(ci_test_gcc
     COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND}
         -DCMAKE_BUILD_TYPE=Debug -GNinja
-        -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
+        -DJSON_BuildTests=ON
         -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_gcc
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_gcc
     COMMAND cd ${PROJECT_BINARY_DIR}/build_gcc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
@@ -421,7 +426,7 @@
 add_custom_target(ci_test_clang
     COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND}
         -DCMAKE_BUILD_TYPE=Debug -GNinja
-        -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
+        -DJSON_BuildTests=ON
         -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang
     COMMAND cd ${PROJECT_BINARY_DIR}/build_clang && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
@@ -463,7 +468,7 @@
 add_custom_target(ci_test_noexceptions
     COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
     -DCMAKE_BUILD_TYPE=Debug -GNinja
-    -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -DCMAKE_CXX_FLAGS=-DJSON_NOEXCEPTION -DDOCTEST_TEST_FILTER=--no-throw
+    -DJSON_BuildTests=ON -DCMAKE_CXX_FLAGS=-DJSON_NOEXCEPTION -DDOCTEST_TEST_FILTER=--no-throw
     -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_noexceptions
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_noexceptions
     COMMAND cd ${PROJECT_BINARY_DIR}/build_noexceptions && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
@@ -477,7 +482,7 @@
 add_custom_target(ci_test_noimplicitconversions
     COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
     -DCMAKE_BUILD_TYPE=Debug -GNinja
-    -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -DJSON_ImplicitConversions=OFF
+    -DJSON_BuildTests=ON -DJSON_ImplicitConversions=OFF
     -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_noimplicitconversions
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_noimplicitconversions
     COMMAND cd ${PROJECT_BINARY_DIR}/build_noimplicitconversions && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
@@ -491,7 +496,7 @@
 add_custom_target(ci_test_diagnostics
     COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
     -DCMAKE_BUILD_TYPE=Debug -GNinja
-    -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -DJSON_Diagnostics=ON
+    -DJSON_BuildTests=ON -DJSON_Diagnostics=ON
     -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_diagnostics
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_diagnostics
     COMMAND cd ${PROJECT_BINARY_DIR}/build_diagnostics && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
@@ -499,17 +504,38 @@
 )
 
 ###############################################################################
+# Enable legacy discarded value comparison.
+###############################################################################
+
+add_custom_target(ci_test_legacycomparison
+    COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
+    -DCMAKE_BUILD_TYPE=Debug -GNinja
+    -DJSON_BuildTests=ON -DJSON_LegacyDiscardedValueComparison=ON
+    -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_legacycomparison
+    COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_legacycomparison
+    COMMAND cd ${PROJECT_BINARY_DIR}/build_legacycomparison && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
+    COMMENT "Compile and test with legacy discarded value comparison enabled"
+)
+
+###############################################################################
 # Coverage.
 ###############################################################################
 
 add_custom_target(ci_test_coverage
     COMMAND CXX=g++ ${CMAKE_COMMAND}
         -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage"
-        -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
+        -DJSON_BuildTests=ON
         -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage
     COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
 
+    COMMAND CXX=g++ ${CMAKE_COMMAND}
+        -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="-m32;--coverage;-fprofile-arcs;-ftest-coverage"
+        -DJSON_BuildTests=ON -DJSON_32bitTest=ONLY
+        -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage32
+    COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32
+    COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
+
     COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc lcov_branch_coverage=1
     COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc lcov_branch_coverage=1
     COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
@@ -563,6 +589,20 @@
 )
 
 ###############################################################################
+# Build and test using the amalgamated header
+###############################################################################
+
+add_custom_target(ci_test_single_header
+    COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND}
+        -DCMAKE_BUILD_TYPE=Debug -GNinja
+        -DJSON_BuildTests=ON -DJSON_MultipleHeader=OFF -DJSON_FastTests=ON
+        -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_single_header
+    COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_single_header
+    COMMAND cd ${PROJECT_BINARY_DIR}/build_single_header && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
+    COMMENT "Compile and test single-header version"
+)
+
+###############################################################################
 # Valgrind.
 ###############################################################################
 
@@ -645,7 +685,7 @@
     COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
         -DCMAKE_BUILD_TYPE=Debug -GNinja
         -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_CLANG_TIDY=${CLANG_TIDY_TOOL}
-        -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
+        -DJSON_BuildTests=ON
         -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_tidy
     COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_tidy
     COMMENT "Check code with Clang-Tidy"
@@ -672,7 +712,7 @@
 
 add_custom_target(ci_infer
     COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer
-    COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
+    COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON
     COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make
     COMMENT "Check code with Infer"
 )
@@ -698,6 +738,7 @@
 ###############################################################################
 
 add_custom_target(ci_non_git_tests
+    COMMAND git config --global --add safe.directory ${PROJECT_SOURCE_DIR}
     COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_non_git_tests/sources
     COMMAND cd ${PROJECT_SOURCE_DIR} && for FILE in `${GIT_TOOL} ls-tree --name-only HEAD`\; do cp -r $$FILE ${PROJECT_BINARY_DIR}/build_non_git_tests/sources \; done
     COMMAND ${CMAKE_COMMAND}
@@ -797,8 +838,9 @@
 ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY)
 ci_get_cmake(3.13.0 CMAKE_3_13_0_BINARY)
 
-set(JSON_CMAKE_FLAGS_3_1_0 "JSON_Install;JSON_MultipleHeaders;JSON_ImplicitConversions;JSON_Valgrind;JSON_Diagnostics;JSON_SystemInclude")
-set(JSON_CMAKE_FLAGS_3_13_0 "JSON_BuildTests")
+set(JSON_CMAKE_FLAGS_3_1_0 JSON_Diagnostics JSON_ImplicitConversions JSON_DisableEnumSerialization 
+    JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind)
+set(JSON_CMAKE_FLAGS_3_13_0 JSON_BuildTests)
 
 function(ci_add_cmake_flags_targets flag min_version)
     string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
@@ -841,7 +883,7 @@
 # Use more installed compilers.
 ###############################################################################
 
-foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14)
+foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14)
     find_program(COMPILER_TOOL NAMES ${COMPILER})
     if (COMPILER_TOOL)
         if ("${COMPILER}" STREQUAL "clang++-9")
@@ -878,6 +920,21 @@
 )
 
 ###############################################################################
+# Intel C++ Compiler
+###############################################################################
+
+add_custom_target(ci_icpc
+    COMMAND ${CMAKE_COMMAND}
+        -DCMAKE_BUILD_TYPE=Debug -GNinja
+        -DCMAKE_C_COMPILER=icc -DCMAKE_CXX_COMPILER=icpc
+        -DJSON_BuildTests=ON -DJSON_FastTests=ON
+        -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_icpc
+    COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_icpc
+    COMMAND cd ${PROJECT_BINARY_DIR}/build_icpc && ${CMAKE_CTEST_COMMAND} --parallel ${N} --exclude-regex "test-unicode" --output-on-failure
+    COMMENT "Compile and test with ICPC"
+)
+
+###############################################################################
 # Clean up all generated files.
 ###############################################################################
 
diff --git a/cmake/test.cmake b/cmake/test.cmake
index 1d14664..b8b1250 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -156,6 +156,7 @@
 #############################################################################
 # json_test_add_test_for(
 #     <file>
+#     [NAME <name>]
 #     MAIN <main>
 #     [CXX_STANDARDS <version_number>...] [FORCE])
 #
@@ -165,6 +166,7 @@
 #
 # if C++ standard <version_number> is supported by the compiler and the
 # source file contains JSON_HAS_CPP_<version_number>.
+# Use NAME <name> to override the filename-derived test name.
 # Use FORCE to create the test regardless of the file containing
 # JSON_HAS_CPP_<version_number>.
 # Test targets are linked against <main>.
@@ -172,15 +174,22 @@
 #############################################################################
 
 function(json_test_add_test_for file)
-    cmake_parse_arguments(args "FORCE" "MAIN" "CXX_STANDARDS" ${ARGN})
-
-    get_filename_component(file_basename ${file} NAME_WE)
-    string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename})
+    cmake_parse_arguments(args "FORCE" "MAIN;NAME" "CXX_STANDARDS" ${ARGN})
 
     if("${args_MAIN}" STREQUAL "")
         message(FATAL_ERROR "Required argument MAIN <main> missing.")
     endif()
 
+    if("${args_NAME}" STREQUAL "")
+        get_filename_component(file_basename ${file} NAME_WE)
+        string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename})
+    else()
+        set(test_name ${args_NAME})
+        if(NOT test_name MATCHES "test-[^$]+")
+            message(FATAL_ERROR "Test name must start with 'test-'.")
+        endif()
+    endif()
+
     if("${args_CXX_STANDARDS}" STREQUAL "")
         set(args_CXX_STANDARDS 11)
     endif()
@@ -202,3 +211,32 @@
         _json_test_add_test(${test_name} ${file} ${args_MAIN} ${cxx_standard})
     endforeach()
 endfunction()
+
+#############################################################################
+# json_test_should_build_32bit_test(
+#     <build_32bit_var> <build_32bit_only_var> <input>)
+#
+# Check if the 32bit unit test should be built based on the value of <input>
+# and store the result in the variables <build_32bit_var> and
+# <build_32bit_only_var>.
+#############################################################################
+
+function(json_test_should_build_32bit_test build_32bit_var build_32bit_only_var input)
+    set(${build_32bit_only_var} OFF PARENT_SCOPE)
+    string(TOUPPER "${input}" ${build_32bit_var})
+    if("${${build_32bit_var}}" STREQUAL AUTO)
+        # check if compiler is targeting 32bit by default
+        include(CheckTypeSize)
+        check_type_size("size_t" sizeof_size_t LANGUAGE CXX)
+        if(sizeof_size_t AND ${sizeof_size_t} EQUAL 4)
+            message(STATUS "Auto-enabling 32bit unit test.")
+            set(${build_32bit_var} ON)
+        else()
+            set(${build_32bit_var} OFF)
+        endif()
+    elseif("${${build_32bit_var}}" STREQUAL ONLY)
+        set(${build_32bit_only_var} ON PARENT_SCOPE)
+    endif()
+
+    set(${build_32bit_var} "${${build_32bit_var}}" PARENT_SCOPE)
+endfunction()
diff --git a/docs/docset/docSet.sql b/docs/docset/docSet.sql
index 8bd4e06..30c4a48 100644
--- a/docs/docset/docSet.sql
+++ b/docs/docset/docSet.sql
@@ -140,6 +140,7 @@
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'features/macros/index.html#json_assertx');
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'features/macros/index.html#json_catch_userexception');
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'features/macros/index.html#json_diagnostics');
+INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'features/macros/index.html#json_disable_enum_serialization');
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'features/macros/index.html#json_has_cpp_11-json_has_cpp_14-json_has_cpp_17-json_has_cpp_20');
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'features/macros/index.html#json_has_cpp_11-json_has_cpp_14-json_has_cpp_17-json_has_cpp_20');
 INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'features/macros/index.html#json_has_cpp_11-json_has_cpp_14-json_has_cpp_17-json_has_cpp_20');
diff --git a/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp b/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp
new file mode 100644
index 0000000..1c10be5
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp
@@ -0,0 +1,23 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+// define a byte container based on std::vector
+using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
+
+using json = nlohmann::json;
+
+int main()
+{
+    // (1) create empty container
+    auto c1 = byte_container_with_subtype();
+
+    std::vector<std::uint8_t> bytes = {{0xca, 0xfe, 0xba, 0xbe}};
+
+    // (2) create container
+    auto c2 = byte_container_with_subtype(bytes);
+
+    // (3) create container with subtype
+    auto c3 = byte_container_with_subtype(bytes, 42);
+
+    std::cout << json(c1) << "\n" << json(c2) << "\n" << json(c3) << std::endl;
+}
diff --git a/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output b/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output
new file mode 100644
index 0000000..67ac1b2
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output
@@ -0,0 +1,3 @@
+{"bytes":[],"subtype":null}
+{"bytes":[202,254,186,190],"subtype":null}
+{"bytes":[202,254,186,190],"subtype":42}
diff --git a/docs/examples/byte_container_with_subtype__clear_subtype.cpp b/docs/examples/byte_container_with_subtype__clear_subtype.cpp
new file mode 100644
index 0000000..f9ce684
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__clear_subtype.cpp
@@ -0,0 +1,21 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+// define a byte container based on std::vector
+using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
+
+using json = nlohmann::json;
+
+int main()
+{
+    std::vector<std::uint8_t> bytes = {{0xca, 0xfe, 0xba, 0xbe}};
+
+    // create container with subtype
+    auto c1 = byte_container_with_subtype(bytes, 42);
+
+    std::cout << "before calling clear_subtype(): " << json(c1) << '\n';
+
+    c1.clear_subtype();
+
+    std::cout << "after calling clear_subtype(): " << json(c1) << '\n';
+}
diff --git a/docs/examples/byte_container_with_subtype__clear_subtype.output b/docs/examples/byte_container_with_subtype__clear_subtype.output
new file mode 100644
index 0000000..9d82129
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__clear_subtype.output
@@ -0,0 +1,2 @@
+before calling clear_subtype(): {"bytes":[202,254,186,190],"subtype":42}
+after calling clear_subtype(): {"bytes":[202,254,186,190],"subtype":null}
diff --git a/docs/examples/byte_container_with_subtype__has_subtype.cpp b/docs/examples/byte_container_with_subtype__has_subtype.cpp
new file mode 100644
index 0000000..61c21ea
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__has_subtype.cpp
@@ -0,0 +1,19 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+// define a byte container based on std::vector
+using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
+
+int main()
+{
+    std::vector<std::uint8_t> bytes = {{0xca, 0xfe, 0xba, 0xbe}};
+
+    // create container
+    auto c1 = byte_container_with_subtype(bytes);
+
+    // create container with subtype
+    auto c2 = byte_container_with_subtype(bytes, 42);
+
+    std::cout << std::boolalpha << "c1.has_subtype() = " << c1.has_subtype()
+              << "\nc2.has_subtype() = " << c2.has_subtype() << std::endl;
+}
diff --git a/docs/examples/byte_container_with_subtype__has_subtype.output b/docs/examples/byte_container_with_subtype__has_subtype.output
new file mode 100644
index 0000000..f4aade2
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__has_subtype.output
@@ -0,0 +1,2 @@
+c1.has_subtype() = false
+c2.has_subtype() = true
diff --git a/docs/examples/byte_container_with_subtype__set_subtype.cpp b/docs/examples/byte_container_with_subtype__set_subtype.cpp
new file mode 100644
index 0000000..b2694c5
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__set_subtype.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+// define a byte container based on std::vector
+using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
+
+using json = nlohmann::json;
+
+int main()
+{
+    std::vector<std::uint8_t> bytes = {{0xca, 0xfe, 0xba, 0xbe}};
+
+    // create container without subtype
+    auto c = byte_container_with_subtype(bytes);
+
+    std::cout << "before calling set_subtype(42): " << json(c) << '\n';
+
+    // set the subtype
+    c.set_subtype(42);
+
+    std::cout << "after calling set_subtype(42): " << json(c) << '\n';
+}
diff --git a/docs/examples/byte_container_with_subtype__set_subtype.output b/docs/examples/byte_container_with_subtype__set_subtype.output
new file mode 100644
index 0000000..648b3ef
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__set_subtype.output
@@ -0,0 +1,2 @@
+before calling set_subtype(42): {"bytes":[202,254,186,190],"subtype":null}
+after calling set_subtype(42): {"bytes":[202,254,186,190],"subtype":42}
diff --git a/docs/examples/byte_container_with_subtype__subtype.cpp b/docs/examples/byte_container_with_subtype__subtype.cpp
new file mode 100644
index 0000000..cd230ad
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__subtype.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+// define a byte container based on std::vector
+using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
+
+int main()
+{
+    std::vector<std::uint8_t> bytes = {{0xca, 0xfe, 0xba, 0xbe}};
+
+    // create container
+    auto c1 = byte_container_with_subtype(bytes);
+
+    // create container with subtype
+    auto c2 = byte_container_with_subtype(bytes, 42);
+
+    std::cout << "c1.subtype() = " << c1.subtype()
+              << "\nc2.subtype() = " << c2.subtype() << std::endl;
+
+    // in case no subtype is set, return special value
+    assert(c1.subtype() == static_cast<byte_container_with_subtype::subtype_type>(-1));
+}
diff --git a/docs/examples/byte_container_with_subtype__subtype.output b/docs/examples/byte_container_with_subtype__subtype.output
new file mode 100644
index 0000000..4795527
--- /dev/null
+++ b/docs/examples/byte_container_with_subtype__subtype.output
@@ -0,0 +1,2 @@
+c1.subtype() = 18446744073709551615
+c2.subtype() = 42
diff --git a/docs/examples/cbor_tag_handler_t.cpp b/docs/examples/cbor_tag_handler_t.cpp
new file mode 100644
index 0000000..79052c7
--- /dev/null
+++ b/docs/examples/cbor_tag_handler_t.cpp
@@ -0,0 +1,28 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // tagged byte string
+    std::vector<std::uint8_t> vec = {{0xd8, 0x42, 0x44, 0xcA, 0xfe, 0xba, 0xbe}};
+
+    // cbor_tag_handler_t::error throws
+    try
+    {
+        auto b_throw_on_tag = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::error);
+    }
+    catch (json::parse_error& e)
+    {
+        std::cout << e.what() << std::endl;
+    }
+
+    // cbor_tag_handler_t::ignore ignores the tag
+    auto b_ignore_tag = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::ignore);
+    std::cout << b_ignore_tag << std::endl;
+
+    // cbor_tag_handler_t::store stores the tag as binary subtype
+    auto b_store_tag = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::store);
+    std::cout << b_store_tag << std::endl;
+}
diff --git a/docs/examples/cbor_tag_handler_t.output b/docs/examples/cbor_tag_handler_t.output
new file mode 100644
index 0000000..18920b1
--- /dev/null
+++ b/docs/examples/cbor_tag_handler_t.output
@@ -0,0 +1,3 @@
+[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0xD8
+{"bytes":[202,254,186,190],"subtype":null}
+{"bytes":[202,254,186,190],"subtype":66}
diff --git a/docs/examples/default_object_comparator_t.cpp b/docs/examples/default_object_comparator_t.cpp
new file mode 100644
index 0000000..9f200fe
--- /dev/null
+++ b/docs/examples/default_object_comparator_t.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    std::cout << std::boolalpha
+              << "one < two : " << json::default_object_comparator_t{}("one", "two") << "\n"
+              << "three < four : " << json::default_object_comparator_t{}("three", "four") << std::endl;
+}
diff --git a/docs/examples/default_object_comparator_t.output b/docs/examples/default_object_comparator_t.output
new file mode 100644
index 0000000..b1daf3b
--- /dev/null
+++ b/docs/examples/default_object_comparator_t.output
@@ -0,0 +1,2 @@
+one < two : true
+three < four : false
diff --git a/docs/examples/error_handler_t.cpp b/docs/examples/error_handler_t.cpp
new file mode 100644
index 0000000..add3f3b
--- /dev/null
+++ b/docs/examples/error_handler_t.cpp
@@ -0,0 +1,24 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON value with invalid UTF-8 byte sequence
+    json j_invalid = "ä\xA9ü";
+    try
+    {
+        std::cout << j_invalid.dump() << std::endl;
+    }
+    catch (json::type_error& e)
+    {
+        std::cout << e.what() << std::endl;
+    }
+
+    std::cout << "string with replaced invalid characters: "
+              << j_invalid.dump(-1, ' ', false, json::error_handler_t::replace)
+              << "\nstring with ignored invalid characters: "
+              << j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore)
+              << '\n';
+}
diff --git a/docs/examples/error_handler_t.output b/docs/examples/error_handler_t.output
new file mode 100644
index 0000000..718d62b
--- /dev/null
+++ b/docs/examples/error_handler_t.output
@@ -0,0 +1,3 @@
+[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9
+string with replaced invalid characters: "ä�ü"
+string with ignored invalid characters: "äü"
diff --git a/docs/examples/from_bjdata.cpp b/docs/examples/from_bjdata.cpp
new file mode 100644
index 0000000..961164c
--- /dev/null
+++ b/docs/examples/from_bjdata.cpp
@@ -0,0 +1,20 @@
+#include <iostream>
+#include <iomanip>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create byte vector
+    std::vector<std::uint8_t> v = {0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61,
+                                   0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68,
+                                   0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D
+                                  };
+
+    // deserialize it with BJData
+    json j = json::from_bjdata(v);
+
+    // print the deserialized JSON value
+    std::cout << std::setw(2) << j << std::endl;
+}
diff --git a/docs/examples/from_bjdata.output b/docs/examples/from_bjdata.output
new file mode 100644
index 0000000..259f63b
--- /dev/null
+++ b/docs/examples/from_bjdata.output
@@ -0,0 +1,4 @@
+{
+  "compact": true,
+  "schema": 0
+}
diff --git a/docs/examples/get_allocator.cpp b/docs/examples/get_allocator.cpp
new file mode 100644
index 0000000..35079a1
--- /dev/null
+++ b/docs/examples/get_allocator.cpp
@@ -0,0 +1,18 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    auto alloc = json::get_allocator();
+    using traits_t = std::allocator_traits<decltype(alloc)>;
+
+    json* j = traits_t::allocate(alloc, 1);
+    traits_t::construct(alloc, j, "Hello, world!");
+
+    std::cout << *j << std::endl;
+
+    traits_t::destroy(alloc, j);
+    traits_t::deallocate(alloc, j, 1);
+}
diff --git a/docs/examples/get_allocator.output b/docs/examples/get_allocator.output
new file mode 100644
index 0000000..8effb3e
--- /dev/null
+++ b/docs/examples/get_allocator.output
@@ -0,0 +1 @@
+"Hello, world!"
diff --git a/docs/examples/iterator_wrapper.cpp b/docs/examples/iterator_wrapper.cpp
deleted file mode 100644
index 84d3725..0000000
--- a/docs/examples/iterator_wrapper.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <iostream>
-#include <nlohmann/json.hpp>
-
-using json = nlohmann::json;
-
-int main()
-{
-    // create JSON values
-    json j_object = {{"one", 1}, {"two", 2}};
-    json j_array = {1, 2, 4, 8, 16};
-
-    //////////////////////////////////////////////////////////////////////////
-    // The static function iterator_wrapper was deprecated in version 3.1.0
-    // and will be removed in version 4.0.0. Please replace all occurrences
-    // of iterator_wrapper(j) with j.items().
-    //////////////////////////////////////////////////////////////////////////
-
-    // example for an object
-    for (auto& x : json::iterator_wrapper(j_object))
-    {
-        std::cout << "key: " << x.key() << ", value: " << x.value() << '\n';
-    }
-
-    // example for an array
-    for (auto& x : json::iterator_wrapper(j_array))
-    {
-        std::cout << "key: " << x.key() << ", value: " << x.value() << '\n';
-    }
-}
diff --git a/docs/examples/iterator_wrapper.output b/docs/examples/iterator_wrapper.output
deleted file mode 100644
index 89b09f5..0000000
--- a/docs/examples/iterator_wrapper.output
+++ /dev/null
@@ -1,7 +0,0 @@
-key: one, value: 1
-key: two, value: 2
-key: 0, value: 1
-key: 1, value: 2
-key: 2, value: 4
-key: 3, value: 8
-key: 4, value: 16
diff --git a/docs/examples/json_pointer__operator_string.cpp b/docs/examples/json_pointer__operator_string.cpp
new file mode 100644
index 0000000..56f2130
--- /dev/null
+++ b/docs/examples/json_pointer__operator_string.cpp
@@ -0,0 +1,19 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // different JSON Pointers
+    json::json_pointer ptr1("/foo/0");
+    json::json_pointer ptr2("/a~1b");
+
+    // implicit conversion to string
+    std::string s;
+    s += ptr1;
+    s += "\n";
+    s += ptr2;
+
+    std::cout << s << std::endl;
+}
diff --git a/docs/examples/json_pointer__operator_string.output b/docs/examples/json_pointer__operator_string.output
new file mode 100644
index 0000000..ec6aba2
--- /dev/null
+++ b/docs/examples/json_pointer__operator_string.output
@@ -0,0 +1,2 @@
+/foo/0
+/a~1b
diff --git a/docs/examples/json_pointer__string_t.cpp b/docs/examples/json_pointer__string_t.cpp
new file mode 100644
index 0000000..fbe0f17
--- /dev/null
+++ b/docs/examples/json_pointer__string_t.cpp
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    json::json_pointer::string_t s = "This is a string.";
+
+    std::cout << s << std::endl;
+
+    std::cout << std::boolalpha << std::is_same<json::json_pointer::string_t, json::string_t>::value << std::endl;
+}
diff --git a/docs/examples/json_pointer__string_t.output b/docs/examples/json_pointer__string_t.output
new file mode 100644
index 0000000..d871137
--- /dev/null
+++ b/docs/examples/json_pointer__string_t.output
@@ -0,0 +1,2 @@
+This is a string.
+true
diff --git a/docs/examples/json_pointer__to_string.cpp b/docs/examples/json_pointer__to_string.cpp
index da397cd..ae1361a 100644
--- a/docs/examples/json_pointer__to_string.cpp
+++ b/docs/examples/json_pointer__to_string.cpp
@@ -19,7 +19,6 @@
     json::json_pointer ptr11("/ ");
     json::json_pointer ptr12("/m~0n");
 
-
     std::cout << ptr1.to_string() << '\n'
               << ptr2.to_string() << '\n'
               << ptr3.to_string() << '\n'
diff --git a/docs/examples/nlohmann_json_version.cpp b/docs/examples/nlohmann_json_version.cpp
new file mode 100644
index 0000000..ca5f537
--- /dev/null
+++ b/docs/examples/nlohmann_json_version.cpp
@@ -0,0 +1,12 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    std::cout << "JSON for Modern C++ version "
+              << NLOHMANN_JSON_VERSION_MAJOR << "."
+              << NLOHMANN_JSON_VERSION_MINOR << "."
+              << NLOHMANN_JSON_VERSION_PATCH << std::endl;
+}
diff --git a/docs/examples/nlohmann_json_version.output b/docs/examples/nlohmann_json_version.output
new file mode 100644
index 0000000..6003439
--- /dev/null
+++ b/docs/examples/nlohmann_json_version.output
@@ -0,0 +1 @@
+JSON for Modern C++ version 3.10.5
diff --git a/docs/examples/object_comparator_t.cpp b/docs/examples/object_comparator_t.cpp
new file mode 100644
index 0000000..6b82c7c
--- /dev/null
+++ b/docs/examples/object_comparator_t.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    std::cout << std::boolalpha
+              << "json::object_comparator_t(\"one\", \"two\") = " << json::object_comparator_t{}("one", "two") << "\n"
+              << "json::object_comparator_t(\"three\", \"four\") = " << json::object_comparator_t{}("three", "four") << std::endl;
+}
diff --git a/docs/examples/object_comparator_t.output b/docs/examples/object_comparator_t.output
new file mode 100644
index 0000000..63620ed
--- /dev/null
+++ b/docs/examples/object_comparator_t.output
@@ -0,0 +1,2 @@
+json::object_comparator_t("one", "two") = true
+json::object_comparator_t("three", "four") = false
diff --git a/docs/examples/ordered_json.cpp b/docs/examples/ordered_json.cpp
new file mode 100644
index 0000000..effad53
--- /dev/null
+++ b/docs/examples/ordered_json.cpp
@@ -0,0 +1,14 @@
+#include <iostream>
+#include <nlohmann/json.hpp>
+
+using ordered_json = nlohmann::ordered_json;
+
+int main()
+{
+    ordered_json j;
+    j["one"] = 1;
+    j["two"] = 2;
+    j["three"] = 3;
+
+    std::cout << j.dump(2) << '\n';
+}
diff --git a/docs/examples/ordered_json.output b/docs/examples/ordered_json.output
new file mode 100644
index 0000000..120cbb2
--- /dev/null
+++ b/docs/examples/ordered_json.output
@@ -0,0 +1,5 @@
+{
+  "one": 1,
+  "two": 2,
+  "three": 3
+}
diff --git a/docs/examples/sax_parse.cpp b/docs/examples/sax_parse.cpp
index 45273eb..8602687 100644
--- a/docs/examples/sax_parse.cpp
+++ b/docs/examples/sax_parse.cpp
@@ -6,7 +6,7 @@
 using json = nlohmann::json;
 
 // a simple event consumer that collects string representations of the passed
-// values; not inheriting from json::json_sax_t is not required, but can
+// values; note inheriting from json::json_sax_t is not required, but can
 // help not to forget a required function
 class sax_event_consumer : public json::json_sax_t
 {
@@ -15,79 +15,79 @@
 
     bool null() override
     {
-        events.push_back("value: null");
+        events.push_back("null()");
         return true;
     }
 
     bool boolean(bool val) override
     {
-        events.push_back("value: " + std::string(val ? "true" : "false"));
+        events.push_back("boolean(val=" + std::string(val ? "true" : "false") + ")");
         return true;
     }
 
     bool number_integer(number_integer_t val) override
     {
-        events.push_back("value: " + std::to_string(val));
+        events.push_back("number_integer(val=" + std::to_string(val) + ")");
         return true;
     }
 
     bool number_unsigned(number_unsigned_t val) override
     {
-        events.push_back("value: " + std::to_string(val));
+        events.push_back("number_unsigned(val=" + std::to_string(val) + ")");
         return true;
     }
 
     bool number_float(number_float_t val, const string_t& s) override
     {
-        events.push_back("value: " + s);
+        events.push_back("number_float(val=" + std::to_string(val) + ", s=" + s + ")");
         return true;
     }
 
     bool string(string_t& val) override
     {
-        events.push_back("value: " + val);
+        events.push_back("string(val=" + val + ")");
         return true;
     }
 
     bool start_object(std::size_t elements) override
     {
-        events.push_back("start: object");
+        events.push_back("start_object(elements=" + std::to_string(elements) + ")");
         return true;
     }
 
     bool end_object() override
     {
-        events.push_back("end: object");
+        events.push_back("end_object()");
         return true;
     }
 
     bool start_array(std::size_t elements) override
     {
-        events.push_back("start: array");
+        events.push_back("start_array(elements=" + std::to_string(elements) + ")");
         return true;
     }
 
     bool end_array() override
     {
-        events.push_back("end: array");
+        events.push_back("end_array()");
         return true;
     }
 
     bool key(string_t& val) override
     {
-        events.push_back("key: " + val);
+        events.push_back("key(val=" + val + ")");
         return true;
     }
 
     bool binary(json::binary_t& val) override
     {
-        events.push_back("binary");
+        events.push_back("binary(val=[...])");
         return true;
     }
 
     bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override
     {
-        events.push_back("error: " + std::string(ex.what()));
+        events.push_back("parse_error(position=" + std::to_string(position) + ", last_token=" + last_token + ",\n            ex=" + std::string(ex.what()) + ")");
         return false;
     }
 };
@@ -107,22 +107,23 @@
                 "Width":  100
             },
             "Animated" : false,
-            "IDs": [116, 943, 234, 38793],
+            "IDs": [116, 943, 234, -38793],
+            "DeletionDate": null,
             "Distance": 12.723374634
         }
-    }
+    }]
     )";
 
     // create a SAX event consumer object
     sax_event_consumer sec;
 
-    // parse and serialize JSON
+    // parse JSON
     bool result = json::sax_parse(text, &sec);
 
     // output the recorded events
     for (auto& event : sec.events)
     {
-        std::cout << "(" << event << ") ";
+        std::cout << event << "\n";
     }
 
     // output the result of sax_parse
diff --git a/docs/examples/sax_parse.output b/docs/examples/sax_parse.output
index e16c2c4..dd2fc2f 100644
--- a/docs/examples/sax_parse.output
+++ b/docs/examples/sax_parse.output
@@ -1,2 +1,37 @@
-(start: object) (key: Image) (start: object) (key: Width) (value: 800) (key: Height) (value: 600) (key: Title) (value: View from 15th Floor) (key: Thumbnail) (start: object) (key: Url) (value: http://www.example.com/image/481989943) (key: Height) (value: 125) (key: Width) (value: 100) (end: object) (key: Animated) (value: false) (key: IDs) (start: array) (value: 116) (value: 943) (value: 234) (value: 38793) (end: array) (key: Distance) (value: 12.723374634) (end: object) (end: object) 
-result: true
+start_object(elements=18446744073709551615)
+key(val=Image)
+start_object(elements=18446744073709551615)
+key(val=Width)
+number_unsigned(val=800)
+key(val=Height)
+number_unsigned(val=600)
+key(val=Title)
+string(val=View from 15th Floor)
+key(val=Thumbnail)
+start_object(elements=18446744073709551615)
+key(val=Url)
+string(val=http://www.example.com/image/481989943)
+key(val=Height)
+number_unsigned(val=125)
+key(val=Width)
+number_unsigned(val=100)
+end_object()
+key(val=Animated)
+boolean(val=false)
+key(val=IDs)
+start_array(elements=18446744073709551615)
+number_unsigned(val=116)
+number_unsigned(val=943)
+number_unsigned(val=234)
+number_integer(val=-38793)
+end_array()
+key(val=DeletionDate)
+null()
+key(val=Distance)
+number_float(val=12.723375, s=12.723374634)
+end_object()
+end_object()
+parse_error(position=460, last_token=12.723374634<U+000A>        }<U+000A>    }],
+            ex=[json.exception.parse_error.101] parse error at line 17, column 6: syntax error while parsing value - unexpected ']'; expected end of input)
+
+result: false
diff --git a/docs/examples/sax_parse__binary.cpp b/docs/examples/sax_parse__binary.cpp
new file mode 100644
index 0000000..08bc85d
--- /dev/null
+++ b/docs/examples/sax_parse__binary.cpp
@@ -0,0 +1,114 @@
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+// a simple event consumer that collects string representations of the passed
+// values; note inheriting from json::json_sax_t is not required, but can
+// help not to forget a required function
+class sax_event_consumer : public json::json_sax_t
+{
+  public:
+    std::vector<std::string> events;
+
+    bool null() override
+    {
+        events.push_back("null()");
+        return true;
+    }
+
+    bool boolean(bool val) override
+    {
+        events.push_back("boolean(val=" + std::string(val ? "true" : "false") + ")");
+        return true;
+    }
+
+    bool number_integer(number_integer_t val) override
+    {
+        events.push_back("number_integer(val=" + std::to_string(val) + ")");
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val) override
+    {
+        events.push_back("number_unsigned(val=" + std::to_string(val) + ")");
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& s) override
+    {
+        events.push_back("number_float(val=" + std::to_string(val) + ", s=" + s + ")");
+        return true;
+    }
+
+    bool string(string_t& val) override
+    {
+        events.push_back("string(val=" + val + ")");
+        return true;
+    }
+
+    bool start_object(std::size_t elements) override
+    {
+        events.push_back("start_object(elements=" + std::to_string(elements) + ")");
+        return true;
+    }
+
+    bool end_object() override
+    {
+        events.push_back("end_object()");
+        return true;
+    }
+
+    bool start_array(std::size_t elements) override
+    {
+        events.push_back("start_array(elements=" + std::to_string(elements) + ")");
+        return true;
+    }
+
+    bool end_array() override
+    {
+        events.push_back("end_array()");
+        return true;
+    }
+
+    bool key(string_t& val) override
+    {
+        events.push_back("key(val=" + val + ")");
+        return true;
+    }
+
+    bool binary(json::binary_t& val) override
+    {
+        events.push_back("binary(val=[...])");
+        return true;
+    }
+
+    bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override
+    {
+        events.push_back("parse_error(position=" + std::to_string(position) + ", last_token=" + last_token + ",\n            ex=" + std::string(ex.what()) + ")");
+        return false;
+    }
+};
+
+int main()
+{
+    // CBOR byte string
+    std::vector<std::uint8_t> vec = {{0x44, 0xcA, 0xfe, 0xba, 0xbe}};
+
+    // create a SAX event consumer object
+    sax_event_consumer sec;
+
+    // parse CBOR
+    bool result = json::sax_parse(vec, &sec, json::input_format_t::cbor);
+
+    // output the recorded events
+    for (auto& event : sec.events)
+    {
+        std::cout << event << "\n";
+    }
+
+    // output the result of sax_parse
+    std::cout << "\nresult: " << std::boolalpha << result << std::endl;
+}
diff --git a/docs/examples/sax_parse__binary.output b/docs/examples/sax_parse__binary.output
new file mode 100644
index 0000000..f880896
--- /dev/null
+++ b/docs/examples/sax_parse__binary.output
@@ -0,0 +1,3 @@
+binary(val=[...])
+
+result: true
diff --git a/docs/examples/to_bjdata.cpp b/docs/examples/to_bjdata.cpp
new file mode 100644
index 0000000..0bfe333
--- /dev/null
+++ b/docs/examples/to_bjdata.cpp
@@ -0,0 +1,63 @@
+#include <iostream>
+#include <iomanip>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+// function to print BJData's diagnostic format
+void print_byte(uint8_t byte)
+{
+    if (32 < byte and byte < 128)
+    {
+        std::cout << (char)byte;
+    }
+    else
+    {
+        std::cout << (int)byte;
+    }
+}
+
+int main()
+{
+    // create a JSON value
+    json j = R"({"compact": true, "schema": false})"_json;
+
+    // serialize it to BJData
+    std::vector<std::uint8_t> v = json::to_bjdata(j);
+
+    // print the vector content
+    for (auto& byte : v)
+    {
+        print_byte(byte);
+    }
+    std::cout << std::endl;
+
+    // create an array of numbers
+    json array = {1, 2, 3, 4, 5, 6, 7, 8};
+
+    // serialize it to BJData using default representation
+    std::vector<std::uint8_t> v_array = json::to_bjdata(array);
+    // serialize it to BJData using size optimization
+    std::vector<std::uint8_t> v_array_size = json::to_bjdata(array, true);
+    // serialize it to BJData using type optimization
+    std::vector<std::uint8_t> v_array_size_and_type = json::to_bjdata(array, true, true);
+
+    // print the vector contents
+    for (auto& byte : v_array)
+    {
+        print_byte(byte);
+    }
+    std::cout << std::endl;
+
+    for (auto& byte : v_array_size)
+    {
+        print_byte(byte);
+    }
+    std::cout << std::endl;
+
+    for (auto& byte : v_array_size_and_type)
+    {
+        print_byte(byte);
+    }
+    std::cout << std::endl;
+}
diff --git a/docs/examples/to_bjdata.output b/docs/examples/to_bjdata.output
new file mode 100644
index 0000000..087980c
--- /dev/null
+++ b/docs/examples/to_bjdata.output
@@ -0,0 +1,4 @@
+{i7compactTi6schemaF}
+[i1i2i3i4i5i6i7i8]
+[#i8i1i2i3i4i5i6i7i8
+[$i#i812345678
diff --git a/docs/mkdocs/docs/api/basic_json/at.md b/docs/mkdocs/docs/api/basic_json/at.md
index dbd4cb6..3eea8dd 100644
--- a/docs/mkdocs/docs/api/basic_json/at.md
+++ b/docs/mkdocs/docs/api/basic_json/at.md
@@ -184,6 +184,7 @@
 
 ## See also
 
+- documentation on [checked access](../../features/element_access/checked_access.md)
 - see [`operator[]`](operator%5B%5D.md) for unchecked access by reference
 - see [`value`](value.md) for access with default value
 
diff --git a/docs/mkdocs/docs/api/basic_json/basic_json.md b/docs/mkdocs/docs/api/basic_json/basic_json.md
index 9a289d6..365cd7c 100644
--- a/docs/mkdocs/docs/api/basic_json/basic_json.md
+++ b/docs/mkdocs/docs/api/basic_json/basic_json.md
@@ -241,7 +241,7 @@
 
 - Overload 5:
 
-    !!! note
+    !!! note "Empty initializer list"
 
         When used without parentheses around an empty initializer list, `basic_json()` is called instead of this
         function, yielding the JSON `#!json null` value.
diff --git a/docs/mkdocs/docs/api/basic_json/cbor_tag_handler_t.md b/docs/mkdocs/docs/api/basic_json/cbor_tag_handler_t.md
index 6622d8a..e19c3ed 100644
--- a/docs/mkdocs/docs/api/basic_json/cbor_tag_handler_t.md
+++ b/docs/mkdocs/docs/api/basic_json/cbor_tag_handler_t.md
@@ -20,6 +20,23 @@
 store
 :   store tagged values as binary container with subtype (for bytes 0xd8..0xdb)
 
+## Examples
+
+??? example
+
+    The example below shows how the different values of the `cbor_tag_handler_t` influence the behavior of
+    [`from_cbor`](from_cbor.md) when reading a tagged byte string.
+
+    ```cpp
+    --8<-- "examples/cbor_tag_handler_t.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/cbor_tag_handler_t.output"
+    ```
+
 ## Version history
 
 - Added in version 3.9.0. Added value `store` in 3.10.0.
diff --git a/docs/mkdocs/docs/api/basic_json/contains.md b/docs/mkdocs/docs/api/basic_json/contains.md
index 67a5ffc..e6d9df8 100644
--- a/docs/mkdocs/docs/api/basic_json/contains.md
+++ b/docs/mkdocs/docs/api/basic_json/contains.md
@@ -60,8 +60,8 @@
 
 ## Notes
 
-1. This method always returns `#!cpp false` when executed on a JSON type that is not an object.
-2. This method can be executed on any JSON value type.
+- This method always returns `#!cpp false` when executed on a JSON type that is not an object.
+- This method can be executed on any JSON value type.
 
 !!! info "Postconditions"
 
diff --git a/docs/mkdocs/docs/api/basic_json/default_object_comparator_t.md b/docs/mkdocs/docs/api/basic_json/default_object_comparator_t.md
index 9e5f6c5..8a237f6 100644
--- a/docs/mkdocs/docs/api/basic_json/default_object_comparator_t.md
+++ b/docs/mkdocs/docs/api/basic_json/default_object_comparator_t.md
@@ -14,6 +14,22 @@
 The actual comparator used depends on [`object_t`](object_t.md) and can be obtained via
 [`object_comparator_t`](object_comparator_t.md).
 
+## Examples
+
+??? example
+
+    The example below demonstrates the default comparator.
+
+    ```cpp
+    --8<-- "examples/default_object_comparator_t.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/default_object_comparator_t.output"
+    ```
+
 ## Version history
 
 - Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/error_handler_t.md b/docs/mkdocs/docs/api/basic_json/error_handler_t.md
index afd20f8..dc32ced 100644
--- a/docs/mkdocs/docs/api/basic_json/error_handler_t.md
+++ b/docs/mkdocs/docs/api/basic_json/error_handler_t.md
@@ -20,6 +20,23 @@
 ignore
 :   ignore invalid UTF-8 sequences; all bytes are copied to the output unchanged
 
+## Examples
+
+??? example
+
+    The example below shows how the different values of the `error_handler_t` influence the behavior of
+    [`dump`](dump.md) when reading serializing an invalid UTF-8 sequence.
+
+    ```cpp
+    --8<-- "examples/error_handler_t.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/error_handler_t.output"
+    ```
+
 ## Version history
 
 - Added in version 3.4.0.
diff --git a/docs/mkdocs/docs/api/basic_json/from_bjdata.md b/docs/mkdocs/docs/api/basic_json/from_bjdata.md
new file mode 100644
index 0000000..3c5eeb3
--- /dev/null
+++ b/docs/mkdocs/docs/api/basic_json/from_bjdata.md
@@ -0,0 +1,93 @@
+# <small>nlohmann::basic_json::</small>from_bjdata
+
+```cpp
+// (1)
+template<typename InputType>
+static basic_json from_bjdata(InputType&& i,
+                              const bool strict = true,
+                              const bool allow_exceptions = true);
+// (2)
+template<typename IteratorType>
+static basic_json from_bjdata(IteratorType first, IteratorType last,
+                              const bool strict = true,
+                              const bool allow_exceptions = true);
+```
+
+Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
+
+1. Reads from a compatible input.
+2. Reads from an iterator range.
+
+The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bjdata.md).
+
+## Template parameters
+
+`InputType`
+:   A compatible input, for instance:
+
+    - an `std::istream` object
+    - a `FILE` pointer
+    - a C-style array of characters
+    - a pointer to a null-terminated string of single byte characters
+    - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
+
+`IteratorType`
+:   a compatible iterator type
+
+## Parameters
+
+`i` (in)
+:   an input in BJData format convertible to an input adapter
+
+`first` (in)
+:   iterator to start of the input
+
+`last` (in)
+:   iterator to end of the input
+
+`strict` (in)
+:   whether to expect the input to be consumed until EOF (`#!cpp true` by default)
+
+`allow_exceptions` (in)
+:   whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
+
+## Return value
+
+deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
+`value_t::discarded`. The latter can be checked with [`is_discarded`](is_discarded.md).
+
+## Exception safety
+
+Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
+
+## Exceptions
+
+- Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or
+  the end of file was not reached when `strict` was set to true
+- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
+- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
+  successfully
+
+## Complexity
+
+Linear in the size of the input.
+
+## Examples
+
+??? example
+
+    The example shows the deserialization of a byte vector in BJData format to a JSON value.
+     
+    ```cpp
+    --8<-- "examples/from_bjdata.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/from_bjdata.output"
+    ```
+
+## Version history
+
+- Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/get_allocator.md b/docs/mkdocs/docs/api/basic_json/get_allocator.md
index 1b7700c..07a4d84 100644
--- a/docs/mkdocs/docs/api/basic_json/get_allocator.md
+++ b/docs/mkdocs/docs/api/basic_json/get_allocator.md
@@ -10,10 +10,22 @@
 
 associated allocator
 
+## Examples
+
+??? example
+
+    The example shows how `get_allocator()` is used to created `json` values.
+    
+    ```cpp
+    --8<-- "examples/get_allocator.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/get_allocator.output"
+    ```
+
 ## Version history
 
-- Unknown.
-
-!!! note
-
-    This documentation page is a stub.
+- Added in version 1.0.0.
diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md
index 02f5c51..05b68c6 100644
--- a/docs/mkdocs/docs/api/basic_json/index.md
+++ b/docs/mkdocs/docs/api/basic_json/index.md
@@ -233,9 +233,10 @@
 - [**operator==**](operator_eq.md) - comparison: equal
 - [**operator!=**](operator_ne.md) - comparison: not equal
 - [**operator<**](operator_lt.md) - comparison: less than
-- [**operator<=**](operator_le.md) - comparison: less than or equal
 - [**operator>**](operator_gt.md) - comparison: greater than
+- [**operator<=**](operator_le.md) - comparison: less than or equal
 - [**operator>=**](operator_ge.md) - comparison: greater than or equal
+- [**operator<=>**](operator_spaceship.md) - comparison: 3-way
 
 ### Serialization / Dumping
 
@@ -268,11 +269,13 @@
 
 ### Binary formats
 
+- [**from_bjdata**](from_bjdata.md) (_static_) - create a JSON value from an input in BJData format
 - [**from_bson**](from_bson.md) (_static_) - create a JSON value from an input in BSON format
 - [**from_cbor**](from_cbor.md) (_static_) - create a JSON value from an input in CBOR format
 - [**from_msgpack**](from_msgpack.md) (_static_) - create a JSON value from an input in MessagePack format
 - [**from_ubjson**](from_ubjson.md) (_static_) - create a JSON value from an input in UBJSON format
 - [**to_bon8**](to_bon8.md) (static) - create a BON8 serialization of a given JSON value
+- [**to_bjdata**](to_bjdata.md) (_static_) - create a BJData serialization of a given JSON value
 - [**to_bson**](to_bson.md) (_static_) - create a BSON serialization of a given JSON value
 - [**to_cbor**](to_cbor.md) (_static_) - create a CBOR serialization of a given JSON value
 - [**to_msgpack**](to_msgpack.md) (_static_) - create a MessagePack serialization of a given JSON value
diff --git a/docs/mkdocs/docs/api/basic_json/input_format_t.md b/docs/mkdocs/docs/api/basic_json/input_format_t.md
index 4accf6d..a3baaba 100644
--- a/docs/mkdocs/docs/api/basic_json/input_format_t.md
+++ b/docs/mkdocs/docs/api/basic_json/input_format_t.md
@@ -6,7 +6,8 @@
     cbor,
     msgpack,
     ubjson,
-    bson
+    bson,
+    bjdata
 };
 ```
 
@@ -27,6 +28,25 @@
 bson
 :   BSON (Binary JSON)
 
+bjdata
+:   BJData (Binary JData)
+
+## Examples
+
+??? example
+
+    The example below shows how an `input_format_t` enum value is passed to `sax_parse` to set the input format to CBOR.
+
+    ```cpp
+    --8<-- "examples/sax_parse__binary.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse__binary.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/basic_json/object_comparator_t.md b/docs/mkdocs/docs/api/basic_json/object_comparator_t.md
index 6c64b64..496a562 100644
--- a/docs/mkdocs/docs/api/basic_json/object_comparator_t.md
+++ b/docs/mkdocs/docs/api/basic_json/object_comparator_t.md
@@ -1,6 +1,5 @@
 # <small>nlohmann::basic_json::</small>object_comparator_t
 
-
 ```cpp
 using object_comparator_t = typename object_t::key_compare;
 // or
@@ -10,6 +9,22 @@
 The comparator used by [`object_t`](object_t.md). Defined as `#!cpp typename object_t::key_compare` if available,
 and [`default_object_comparator_t`](default_object_comparator_t.md) otherwise.
 
+## Examples
+
+??? example
+
+    The example below demonstrates the used object comparator.
+
+    ```cpp
+    --8<-- "examples/object_comparator_t.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/object_comparator_t.output"
+    ```
+
 ## Version history
 
 - Added in version 3.0.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator+=.md b/docs/mkdocs/docs/api/basic_json/operator+=.md
index 074b300..dc5f2ec 100644
--- a/docs/mkdocs/docs/api/basic_json/operator+=.md
+++ b/docs/mkdocs/docs/api/basic_json/operator+=.md
@@ -41,12 +41,9 @@
 
 ## Exceptions
 
-1. The function can throw the following exceptions:
-    - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
-      JSON array or null; example: `"cannot use operator+=() with number"`
-2. The function can throw the following exceptions:
-    - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
-      JSON object or null; example: `"cannot use operator+=() with number"`
+All functions can throw the following exception:
+  - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
+    JSON array or null; example: `"cannot use operator+=() with number"`
 
 ## Complexity
 
diff --git "a/docs/mkdocs/docs/api/basic_json/operator\133\135.md" "b/docs/mkdocs/docs/api/basic_json/operator\133\135.md"
index cd5638b..9fa0c89 100644
--- "a/docs/mkdocs/docs/api/basic_json/operator\133\135.md"
+++ "b/docs/mkdocs/docs/api/basic_json/operator\133\135.md"
@@ -198,6 +198,8 @@
 
 ## See also
 
+- documentation on [unchecked access](../../features/element_access/unchecked_access.md)
+- documentation on [runtime assertions](../../features/assertions.md)
 - see [`at`](at.md) for access by reference with range checking
 - see [`value`](value.md) for access with default value
 
diff --git a/docs/mkdocs/docs/api/basic_json/operator_ValueType.md b/docs/mkdocs/docs/api/basic_json/operator_ValueType.md
index 7875887..7c19016 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_ValueType.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_ValueType.md
@@ -56,7 +56,6 @@
     [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) to `0` and replace any implicit
     conversions with calls to [`get`](../basic_json/get.md).
 
-
 ## Examples
 
 ??? example
diff --git a/docs/mkdocs/docs/api/basic_json/operator_eq.md b/docs/mkdocs/docs/api/basic_json/operator_eq.md
index 49f96b1..6c86bf1 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_eq.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_eq.md
@@ -1,21 +1,31 @@
 # <small>nlohmann::basic_json::</small>operator==
 
 ```cpp
-bool operator==(const_reference lhs, const_reference rhs) noexcept;
+// until C++20
+bool operator==(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator==(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator==(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator==(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator==(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
+
+// since C++20
+class basic_json {
+    bool operator==(const_reference rhs) const noexcept;              // (1)
+
+    template<typename ScalarType>
+    bool operator==(ScalarType rhs) const noexcept;                   // (2)
+};
 ```
 
-Compares two JSON values for equality according to the following rules:
+1. Compares two JSON values for equality according to the following rules:
+    - Two JSON values are equal if (1) neither value is discarded, or (2) they are of the same
+      type and their stored values are the same according to their respective `operator==`.
+    - Integer and floating-point numbers are automatically converted before comparison.
 
-- Two JSON values are equal if (1) they are not discarded, (2) they are from the same type, and (3) their stored values
-  are the same according to their respective `operator==`.
-- Integer and floating-point numbers are automatically converted before comparison. Note that two NaN values are always
-  treated as unequal.
+2. Compares a JSON value and a scalar or a scalar and a JSON value for equality by converting the
+   scalar to a JSON value and comparing both JSON values according to 1.
 
 ## Template parameters
 
@@ -32,7 +42,7 @@
 
 ## Return value
 
-whether the values `lhs` and `rhs` are equal
+whether the values `lhs`/`*this` and `rhs` are equal
 
 ## Exception safety
 
@@ -44,13 +54,17 @@
 
 ## Notes
 
-!!! note
+!!! note "Comparing special values"
 
-    - NaN values never compare equal to themselves or to other NaN values.
+    - `NaN` values are unordered within the domain of numbers.
+      The following comparisons all yield `#!cpp false`:
+        1. Comparing a `NaN` with itself.
+        2. Comparing a `NaN` with another `NaN`.
+        3. Comparing a `NaN` and any other number.
     - JSON `#!cpp null` values are all equal.
     - Discarded values never compare equal to themselves.
 
-!!! note
+!!! note "Comparing floating-point numbers"
 
     Floating-point numbers inside JSON values numbers are compared with `json::number_float_t::operator==` which is
     `double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative
@@ -117,4 +131,5 @@
 
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
+2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_ge.md b/docs/mkdocs/docs/api/basic_json/operator_ge.md
index 68aac65..6730f68 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_ge.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_ge.md
@@ -1,17 +1,25 @@
 # <small>nlohmann::basic_json::</small>operator>=
 
 ```cpp
-bool operator>=(const_reference lhs, const_reference rhs) noexcept,
+// until C++20
+bool operator>=(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator>=(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator>=(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator>=(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator>=(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
 ```
 
-Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs` by calculating
-`#!cpp !(lhs < rhs)`.
+1. Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs`
+   according to the following rules:
+    - The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
+      operand is `NaN` and the other operand is either `NaN` or any other number.
+    - Otherwise, returns the result of `#!cpp !(lhs < rhs)`.
+
+2. Compares wether a JSON value is greater than or equal to a scalar or a scalar is greater than or
+   equal to a JSON value by converting the scalar to a JSON value and comparing both JSON values
+   according to 1.
 
 ## Template parameters
 
@@ -38,6 +46,21 @@
 
 Linear.
 
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    `NaN` values are unordered within the domain of numbers.
+    The following comparisons all yield `#!cpp false`:
+      1. Comparing a `NaN` with itself.
+      2. Comparing a `NaN` with another `NaN`.
+      3. Comparing a `NaN` and any other number.
+
+!!! note "Operator overload resolution"
+
+    Since C++20 overload resolution will consider the _rewritten candidate_ generated from
+    [`operator<=>`](operator_spaceship.md).
+
 ## Examples
 
 ??? example
@@ -54,6 +77,11 @@
     --8<-- "examples/operator__greaterequal.output"
     ```
 
+## See also
+
+- [**operator<=>**](operator_spaceship.md) comparison: 3-way
+
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
+2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_gt.md b/docs/mkdocs/docs/api/basic_json/operator_gt.md
index 92ec305..540e607 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_gt.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_gt.md
@@ -1,16 +1,24 @@
 # <small>nlohmann::basic_json::</small>operator>
 
 ```cpp
-bool operator>(const_reference lhs, const_reference rhs) noexcept,
+// until C++20
+bool operator>(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator>(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator>(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator>(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator>(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
 ```
 
-Compares whether one JSON value `lhs` is greater than another JSON value `rhs` by calculating `#!cpp !(lhs <= rhs)`.
+1. Compares whether one JSON value `lhs` is greater than another JSON value `rhs` according to the
+  following rules:
+    - The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
+      operand is `NaN` and the other operand is either `NaN` or any other number.
+    - Otherwise, returns the result of `#!cpp !(lhs <= rhs)`.
+
+2. Compares wether a JSON value is greater than a scalar or a scalar is greater than a JSON value by
+   converting the scalar to a JSON value and comparing both JSON values according to 1.
 
 ## Template parameters
 
@@ -37,6 +45,21 @@
 
 Linear.
 
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    `NaN` values are unordered within the domain of numbers.
+    The following comparisons all yield `#!cpp false`:
+      1. Comparing a `NaN` with itself.
+      2. Comparing a `NaN` with another `NaN`.
+      3. Comparing a `NaN` and any other number.
+
+!!! note "Operator overload resolution"
+
+    Since C++20 overload resolution will consider the _rewritten candidate_ generated from
+    [`operator<=>`](operator_spaceship.md).
+
 ## Examples
 
 ??? example
@@ -53,6 +76,11 @@
     --8<-- "examples/operator__greater.output"
     ```
 
+## See also
+
+- [**operator<=>**](operator_spaceship.md) comparison: 3-way
+
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
+2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_le.md b/docs/mkdocs/docs/api/basic_json/operator_le.md
index 54f9a28..c0f90ac 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_le.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_le.md
@@ -1,17 +1,25 @@
 # <small>nlohmann::basic_json::</small>operator<=
 
 ```cpp
-bool operator<=(const_reference lhs, const_reference rhs) noexcept,
+// until C++20
+bool operator<=(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator<=(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator<=(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator<=(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator<=(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
 ```
 
-Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs` by calculating
-`#cpp !(rhs < lhs)`.
+1. Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs`
+   according to the following rules:
+    - The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
+      operand is `NaN` and the other operand is either `NaN` or any other number.
+    - Otherwise, returns the result of `#!cpp !(rhs < lhs)`.
+
+1. Compares wether a JSON value is less than or equal to a scalar or a scalar is less than or equal
+   to a JSON value by converting the scalar to a JSON value and comparing both JSON values according
+   to 1.
 
 ## Template parameters
 
@@ -38,6 +46,21 @@
 
 Linear.
 
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    `NaN` values are unordered within the domain of numbers.
+    The following comparisons all yield `#!cpp false`:
+      1. Comparing a `NaN` with itself.
+      2. Comparing a `NaN` with another `NaN`.
+      3. Comparing a `NaN` and any other number.
+
+!!! note "Operator overload resolution"
+
+    Since C++20 overload resolution will consider the _rewritten candidate_ generated from
+    [`operator<=>`](operator_spaceship.md).
+
 ## Examples
 
 ??? example
@@ -54,6 +77,11 @@
     --8<-- "examples/operator__lessequal.output"
     ```
 
+## See also
+
+- [**operator<=>**](operator_spaceship.md) comparison: 3-way
+
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
+2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_lt.md b/docs/mkdocs/docs/api/basic_json/operator_lt.md
index d1a4999..b5d191e 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_lt.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_lt.md
@@ -1,31 +1,34 @@
 # <small>nlohmann::basic_json::</small>operator<
 
 ```cpp
-bool operator<(const_reference lhs, const_reference rhs) noexcept;
+// until C++20
+bool operator<(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator<(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator<(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator<(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator<(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
 ```
 
-Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the following rules:
+1. Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the
+  following rules:
+    - If either operand is discarded, the comparison yields `#!cpp false`.
+    - If both operands have the same type, the values are compared using their respective `operator<`.
+    - Integer and floating-point numbers are automatically converted before comparison.
+    - In case `lhs` and `rhs` have different types, the values are ignored and the order of the types
+      is considered, which is:
+        1. null
+        2. boolean
+        3. number (all types)
+        4. object
+        5. array
+        6. string
+        7. binary
+      For instance, any boolean value is considered less than any string.
 
-- If `lhs` and `rhs` have the same type, the values are compared using the default `<` operator.
-- Integer and floating-point numbers are automatically converted before comparison
-- Discarded values a
-- In case `lhs` and `rhs` have different types, the values are ignored and the order of the types is considered, which
-  is:
-    1. null
-    2. boolean
-    3. number (all types)
-    4. object
-    5. array
-    6. string
-    7. binary
-
-    For instance, any boolean value is considered less than any string.
+2. Compares wether a JSON value is less than a scalar or a scalar is less than a JSON value by converting
+   the scalar to a JSON value and comparing both JSON values according to 1.
 
 ## Template parameters
 
@@ -52,6 +55,21 @@
 
 Linear.
 
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    `NaN` values are unordered within the domain of numbers.
+    The following comparisons all yield `#!cpp false`:
+      1. Comparing a `NaN` with itself.
+      2. Comparing a `NaN` with another `NaN`.
+      3. Comparing a `NaN` and any other number.
+
+!!! note "Operator overload resolution"
+
+    Since C++20 overload resolution will consider the _rewritten candidate_ generated from
+    [`operator<=>`](operator_spaceship.md).
+
 ## Examples
 
 ??? example
@@ -68,6 +86,11 @@
     --8<-- "examples/operator__less.output"
     ```
 
+## See also
+
+- [**operator<=>**](operator_spaceship.md) comparison: 3-way
+
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
+2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_ne.md b/docs/mkdocs/docs/api/basic_json/operator_ne.md
index e94da9b..f5d989b 100644
--- a/docs/mkdocs/docs/api/basic_json/operator_ne.md
+++ b/docs/mkdocs/docs/api/basic_json/operator_ne.md
@@ -1,16 +1,32 @@
 # <small>nlohmann::basic_json::</small>operator!=
 
 ```cpp
-bool operator!=(const_reference lhs, const_reference rhs) noexcept;
+// until C++20
+bool operator!=(const_reference lhs, const_reference rhs) noexcept;   // (1)
 
 template<typename ScalarType>
-bool operator!=(const_reference lhs, const ScalarType rhs) noexcept;
+bool operator!=(const_reference lhs, const ScalarType rhs) noexcept;  // (2)
 
 template<typename ScalarType>
-bool operator!=(ScalarType lhs, const const_reference rhs) noexcept;
+bool operator!=(ScalarType lhs, const const_reference rhs) noexcept;  // (2)
+
+// since C++20
+class basic_json {
+    bool operator!=(const_reference rhs) const noexcept;              // (1)
+
+    template<typename ScalarType>
+    bool operator!=(ScalarType rhs) const noexcept;                   // (2)
+};
 ```
 
-Compares two JSON values for inequality by calculating `#!cpp !(lhs == rhs)`.
+1. Compares two JSON values for inequality according to the following rules:
+    - The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
+      operand is `NaN` and the other operand is either `NaN` or any other number.
+    - Otherwise, returns the result of `#!cpp !(lhs == rhs)` (until C++20) or
+      `#!cpp !(*this == rhs)` (since C++20).
+
+2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the
+   scalar to a JSON value and comparing both JSON values according to 1.
 
 ## Template parameters
 
@@ -27,7 +43,7 @@
 
 ## Return value
 
-whether the values `lhs` and `rhs` are not equal
+whether the values `lhs`/`*this` and `rhs` are not equal
 
 ## Exception safety
 
@@ -37,6 +53,16 @@
 
 Linear.
 
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    `NaN` values are unordered within the domain of numbers.
+    The following comparisons all yield `#!cpp false`:
+      1. Comparing a `NaN` with itself.
+      2. Comparing a `NaN` with another `NaN`.
+      3. Comparing a `NaN` and any other number.
+
 ## Examples
 
 ??? example
@@ -69,4 +95,5 @@
 
 ## Version history
 
-- Added in version 1.0.0.
+1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
+2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/operator_spaceship.md b/docs/mkdocs/docs/api/basic_json/operator_spaceship.md
new file mode 100644
index 0000000..4beba4f
--- /dev/null
+++ b/docs/mkdocs/docs/api/basic_json/operator_spaceship.md
@@ -0,0 +1,70 @@
+# <small>nlohmann::basic_json::</small>operator<=>
+
+```cpp
+// since C++20
+class basic_json {
+    std::partial_ordering operator<=>(const_reference rhs) const noexcept;  // (1)
+
+    template<typename ScalarType>
+    std::partial_ordering operator<=>(const ScalarType rhs) const noexcept; // (2)
+};
+```
+
+1. 3-way compares two JSON values producing a result of type `std::partial_ordering` according to the following rules:
+    - Two JSON values compare with a result of `std::partial_ordering::unordered` if either value is discarded.
+    - If both JSON values are of the same type, the result is produced by 3-way comparing their stored values using their
+      respective `operator<=>`.
+    - Integer and floating-point numbers are converted to their common type and then 3-way compared using their respective
+      `operator<=>`.
+      For instance, comparing an integer and a floating-point value will 3-way compare the first value convertered to
+      floating-point with the second value.
+    - Otherwise, yields a result by comparing the type (see [`value_t`](value_t.md)).
+  
+2. 3-way compares a JSON value and a scalar or a scalar and a JSON value by converting the scalar to a JSON value and 3-way
+   comparing both JSON values (see 1).
+
+## Template parameters
+
+`ScalarType`
+:   a scalar type according to `std::is_scalar<ScalarType>::value`
+
+## Parameters
+
+`rhs` (in)
+:   second value to consider 
+
+## Return value
+
+the `std::partial_ordering` of the 3-way comparison of `*this` and `rhs`
+
+## Exception safety
+
+No-throw guarantee: this function never throws exceptions.
+
+## Complexity
+
+Linear.
+
+## Notes
+
+!!! note "Comparing `NaN`"
+
+    - `NaN` values are unordered within the domain of numbers.
+      The following comparisons all yield `std::partial_ordering::unordered`:
+        1. Comparing a `NaN` with itself.
+        2. Comparing a `NaN` with another `NaN`.
+        3. Comparing a `NaN` and any other number.
+
+## See also
+
+- [**operator==**](operator_eq.md) - comparison: equal
+- [**operator!=**](operator_ne.md) - comparison: not equal
+- [**operator<**](operator_lt.md) - comparison: less than
+- [**operator<=**](operator_le.md) - comparison: less than or equal
+- [**operator>**](operator_gt.md) - comparison: greater than
+- [**operator>=**](operator_ge.md) - comparison: greater than or equal
+
+## Version history
+
+1. Added in version 3.11.0.
+2. Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/push_back.md b/docs/mkdocs/docs/api/basic_json/push_back.md
index 60bfed3..5c7d20d 100644
--- a/docs/mkdocs/docs/api/basic_json/push_back.md
+++ b/docs/mkdocs/docs/api/basic_json/push_back.md
@@ -37,12 +37,9 @@
 
 ## Exceptions
 
-1. The function can throw the following exceptions:
-    - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
-      JSON array or null; example: `"cannot use push_back() with number"`
-2. The function can throw the following exceptions:
-    - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
-      JSON object or null; example: `"cannot use push_back() with number"`
+All functions can throw the following exception:
+  - Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) when called on a type other than
+    JSON array or null; example: `"cannot use push_back() with number"`
 
 ## Complexity
 
diff --git a/docs/mkdocs/docs/api/basic_json/to_bjdata.md b/docs/mkdocs/docs/api/basic_json/to_bjdata.md
new file mode 100644
index 0000000..1ea8505
--- /dev/null
+++ b/docs/mkdocs/docs/api/basic_json/to_bjdata.md
@@ -0,0 +1,70 @@
+# <small>nlohmann::basic_json::</small>to_bjdata
+
+```cpp
+// (1)
+static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+                                           const bool use_size = false,
+                                           const bool use_type = false);
+
+// (2)
+static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                      const bool use_size = false, const bool use_type = false);
+static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+                      const bool use_size = false, const bool use_type = false);
+```
+
+Serializes a given JSON value `j` to a byte vector using the BJData (Binary JData) serialization format. BJData
+aims to be more compact than JSON itself, yet more efficient to parse.
+
+1. Returns a byte vector containing the BJData serialization.
+2. Writes the BJData serialization to an output adapter.
+
+The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bjdata.md).
+
+## Parameters
+
+`j` (in)
+:   JSON value to serialize
+
+`o` (in)
+:   output adapter to write serialization to
+
+`use_size` (in)
+:   whether to add size annotations to container types; optional, `#!cpp false` by default.
+
+`use_type` (in)
+:   whether to add type annotations to container types (must be combined with `#!cpp use_size = true`); optional,
+`#!cpp false` by default.
+
+## Return value
+
+1. BJData serialization as byte vector
+2. (none)
+
+## Exception safety
+
+Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
+
+## Complexity
+
+Linear in the size of the JSON value `j`.
+
+## Examples
+
+??? example
+
+    The example shows the serialization of a JSON value to a byte vector in BJData format.
+     
+    ```cpp
+    --8<-- "examples/to_bjdata.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/to_bjdata.output"
+    ```
+
+## Version history
+
+- Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/basic_json/value_t.md b/docs/mkdocs/docs/api/basic_json/value_t.md
index 768b13c..f835740 100644
--- a/docs/mkdocs/docs/api/basic_json/value_t.md
+++ b/docs/mkdocs/docs/api/basic_json/value_t.md
@@ -24,10 +24,57 @@
 
 ## Notes
 
-There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library
-distinguishes these three types for numbers: [`number_unsigned_t`](number_unsigned_t.md) is used for unsigned integers,
-[`number_integer_t`](number_integer_t.md) is used for signed integers, and [`number_float_t`](number_float_t.md) is used
-for floating-point numbers or to approximate integers which do not fit in the limits of their respective type.
+!!! note "Ordering"
+
+    The order of types is as follows:
+
+    1. `null`
+    2. `boolean`
+    3. `number_integer`, `number_unsigned`, `number_float`
+    4. `object`
+    5. `array`
+    6. `string`
+    7. `binary`
+
+    `discarded` is unordered.
+
+!!! note "Types of numbers"
+
+    There are three enumerators for numbers (`number_integer`, `number_unsigned`, and `number_float`) to distinguish
+    between different types of numbers:
+
+      - [`number_unsigned_t`](number_unsigned_t.md) for unsigned integers
+      - [`number_integer_t`](number_integer_t.md) for signed integers
+      - [`number_float_t`](number_float_t.md) for floating-point numbers or to approximate integers which do not fit
+        into the limits of their respective type
+
+!!! warning "Comparison operators"
+
+    `operator<` and `operator<=>` (since C++20) are overloaded and compare according to the ordering described above.
+    Until C++20 all other relational and equality operators yield results according to the integer value of each
+    enumerator.
+    Since C++20 some compilers consider the _rewritten candidates_ generated from `operator<=>` during overload
+    resolution, while others do not.
+    For predictable and portable behavior use:
+
+      - `operator<` or `operator<=>` when wanting to compare according to the order described above
+      - `operator==` or `operator!=` when wanting to compare according to each enumerators integer value
+
+## Examples
+
+??? example
+
+    The following code how `type()` queries the `value_t` for all JSON types.
+    
+    ```cpp
+    --8<-- "examples/type.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/type.output"
+    ```
 
 ## Version history
 
diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md
index caa273d..9913a9b 100644
--- a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md
+++ b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md
@@ -25,6 +25,22 @@
 `subtype` (in)
 :   subtype
 
+## Examples
+
+??? example
+
+    The example below demonstrates how byte containers can be created.
+
+    ```cpp
+    --8<-- "examples/byte_container_with_subtype__byte_container_with_subtype.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/byte_container_with_subtype__byte_container_with_subtype.output"
+    ```
+
 ## Version history
 
 Since version 3.8.0.
diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md
index 56f8ee0..c62dead 100644
--- a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md
+++ b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md
@@ -15,6 +15,22 @@
 
 Constant.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how `clear_subtype` can remove subtypes.
+
+    ```cpp
+    --8<-- "examples/byte_container_with_subtype__clear_subtype.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/byte_container_with_subtype__clear_subtype.output"
+    ```
+
 ## Version history
 
 Since version 3.8.0.
diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md
index 2fe4181..e06286e 100644
--- a/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md
+++ b/docs/mkdocs/docs/api/byte_container_with_subtype/has_subtype.md
@@ -18,6 +18,22 @@
 
 Constant.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how `has_subtype` can check whether a subtype was set.
+
+    ```cpp
+    --8<-- "examples/byte_container_with_subtype__has_subtype.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/byte_container_with_subtype__has_subtype.output"
+    ```
+
 ## Version history
 
 Since version 3.8.0.
diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md
index 40cc272..cf21732 100644
--- a/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md
+++ b/docs/mkdocs/docs/api/byte_container_with_subtype/set_subtype.md
@@ -20,6 +20,22 @@
 
 Constant.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how a subtype can be set with `set_subtype`.
+
+    ```cpp
+    --8<-- "examples/byte_container_with_subtype__set_subtype.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/byte_container_with_subtype__set_subtype.output"
+    ```
+
 ## Version history
 
 Since version 3.8.0.
diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/subtype.md
index e78654b..389241a 100644
--- a/docs/mkdocs/docs/api/byte_container_with_subtype/subtype.md
+++ b/docs/mkdocs/docs/api/byte_container_with_subtype/subtype.md
@@ -19,6 +19,23 @@
 
 Constant.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how the subtype can be retrieved with `subtype`. Note how `subtype_type(-1)` is
+    returned for container `c1`.
+
+    ```cpp
+    --8<-- "examples/byte_container_with_subtype__subtype.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/byte_container_with_subtype__subtype.output"
+    ```
+
 ## Version history
 
 - Added in version 3.8.0
diff --git a/docs/mkdocs/docs/api/json.md b/docs/mkdocs/docs/api/json.md
index 48d3441..36edcc2 100644
--- a/docs/mkdocs/docs/api/json.md
+++ b/docs/mkdocs/docs/api/json.md
@@ -7,6 +7,22 @@
 This type is the default specialization of the [basic_json](basic_json/index.md) class which uses the standard template
 types.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how to use the type `nlohmann::json`.
+
+    ```cpp
+    --8<-- "examples/README.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/README.output"
+    ```
+
 ## Version history
 
 Since version 1.0.0.
diff --git a/docs/mkdocs/docs/api/json_pointer/index.md b/docs/mkdocs/docs/api/json_pointer/index.md
index dca9c38..dc07d89 100644
--- a/docs/mkdocs/docs/api/json_pointer/index.md
+++ b/docs/mkdocs/docs/api/json_pointer/index.md
@@ -14,11 +14,11 @@
 `RefStringType`
 :   the string type used for the reference tokens making up the JSON pointer
 
-## Notes
+!!! warning "Deprecation"
 
-For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md) in
-which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is deprecated
-and may be removed in a future major version.
+    For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md)
+    in which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is
+    deprecated and may be removed in a future major version.
 
 ## Member types
 
diff --git a/docs/mkdocs/docs/api/json_pointer/operator_string.md b/docs/mkdocs/docs/api/json_pointer/operator_string.md
index 836728c..f2f99ca 100644
--- a/docs/mkdocs/docs/api/json_pointer/operator_string.md
+++ b/docs/mkdocs/docs/api/json_pointer/operator_string.md
@@ -19,6 +19,22 @@
 }
 ```
 
+## Examples
+
+??? example
+
+    The example shows how JSON Pointers can be implicitly converted to strings.
+     
+    ```cpp
+    --8<-- "examples/json_pointer__operator_string.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/json_pointer__operator_string.output"
+    ```
+
 ## Version history
 
 - Since version 2.0.0.
diff --git a/docs/mkdocs/docs/api/json_pointer/string_t.md b/docs/mkdocs/docs/api/json_pointer/string_t.md
index f18b51b..c8527bc 100644
--- a/docs/mkdocs/docs/api/json_pointer/string_t.md
+++ b/docs/mkdocs/docs/api/json_pointer/string_t.md
@@ -7,6 +7,22 @@
 
 See [`basic_json::string_t`](../basic_json/string_t.md) for more information.
 
+## Examples
+
+??? example
+
+    The example shows the type `string_t` and its relation to `basic_json::string_t`.
+     
+    ```cpp
+    --8<-- "examples/json_pointer__string_t.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/json_pointer__string_t.output"
+    ```
+
 ## Version history
 
 - Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/json_sax/binary.md b/docs/mkdocs/docs/api/json_sax/binary.md
index e9a1d39..753e99c 100644
--- a/docs/mkdocs/docs/api/json_sax/binary.md
+++ b/docs/mkdocs/docs/api/json_sax/binary.md
@@ -19,6 +19,22 @@
 
 It is safe to move the passed binary value.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse__binary.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse__binary.output"
+    ```
+
 ## Version history
 
 - Added in version 3.8.0.
diff --git a/docs/mkdocs/docs/api/json_sax/boolean.md b/docs/mkdocs/docs/api/json_sax/boolean.md
index a5a8ddc..78163b3 100644
--- a/docs/mkdocs/docs/api/json_sax/boolean.md
+++ b/docs/mkdocs/docs/api/json_sax/boolean.md
@@ -15,6 +15,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/end_array.md b/docs/mkdocs/docs/api/json_sax/end_array.md
index 245cf97..46b4c7a 100644
--- a/docs/mkdocs/docs/api/json_sax/end_array.md
+++ b/docs/mkdocs/docs/api/json_sax/end_array.md
@@ -10,6 +10,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/end_object.md b/docs/mkdocs/docs/api/json_sax/end_object.md
index 5654e26..8df0ab4 100644
--- a/docs/mkdocs/docs/api/json_sax/end_object.md
+++ b/docs/mkdocs/docs/api/json_sax/end_object.md
@@ -10,6 +10,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/key.md b/docs/mkdocs/docs/api/json_sax/key.md
index 5e79272..ebd3ae2 100644
--- a/docs/mkdocs/docs/api/json_sax/key.md
+++ b/docs/mkdocs/docs/api/json_sax/key.md
@@ -19,6 +19,22 @@
 
 It is safe to move the passed object key value.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/null.md b/docs/mkdocs/docs/api/json_sax/null.md
index bcbb4a3..71b2a45 100644
--- a/docs/mkdocs/docs/api/json_sax/null.md
+++ b/docs/mkdocs/docs/api/json_sax/null.md
@@ -10,6 +10,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/number_float.md b/docs/mkdocs/docs/api/json_sax/number_float.md
index 9419328..e1b3fb6 100644
--- a/docs/mkdocs/docs/api/json_sax/number_float.md
+++ b/docs/mkdocs/docs/api/json_sax/number_float.md
@@ -18,6 +18,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/number_integer.md b/docs/mkdocs/docs/api/json_sax/number_integer.md
index 00ff2ea..8628a65 100644
--- a/docs/mkdocs/docs/api/json_sax/number_integer.md
+++ b/docs/mkdocs/docs/api/json_sax/number_integer.md
@@ -15,6 +15,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/number_unsigned.md b/docs/mkdocs/docs/api/json_sax/number_unsigned.md
index 66d0bda..7b59748 100644
--- a/docs/mkdocs/docs/api/json_sax/number_unsigned.md
+++ b/docs/mkdocs/docs/api/json_sax/number_unsigned.md
@@ -15,6 +15,22 @@
 
 Whether parsing should proceed.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/parse_error.md b/docs/mkdocs/docs/api/json_sax/parse_error.md
index 00f1aa9..d4405d0 100644
--- a/docs/mkdocs/docs/api/json_sax/parse_error.md
+++ b/docs/mkdocs/docs/api/json_sax/parse_error.md
@@ -23,6 +23,22 @@
 
 Whether parsing should proceed (**must return `#!cpp false`**).
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/start_array.md b/docs/mkdocs/docs/api/json_sax/start_array.md
index d10a6e5..8ef10a4 100644
--- a/docs/mkdocs/docs/api/json_sax/start_array.md
+++ b/docs/mkdocs/docs/api/json_sax/start_array.md
@@ -19,6 +19,22 @@
 
 Binary formats may report the number of elements.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/start_object.md b/docs/mkdocs/docs/api/json_sax/start_object.md
index fd90f19..24fedff 100644
--- a/docs/mkdocs/docs/api/json_sax/start_object.md
+++ b/docs/mkdocs/docs/api/json_sax/start_object.md
@@ -19,6 +19,22 @@
 
 Binary formats may report the number of elements.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/json_sax/string.md b/docs/mkdocs/docs/api/json_sax/string.md
index 113fe13..1cb6014 100644
--- a/docs/mkdocs/docs/api/json_sax/string.md
+++ b/docs/mkdocs/docs/api/json_sax/string.md
@@ -19,6 +19,22 @@
 
 It is safe to move the passed string value.
 
+## Examples
+
+??? example
+
+    .The example below shows how the SAX interface is used.
+
+    ```cpp
+    --8<-- "examples/sax_parse.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/sax_parse.output"
+    ```
+
 ## Version history
 
 - Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md
index 56924da..d95869b 100644
--- a/docs/mkdocs/docs/api/macros/index.md
+++ b/docs/mkdocs/docs/api/macros/index.md
@@ -17,6 +17,8 @@
 
 - [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard
 - [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support
+- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
+- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
 - [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
 - [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
 
@@ -27,8 +29,15 @@
 
 ## Type conversions
 
+- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
 - [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
 
+<!-- comment-->
+## Comparison behavior
+
+- [**JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON**](json_use_legacy_discarded_value_comparison.md) -
+  control comparison of discarded values
+
 ## Serialization/deserialization macros
 
 - [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**<br>**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)**](nlohmann_define_type_intrusive.md) - serialization/deserialization of types _with_ access to private variables
diff --git a/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md
new file mode 100644
index 0000000..ea291f9
--- /dev/null
+++ b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md
@@ -0,0 +1,135 @@
+# JSON_DISABLE_ENUM_SERIALIZATION
+
+```cpp
+#define JSON_DISABLE_ENUM_SERIALIZATION
+```
+
+When defined, default serialization and deserialization functions for enums are excluded and have to be provided by the user, for example, using [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) (see [arbitrary type conversions](../../features/arbitrary_types.md) for more details).
+
+Parsing or serializing an enum will result in a compiler error.
+
+This works for both unscoped and scoped enums.
+
+## Default definition
+
+By default, `#!cpp JSON_DISABLE_ENUM_SERIALIZATION` is not defined.
+
+```cpp
+#undef JSON_DISABLE_ENUM_SERIALIZATION
+```
+
+## Examples
+
+??? example "Example 1: Disabled behavior"
+
+    The code below forces the library **not** to create default serialization/deserialization functions `from_json` and `to_json`, meaning the code below **does not** compile.
+
+    ```cpp
+    #define JSON_DISABLE_ENUM_SERIALIZATION 1
+    #include <nlohmann/json.hpp>
+
+    using json = nlohmann::json;
+
+    enum class Choice
+    {
+        first,
+        second,
+    };
+    
+    int main()
+    {
+        // normally invokes to_json serialization function but with JSON_DISABLE_ENUM_SERIALIZATION defined, it does not
+        const json j = Choice::first; 
+
+        // normally invokes from_json parse function but with JSON_DISABLE_ENUM_SERIALIZATION defined, it does not
+        Choice ch = j.get<Choice>();
+    }
+    ```
+
+??? example "Example 2: Serialize enum macro"
+
+    The code below forces the library **not** to create default serialization/deserialization functions `from_json` and `to_json`, but uses [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) to parse and serialize the enum.
+
+    ```cpp
+    #define JSON_DISABLE_ENUM_SERIALIZATION 1
+    #include <nlohmann/json.hpp>
+
+    using json = nlohmann::json;
+
+    enum class Choice
+    {
+        first,
+        second,
+    };
+
+    NLOHMANN_JSON_SERIALIZE_ENUM(Choice,
+    {
+        { Choice::first, "first" },
+        { Choice::second, "second" },
+    })
+    
+    int main()
+    {
+        // uses user-defined to_json function defined by macro
+        const json j = Choice::first; 
+
+        // uses user-defined from_json function defined by macro
+        Choice ch = j.get<Choice>();
+    }
+    ```
+
+??? example "Example 3: User-defined serialization/deserialization functions"
+
+    The code below forces the library **not** to create default serialization/deserialization functions `from_json` and `to_json`, but uses user-defined functions to parse and serialize the enum.
+
+    ```cpp
+    #define JSON_DISABLE_ENUM_SERIALIZATION 1
+    #include <nlohmann/json.hpp>
+
+    using json = nlohmann::json;
+
+    enum class Choice
+    {
+        first,
+        second,
+    };
+
+    void from_json(const json& j, Choice& ch)
+    {
+        auto value = j.get<std::string>();
+        if (value == "first")
+        {
+            ch = Choice::first;
+        }
+        else if (value == "second")
+        {
+            ch = Choice::second;
+        }
+    }
+
+    void to_json(json& j, const Choice& ch)
+    {
+        auto value = j.get<std::string>();
+        if (value == "first")
+        {
+            ch = Choice::first;
+        }
+        else if (value == "second")
+        {
+            ch = Choice::second;
+        }
+    }
+    
+    int main()
+    {
+        // uses user-defined to_json function
+        const json j = Choice::first; 
+
+        // uses user-defined from_json function
+        Choice ch = j.get<Choice>();
+    }
+    ```
+
+## See also
+
+- [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md)
\ No newline at end of file
diff --git a/docs/mkdocs/docs/api/macros/json_has_cpp_11.md b/docs/mkdocs/docs/api/macros/json_has_cpp_11.md
index 3bee843..f3eaa58 100644
--- a/docs/mkdocs/docs/api/macros/json_has_cpp_11.md
+++ b/docs/mkdocs/docs/api/macros/json_has_cpp_11.md
@@ -23,6 +23,19 @@
 - `#!cpp JSON_HAS_CPP_11` is always defined.
 - All macros are undefined outside the library.
 
+## Examples
+
+??? example
+
+    The code below forces the library to use the C++14 standard:
+
+    ```cpp
+    #define JSON_HAS_CPP_14 1
+    #include <nlohmann/json.hpp>
+
+    ...
+    ```
+
 ## Version history
 
 - Added in version 3.10.5.
diff --git a/docs/mkdocs/docs/api/macros/json_has_filesystem.md b/docs/mkdocs/docs/api/macros/json_has_filesystem.md
index 160ad8c..308aea2 100644
--- a/docs/mkdocs/docs/api/macros/json_has_filesystem.md
+++ b/docs/mkdocs/docs/api/macros/json_has_filesystem.md
@@ -25,6 +25,19 @@
   filesystem support.
 - Both macros are undefined outside the library.
 
+## Examples
+
+??? example
+
+    The code below forces the library to use the header `<experimental/filesystem>`.
+
+    ```cpp
+    #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+    #include <nlohmann/json.hpp>
+
+    ...
+    ```
+
 ## Version history
 
 - Added in version 3.10.5.
diff --git a/docs/mkdocs/docs/api/macros/json_has_ranges.md b/docs/mkdocs/docs/api/macros/json_has_ranges.md
new file mode 100644
index 0000000..ae59697
--- /dev/null
+++ b/docs/mkdocs/docs/api/macros/json_has_ranges.md
@@ -0,0 +1,18 @@
+# JSON_HAS_RANGES
+
+```cpp
+#define JSON_HAS_RANGES /* value */
+```
+
+This macro indicates whether the standard library has any support for ranges. Implies support for concepts.
+Possible values are `1` when supported or `0` when unsupported.
+
+## Default definition
+
+The default value is detected based on the preprocessor macro `#!cpp __cpp_lib_ranges`.
+
+When the macro is not defined, the library will define it to its default value.
+
+## Version history
+
+- Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/macros/json_has_three_way_comparison.md b/docs/mkdocs/docs/api/macros/json_has_three_way_comparison.md
new file mode 100644
index 0000000..fc1dcb4
--- /dev/null
+++ b/docs/mkdocs/docs/api/macros/json_has_three_way_comparison.md
@@ -0,0 +1,19 @@
+# JSON_HAS_THREE_WAY_COMPARISON
+
+```cpp
+#define JSON_HAS_THREE_WAY_COMPARISON /* value */
+```
+
+This macro indicates whether the compiler and standard library support 3-way comparison.
+Possible values are `1` when supported or `0` when unsupported.
+
+## Default definition
+
+The default value is detected based on the preprocessor macros `#!cpp __cpp_impl_three_way_comparison`
+and `#!cpp __cpp_lib_three_way_comparison`.
+
+When the macro is not defined, the library will define it to its default value.
+
+## Version history
+
+- Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/macros/json_no_io.md b/docs/mkdocs/docs/api/macros/json_no_io.md
index 10ae24c..ef37384 100644
--- a/docs/mkdocs/docs/api/macros/json_no_io.md
+++ b/docs/mkdocs/docs/api/macros/json_no_io.md
@@ -16,6 +16,20 @@
 #undef JSON_NO_IO
 ```
 
+## Examples
+
+??? example
+
+    The code below forces the library not to use the headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and
+    `<ostream>`.
+
+    ```cpp
+    #define JSON_NO_IO 1
+    #include <nlohmann/json.hpp>
+
+    ...
+    ```
+
 ## Version history
 
 - Added in version 3.10.0.
diff --git a/docs/mkdocs/docs/api/macros/json_noexception.md b/docs/mkdocs/docs/api/macros/json_noexception.md
index 0f32b63..c801b85 100644
--- a/docs/mkdocs/docs/api/macros/json_noexception.md
+++ b/docs/mkdocs/docs/api/macros/json_noexception.md
@@ -23,6 +23,19 @@
 The explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not
 available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824).
 
+## Examples
+
+??? example
+
+    The code below switches off exceptions in the library.
+
+    ```cpp
+    #define JSON_NOEXCEPTION 1
+    #include <nlohmann/json.hpp>
+
+    ...
+    ```
+
 ## See also
 
 - [Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for more information how to switch off exceptions
diff --git a/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md b/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md
index c58d0ea..374fa4c 100644
--- a/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md
+++ b/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md
@@ -15,6 +15,19 @@
 #undef JSON_SKIP_UNSUPPORTED_COMPILER_CHECK
 ```
 
+## Examples
+
+??? example
+
+    The code below switches off the check whether the compiler is supported.
+
+    ```cpp
+    #define JSON_SKIP_UNSUPPORTED_COMPILER_CHECK 1
+    #include <nlohmann/json.hpp>
+
+    ...
+    ```
+
 ## Version history
 
 Added in version 3.2.0.
diff --git a/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md b/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md
new file mode 100644
index 0000000..4f630db
--- /dev/null
+++ b/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md
@@ -0,0 +1,61 @@
+# JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+
+```cpp
+#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON /* value */
+```
+
+This macro enables the (incorrect) legacy comparison behavior of discarded JSON values.
+Possible values are `1` to enable or `0` to disable (default).
+
+When enabled, comparisons involving at least one discarded JSON value yield results as follows:
+
+| **Operator** | **Result**    |
+|--------------|---------------|
+| `==`         | `#!cpp false` |
+| `!=`         | `#!cpp true`  |
+| `<`          | `#!cpp false` |
+| `<=`         | `#!cpp true`  |
+| `>=`         | `#!cpp true`  |
+| `>`          | `#!cpp false` |
+
+Otherwise, comparisons involving at least one discarded JSON value always yield `#!cpp false`.
+
+## Default definition
+
+The default value is `0`.
+
+```cpp
+#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+```
+
+When the macro is not defined, the library will define it to its default value.
+
+## Notes
+
+!!! warning "Inconsistent behavior in C++20 and beyond"
+
+    When targeting C++20 or above, enabling the legacy comparison behavior is _strongly_
+    discouraged.
+
+      - The 3-way comparison operator (`<=>`) will always give the correct result
+        (`#!cpp std::partial_ordering::unordered`) regardless of the value of
+        `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`.
+      - Overloads for the equality and relational operators emulate the legacy behavior.
+
+    Code outside your control may use either 3-way comparison or the equality and
+    relational operators, resulting in inconsistent and unpredictable behavior.
+
+    See [`operator<=>`](../basic_json/operator_spaceship.md) for more information on 3-way
+    comparison.
+
+!!! warning "Deprecation"
+
+    The legacy comparison behavior is deprecated and may be removed in a future major
+    version release.
+
+    New code should not depend on it and existing code should try to remove or rewrite
+    expressions relying on it.
+
+## Version history
+
+- Added in version 3.11.0.
diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md
index 7b0f898..b7204a8 100644
--- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md
+++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md
@@ -78,6 +78,7 @@
 ## See also
 
 - [Specializing enum conversion](../../features/enum_conversion.md)
+- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
 
 ## Version history
 
diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_version_major.md b/docs/mkdocs/docs/api/macros/nlohmann_json_version_major.md
index 8267852..d7a3142 100644
--- a/docs/mkdocs/docs/api/macros/nlohmann_json_version_major.md
+++ b/docs/mkdocs/docs/api/macros/nlohmann_json_version_major.md
@@ -13,6 +13,23 @@
 
 The macros are defined according to the current library version.
 
+## Examples
+
+??? example
+
+    The example below shows how `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, and
+    `NLOHMANN_JSON_VERSION_PATCH` are defined by the library.
+
+    ```cpp
+    --8<-- "examples/nlohmann_json_version.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/nlohmann_json_version.output"
+    ```
+
 ## See also
 
 - [meta](../basic_json/meta.md) - returns version information on the library
diff --git a/docs/mkdocs/docs/api/ordered_json.md b/docs/mkdocs/docs/api/ordered_json.md
index 8b122f9..7cfd9f4 100644
--- a/docs/mkdocs/docs/api/ordered_json.md
+++ b/docs/mkdocs/docs/api/ordered_json.md
@@ -6,9 +6,26 @@
 
 This type preserves the insertion order of object keys.
 
+## Examples
+
+??? example
+
+    The example below demonstrates how `ordered_json` preserves the insertion order of object keys.
+
+    ```cpp
+    --8<-- "examples/ordered_json.cpp"
+    ```
+    
+    Output:
+    
+    ```json
+    --8<-- "examples/ordered_json.output"
+    ```
+
 ## See also
 
 - [ordered_map](ordered_map.md)
+- [Object Order](../features/object_order.md)
 
 ## Version history
 
diff --git a/docs/mkdocs/docs/css/custom.css b/docs/mkdocs/docs/css/custom.css
new file mode 100644
index 0000000..7a1008b
--- /dev/null
+++ b/docs/mkdocs/docs/css/custom.css
@@ -0,0 +1,4 @@
+/* disable ligatures in code and preformatted blocks */
+code, pre {
+    font-variant-ligatures: none;
+}
diff --git a/docs/mkdocs/docs/features/binary_formats/bjdata.md b/docs/mkdocs/docs/features/binary_formats/bjdata.md
new file mode 100644
index 0000000..fae5565
--- /dev/null
+++ b/docs/mkdocs/docs/features/binary_formats/bjdata.md
@@ -0,0 +1,209 @@
+# BJData
+
+The [BJData format](https://neurojson.org) was derived from and improved upon
+[Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12).
+Specifically, it introduces an optimized array container for efficient storage
+of N-dimensional packed arrays (**ND-arrays**); it also adds 4 new type markers -
+`[u] - uint16`, `[m] - uint32`, `[M] - uint64` and `[h] - float16` - to
+unambigiously map common binary numeric types; furthermore, it uses little-endian
+(LE) to store all numerics instead of big-endian (BE) as in UBJSON to avoid
+unnecessary conversions on commonly available platforms.
+
+Compared to other binary-JSON-like formats such as MessagePack and CBOR, both BJData and
+UBJSON demonstrate a rare combination of being both binary and **quasi-human-readable**. This
+is because all semantic elements in BJData and UBJSON, including the data-type markers
+and name/string types are directly human-readable. Data stored in the BJData/UBJSON format
+are not only compact in size, fast to read/write, but also can be directly searched
+or read using simple processing.
+
+!!! abstract "References"
+
+	- [BJData Specification](https://neurojson.org/bjdata/draft2)
+
+## Serialization
+
+The library uses the following mapping from JSON values types to BJData types according to the BJData specification:
+
+| JSON value type | value/range                               | BJData type    | marker |
+|-----------------|-------------------------------------------|----------------|--------|
+| null            | `null`                                    | null           | `Z`    |
+| boolean         | `true`                                    | true           | `T`    |
+| boolean         | `false`                                   | false          | `F`    |
+| number_integer  | -9223372036854775808..-2147483649         | int64          | `L`    |
+| number_integer  | -2147483648..-32769                       | int32          | `l`    |
+| number_integer  | -32768..-129                              | int16          | `I`    |
+| number_integer  | -128..127                                 | int8           | `i`    |
+| number_integer  | 128..255                                  | uint8          | `U`    |
+| number_integer  | 256..32767                                | int16          | `I`    |
+| number_integer  | 32768..65535                              | uint16         | `u`    |
+| number_integer  | 65536..2147483647                         | int32          | `l`    |
+| number_integer  | 2147483648..4294967295                    | uint32         | `m`    |
+| number_integer  | 4294967296..9223372036854775807           | int64          | `L`    |
+| number_integer  | 9223372036854775808..18446744073709551615 | uint64         | `M`    |
+| number_unsigned | 0..127                                    | int8           | `i`    |
+| number_unsigned | 128..255                                  | uint8          | `U`    |
+| number_unsigned | 256..32767                                | int16          | `I`    |
+| number_unsigned | 32768..65535                              | uint16         | `u`    |
+| number_unsigned | 65536..2147483647                         | int32          | `l`    |
+| number_unsigned | 2147483648..4294967295                    | uint32         | `m`    |
+| number_unsigned | 4294967296..9223372036854775807           | int64          | `L`    |
+| number_unsigned | 9223372036854775808..18446744073709551615 | uint64         | `M`    |
+| number_float    | *any value*                               | float64        | `D`    |
+| string          | *with shortest length indicator*          | string         | `S`    |
+| array           | *see notes on optimized format/ND-array*  | array          | `[`    |
+| object          | *see notes on optimized format*           | map            | `{`    |
+
+!!! success "Complete mapping"
+
+	The mapping is **complete** in the sense that any JSON value type can be converted to a BJData value.
+
+	Any BJData output created by `to_bjdata` can be successfully parsed by `from_bjdata`.
+
+!!! warning "Size constraints"
+
+	The following values can **not** be converted to a BJData value:
+
+      - strings with more than 18446744073709551615 bytes (theoretical)
+
+!!! info "Unused BJData markers"
+
+	The following markers are not used in the conversion:
+
+    - `Z`: no-op values are not created.
+    - `C`: single-byte strings are serialized with `S` markers.
+
+!!! info "NaN/infinity handling"
+
+	If NaN or Infinity are stored inside a JSON number, they are
+    serialized properly. This behavior differs from the `dump()`
+    function which serializes NaN or Infinity to `null`.
+
+
+!!! info "Endianness"
+
+	A breaking difference between BJData and UBJSON is the endianness
+    of numerical values. In BJData, all numerical data types (integers
+    `UiuImlML` and floating-point values `hdD`) are stored in the little-endian (LE)
+    byte order as opposed to big-endian as used by UBJSON. To adopt LE
+    to store numeric records avoids unnecessary byte swapping on most modern
+    computers where LE is used as the default byte order.
+
+!!! info "Optimized formats"
+
+	The optimized formats for containers are supported: Parameter
+    `use_size` adds size information to the beginning of a container and
+    removes the closing marker. Parameter `use_type` further checks
+    whether all elements of a container have the same type and adds the
+    type marker to the beginning of the container. The `use_type`
+    parameter must only be used together with `use_size = true`.
+
+    Note that `use_size = true` alone may result in larger representations -
+    the benefit of this parameter is that the receiving side is
+    immediately informed on the number of elements of the container.
+
+!!! info "ND-array optimized format"
+
+	BJData extends UBJSON's optimized array **size** marker to support
+    ND-array of uniform numerical data types (referred to as the *packed array*).
+    For example, 2-D `uint8` integer array `[[1,2],[3,4],[5,6]]` that can be stored
+    as nested optimized array in UBJSON `[ [$U#i2 1 2 [$U#i2 3 4 [$U#i2 5 6 ]`,
+    can be further compressed in BJData and stored as `[$U#[$i#i2 2 3 1 2 3 4 5 6`
+    or `[$U#[i2 i3] 1 2 3 4 5 6`.
+
+    In order to maintain the type and dimension information of an ND-array,
+    when this library parses a BJData ND-array via `from_bjdata`, it converts the
+    data into a JSON object, following the **annotated array format** as defined in the
+    [JData specification (Draft 3)](https://github.com/NeuroJSON/jdata/blob/master/JData_specification.md#annotated-storage-of-n-d-arrays).
+    For example, the above 2-D `uint8` array can be parsed and accessed as
+
+    ```json
+    {
+        "_ArrayType_": "uint8",
+        "_ArraySize_": [2,3],
+        "_ArrayData_": [1,2,3,4,5,6]
+    }
+    ```
+
+    In the reversed direction, when `to_bjdata` detects a JSON object in the
+    above form, it automatically converts such object into a BJData ND-array
+    to generate compact output. The only exception is that when the 1-D dimensional
+    vector stored in `"_ArraySize_"` contains a single integer, or two integers with
+    one being 1, a regular 1-D optimized array is generated.
+
+    The current version of this library has not yet supported automatic
+    recognition and conversion from a nested JSON array input to a BJData ND-array.
+
+!!! info "Restrictions in optimized data types for arrays and objects"
+
+	Due to diminished space saving, hampered readability, and increased
+    security risks, in BJData, the allowed data types following the `$` marker
+    in an optimized array and object container are restricted to
+    **non-zero-fixed-length** data types. Therefore, the valid optimized
+    type markers can only be one of `UiuImlMLhdDC`. This also means other
+    variable (`[{SH`) or zero-length types (`TFN`) can not be used in an
+    optimized array or object in BJData.
+
+!!! info "Binary values"
+
+	If the JSON data contains the binary type, the value stored is a list
+    of integers, as suggested by the BJData documentation.  In particular,
+    this means that serialization and the deserialization of a JSON
+    containing binary values into BJData and back will result in a
+    different JSON object.
+
+
+??? example
+
+    ```cpp
+    --8<-- "examples/to_bjdata.cpp"
+    ```
+
+    Output:
+
+    ```c
+    --8<-- "examples/to_bjdata.output"
+    ```
+
+## Deserialization
+
+The library maps BJData types to JSON value types as follows:
+
+| BJData type | JSON value type                         | marker |
+|-------------|-----------------------------------------|--------|
+| no-op       | *no value, next value is read*          | `N`    |
+| null        | `null`                                  | `Z`    |
+| false       | `false`                                 | `F`    |
+| true        | `true`                                  | `T`    |
+| float16     | number_float                            | `h`    |
+| float32     | number_float                            | `d`    |
+| float64     | number_float                            | `D`    |
+| uint8       | number_unsigned                         | `U`    |
+| int8        | number_integer                          | `i`    |
+| uint16      | number_unsigned                         | `u`    |
+| int16       | number_integer                          | `I`    |
+| uint32      | number_unsigned                         | `m`    |
+| int32       | number_integer                          | `l`    |
+| uint64      | number_unsigned                         | `M`    |
+| int64       | number_integer                          | `L`    |
+| string      | string                                  | `S`    |
+| char        | string                                  | `C`    |
+| array       | array (optimized values are supported)  | `[`    |
+| ND-array    | object (in JData annotated array format)|`[$.#[.`|
+| object      | object (optimized values are supported) | `{`    |
+
+!!! success "Complete mapping"
+
+	The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
+
+
+??? example
+
+    ```cpp
+    --8<-- "examples/from_bjdata.cpp"
+    ```
+
+    Output:
+
+    ```json
+    --8<-- "examples/from_bjdata.output"
+    ```
diff --git a/docs/mkdocs/docs/features/binary_formats/index.md b/docs/mkdocs/docs/features/binary_formats/index.md
index 69c46ce..41cb72c 100644
--- a/docs/mkdocs/docs/features/binary_formats/index.md
+++ b/docs/mkdocs/docs/features/binary_formats/index.md
@@ -1,7 +1,9 @@
 # Binary Formats
 
-Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports
+Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over
+a network. Hence, the library supports
 
+- [BJData](bjdata.md) (Binary JData),
 - [BSON](bson.md) (Binary JSON),
 - [CBOR](cbor.md) (Concise Binary Object Representation),
 - [MessagePack](messagepack.md), and
@@ -16,6 +18,7 @@
 
 | Format      | Serialization                                 | Deserialization                              |
 |-------------|-----------------------------------------------|----------------------------------------------|
+| BJData      | complete                                      | complete                                     |
 | BSON        | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
 | CBOR        | complete                                      | incomplete, but all JSON types are supported |
 | MessagePack | complete                                      | complete                                     |
@@ -26,6 +29,7 @@
 
 | Format      | Binary values | Binary subtypes |
 |-------------|---------------|-----------------|
+| BJData      | not supported | not supported   |
 | BSON        | supported     | supported       |
 | CBOR        | supported     | supported       |
 | MessagePack | supported     | supported       |
@@ -37,14 +41,17 @@
 ### Sizes
 
 | Format             | [canada.json](https://github.com/nlohmann/json_test_data/blob/master/nativejson-benchmark/canada.json) | [twitter.json](https://github.com/nlohmann/json_test_data/blob/master/nativejson-benchmark/twitter.json) | [citm_catalog.json](https://github.com/nlohmann/json_test_data/blob/master/nativejson-benchmark/citm_catalog.json) | [jeopardy.json](https://github.com/nlohmann/json_test_data/blob/master/jeopardy/jeopardy.json) | [sample.json](https://github.com/nlohmann/json_test_data/blob/master/json_testsuite/sample.json) |
-|--------------------|-------------|--------------|-------------------|---------------| ------------- |
-| BSON               | 85,8 %      | 95,2 %       | 95,8 %            | 106,7 % (1)      | N/A (2)      |
-| CBOR               | 50,5 %      | 86,3 %       | 68,4 %            | 88,0 %        | 87,2 %        |
-| MessagePack        | 50,6 %      | 86,0 %       | 68,5 %            | 87,9 %        | 87,2 %        |
-| UBJSON             | 53,2 %      | 91,3 %       | 78,2 %            | 96,6 %        | 88,2 %        |
-| UBJSON (size)      | 58,6 %      | 92,3 %       | 86,8 %            | 97,4 %        | 89,3 %        |
-| UBJSON (size+type) | 55,9 %      | 92,3 %       | 85,0 %            | 95,0 %        | 89,5 %        |
-| BON8               | 50,5 %      | 83,8 %       | 63,5 %            | 87,5 %        | 85,6 %        |
+|--------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
+| BJData             | 53.2 %                                                                                                 | 91.1 %                                                                                                   | 78.1 %                                                                                                             | 96.6 %                                                                                         |
+| BJData (size)      | 58.6 %                                                                                                 | 92.1 %                                                                                                   | 86.7 %                                                                                                             | 97.4 %                                                                                         |
+| BJData (size+tyoe) | 58.6 %                                                                                                 | 92.1 %                                                                                                   | 86.5 %                                                                                                             | 97.4 %                                                                                         |
+| BSON               | 85.8 %                                                                                                 | 95.2 %                                                                                                   | 95.8 %                                                                                                             | 106.7 % (1)                                                                                    | N/A (2)                                                                                          |
+| CBOR               | 50.5 %                                                                                                 | 86.3 %                                                                                                   | 68.4 %                                                                                                             | 88.0 %                                                                                         | 87,2 %                                                                                           |
+| MessagePack        | 50.5 %                                                                                                 | 86.0 %                                                                                                   | 68.5 %                                                                                                             | 87.9 %                                                                                         | 87,2 %                                                                                           |
+| UBJSON             | 53.2 %                                                                                                 | 91.3 %                                                                                                   | 78.2 %                                                                                                             | 96.6 %                                                                                         | 88,2 %                                                                                           |
+| UBJSON (size)      | 58.6 %                                                                                                 | 92.3 %                                                                                                   | 86.8 %                                                                                                             | 97.4 %                                                                                         | 89,3 %                                                                                           |
+| UBJSON (size+type) | 55.9 %                                                                                                 | 92.3 %                                                                                                   | 85.0 %                                                                                                             | 95.0 %                                                                                         | 89,5 %                                                                                           |
+| BON8               | 50,5 %                                                                                                 | 83,8 %                                                                                                   | 63,5 %                                                                                                             | 87,5 %                                                                                         | 85,6 %                                                                                           |
 
 Sizes compared to minified JSON value.
 
diff --git a/docs/mkdocs/docs/features/binary_values.md b/docs/mkdocs/docs/features/binary_values.md
index c58834c..5ad6433 100644
--- a/docs/mkdocs/docs/features/binary_values.md
+++ b/docs/mkdocs/docs/features/binary_values.md
@@ -1,8 +1,12 @@
 # Binary Values
 
-The library implements several [binary formats](binary_formats/index.md) that encode JSON in an efficient way. Most of these formats support binary values; that is, values that have semantics define outside the library and only define a sequence of bytes to be stored.
+The library implements several [binary formats](binary_formats/index.md) that encode JSON in an efficient way. Most of
+these formats support binary values; that is, values that have semantics define outside the library and only define a
+sequence of bytes to be stored.
 
-JSON itself does not have a binary value. As such, binary values are an extension that this library implements to store values received by a binary format. Binary values are never created by the JSON parser, and are only part of a serialized JSON text if they have been created manually or via a binary format.
+JSON itself does not have a binary value. As such, binary values are an extension that this library implements to store
+values received by a binary format. Binary values are never created by the JSON parser, and are only part of a
+serialized JSON text if they have been created manually or via a binary format.
 
 ## API for binary values
 
@@ -19,7 +23,9 @@
 "std::vector<uint8_t>" <|-- json::binary_t
 ```
 
-By default, binary values are stored as `std::vector<std::uint8_t>`. This type can be changed by providing a template parameter to the `basic_json` type. To store binary subtypes, the storage type is extended and exposed as `json::binary_t`:
+By default, binary values are stored as `std::vector<std::uint8_t>`. This type can be changed by providing a template
+parameter to the `basic_json` type. To store binary subtypes, the storage type is extended and exposed as
+`json::binary_t`:
 
 ```cpp
 auto binary = json::binary_t({0xCA, 0xFE, 0xBA, 0xBE});
@@ -87,7 +93,9 @@
 
 ### JSON
 
-JSON does not have a binary type, and this library does not introduce a new type as this would break conformance. Instead, binary values are serialized as an object with two keys: `bytes` holds an array of integers, and `subtype` is an integer or `null`.
+JSON does not have a binary type, and this library does not introduce a new type as this would break conformance.
+Instead, binary values are serialized as an object with two keys: `bytes` holds an array of integers, and `subtype`
+is an integer or `null`.
 
 ??? example
 
@@ -115,11 +123,72 @@
 
 !!! warning "No roundtrip for binary values"
 
-    The JSON parser will not parse the objects generated by binary values back to binary values. This is by design to remain standards compliant. Serializing binary values to JSON is only implemented for debugging purposes.
+    The JSON parser will not parse the objects generated by binary values back to binary values. This is by design to
+    remain standards compliant. Serializing binary values to JSON is only implemented for debugging purposes.
+
+### BJData
+
+[BJData](binary_formats/bjdata.md) neither supports binary values nor subtypes, and proposes to serialize binary values
+as array of uint8 values. This translation is implemented by the library.
+
+??? example
+
+    Code:
+    
+    ```cpp
+    // create a binary value of subtype 42 (will be ignored in BJData)
+    json j;
+    j["binary"] = json::binary({0xCA, 0xFE, 0xBA, 0xBE}, 42);
+
+    // convert to BJData
+    auto v = json::to_bjdata(j);      
+    ```
+            
+    `v` is a `std::vector<std::uint8t>` with the following 20 elements:
+
+    ```c
+    0x7B                                             // '{'
+        0x69 0x06                                    // i 6 (length of the key)
+        0x62 0x69 0x6E 0x61 0x72 0x79                // "binary"
+        0x5B                                         // '['
+            0x55 0xCA 0x55 0xFE 0x55 0xBA 0x55 0xBE  // content (each byte prefixed with 'U')
+        0x5D                                         // ']'
+    0x7D                                             // '}'
+    ```
+
+    The following code uses the type and size optimization for UBJSON:
+
+    ```cpp
+    // convert to UBJSON using the size and type optimization
+    auto v = json::to_bjdata(j, true, true);
+    ```
+
+    The resulting vector has 22 elements; the optimization is not effective for examples with few values:
+
+    ```c
+    0x7B                                // '{'
+        0x23 0x69 0x01                  // '#' 'i' type of the array elements: unsigned integers
+        0x69 0x06                       // i 6 (length of the key)
+        0x62 0x69 0x6E 0x61 0x72 0x79   // "binary"
+        0x5B                            // '[' array
+            0x24 0x55                   // '$' 'U' type of the array elements: unsigned integers
+            0x23 0x69 0x04              // '#' i 4 number of array elements
+            0xCA 0xFE 0xBA 0xBE         // content
+    ```
+
+    Note that subtype (42) is **not** serialized and that UBJSON has **no binary type**, and deserializing `v` would
+    yield the following value:
+
+    ```json
+    {
+      "binary": [202, 254, 186, 190]
+    }
+    ```
 
 ### BSON
 
-[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as unsigned 8-bit integer. If no subtype is given, the generic binary subtype 0x00 is used.
+[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as
+unsigned 8-bit integer. If no subtype is given, the generic binary subtype 0x00 is used.
 
 ??? example
 
@@ -159,7 +228,9 @@
 
 ### CBOR
 
-[CBOR](binary_formats/cbor.md) supports binary values, but no subtypes. Subtypes will be serialized as tags. Any binary value will be serialized as byte strings. The library will choose the smallest representation using the length of the byte array.
+[CBOR](binary_formats/cbor.md) supports binary values, but no subtypes. Subtypes will be serialized as tags. Any binary
+value will be serialized as byte strings. The library will choose the smallest representation using the length of the
+byte array.
 
 ??? example
 
@@ -185,7 +256,8 @@
             0xCA 0xFE 0xBA 0xBE            // content
     ```
 
-    Note that the subtype is serialized as tag. However, parsing tagged values yield a parse error unless `json::cbor_tag_handler_t::ignore` or `json::cbor_tag_handler_t::store` is passed to `json::from_cbor`.
+    Note that the subtype is serialized as tag. However, parsing tagged values yield a parse error unless
+    `json::cbor_tag_handler_t::ignore` or `json::cbor_tag_handler_t::store` is passed to `json::from_cbor`.
 
     ```json
     {
@@ -198,7 +270,9 @@
 
 ### MessagePack
 
-[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and ext32. The subtype is then added as signed 8-bit integer.
+[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family
+is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and
+ext32. The subtype is then added as signed 8-bit integer.
 
 If no subtype is given, the bin family (bin8, bin16, bin32) is used.
 
@@ -239,7 +313,8 @@
 
 ### UBJSON
 
-[UBJSON](binary_formats/ubjson.md) neither supports binary values nor subtypes, and proposes to serialize binary values as array of uint8 values. This translation is implemented by the library.
+[UBJSON](binary_formats/ubjson.md) neither supports binary values nor subtypes, and proposes to serialize binary values
+as array of uint8 values. This translation is implemented by the library.
 
 ??? example
 
@@ -251,7 +326,7 @@
     j["binary"] = json::binary({0xCA, 0xFE, 0xBA, 0xBE}, 42);
 
     // convert to UBJSON
-    auto v = json::to_msgpack(j);      
+    auto v = json::to_ubjson(j);      
     ```
             
     `v` is a `std::vector<std::uint8t>` with the following 20 elements:
@@ -287,7 +362,8 @@
             0xCA 0xFE 0xBA 0xBE         // content
     ```
 
-    Note that subtype (42) is **not** serialized and that UBJSON has **no binary type**, and deserializing `v` would yield the following value:
+    Note that subtype (42) is **not** serialized and that UBJSON has **no binary type**, and deserializing `v` would
+    yield the following value:
 
     ```json
     {
diff --git a/docs/mkdocs/docs/features/element_access/checked_access.md b/docs/mkdocs/docs/features/element_access/checked_access.md
index fd444be..c4023cc 100644
--- a/docs/mkdocs/docs/features/element_access/checked_access.md
+++ b/docs/mkdocs/docs/features/element_access/checked_access.md
@@ -2,9 +2,11 @@
 
 ## Overview
 
-The `#!cpp at()` member function performs checked access; that is, it returns a reference to the desired value if it exists and throws a [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range) otherwise.
+The [`at`](../../api/basic_json/at.md) member function performs checked access; that is, it returns a reference to the
+desired value if it exists and throws a [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range)
+otherwise.
 
-??? example
+??? example "Read access"
 
     Consider the following JSON value:
     
@@ -18,18 +20,18 @@
     
     Assume the value is parsed to a `json` variable `j`.
 
-    | expression | value |
-    | ---------- | ----- |
-    | `#!cpp j`  | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` |
-    | `#!cpp j.at("name")`  | `#!json "Mary Smith"` |
-    | `#!cpp j.at("age")`  | `#!json 42` |
-    | `#!cpp j.at("hobbies")`  | `#!json ["hiking", "reading"]` |
-    | `#!cpp j.at("hobbies").at(0)`  | `#!json "hiking"` |
-    | `#!cpp j.at("hobbies").at(1)`  | `#!json "reading"` |
+    | expression                    | value                                                                        |
+    |-------------------------------|------------------------------------------------------------------------------|
+    | `#!cpp j`                     | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` |
+    | `#!cpp j.at("name")`          | `#!json "Mary Smith"`                                                        |
+    | `#!cpp j.at("age")`           | `#!json 42`                                                                  |
+    | `#!cpp j.at("hobbies")`       | `#!json ["hiking", "reading"]`                                               |
+    | `#!cpp j.at("hobbies").at(0)` | `#!json "hiking"`                                                            |
+    | `#!cpp j.at("hobbies").at(1)` | `#!json "reading"`                                                           |
 
 The return value is a reference, so it can be modified by the original value.
 
-??? example
+??? example "Write access"
 
     ```cpp
     j.at("name") = "John Smith";
@@ -45,9 +47,10 @@
     }
     ```
 
-When accessing an invalid index (i.e., an index greater than or equal to the array size) or the passed object key is non-existing, an exception is thrown.
+When accessing an invalid index (i.e., an index greater than or equal to the array size) or the passed object key is
+non-existing, an exception is thrown.
 
-??? example
+??? example "Accessing via invalid index or missing key"
 
     ```cpp
     j.at("hobbies").at(3) = "cooking";
@@ -59,13 +62,24 @@
     [json.exception.out_of_range.401] array index 3 is out of range
     ```
 
+    When you [extended diagnostic messages](../../home/exceptions.md#extended-diagnostic-messages) are enabled by
+    defining [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md), the exception further gives information where
+    the key or index is missing or out of range.
+    
+    ```
+    [json.exception.out_of_range.401] (/hobbies) array index 3 is out of range
+    ```
+
 ## Notes
 
 
 !!! failure "Exceptions"
 
-    - `at` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error304) is thrown.
-    - [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range) exceptions are thrown if the provided key is not found in an object or the provided index is invalid.
+    - [`at`](../../api/basic_json/at.md) can only be used with objects (with a string argument) or with arrays (with a
+      numeric argument). For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error304)
+      is thrown.
+    - [`basic_json::out_of_range` exception](../../home/exceptions.md#out-of-range) exceptions are thrown if the
+      provided key is not found in an object or the provided index is invalid.
 
 ## Summary
 
diff --git a/docs/mkdocs/docs/features/element_access/unchecked_access.md b/docs/mkdocs/docs/features/element_access/unchecked_access.md
index 4fdef35..1bdea94 100644
--- a/docs/mkdocs/docs/features/element_access/unchecked_access.md
+++ b/docs/mkdocs/docs/features/element_access/unchecked_access.md
@@ -2,9 +2,10 @@
 
 ## Overview
 
-Elements in a JSON object and a JSON array can be accessed via `#!cpp operator[]` similar to a `#!cpp std::map` and a `#!cpp std::vector`, respectively.
+Elements in a JSON object and a JSON array can be accessed via [`operator[]`](../../api/basic_json/operator%5B%5D.md)
+similar to a `#!cpp std::map` and a `#!cpp std::vector`, respectively.
 
-??? example
+??? example "Read access"
 
     Consider the following JSON value:
     
@@ -18,18 +19,19 @@
     
     Assume the value is parsed to a `json` variable `j`.
 
-    | expression | value |
-    | ---------- | ----- |
-    | `#!cpp j`  | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` |
-    | `#!cpp j["name"]`  | `#!json "Mary Smith"` |
-    | `#!cpp j["age"]`  | `#!json 42` |
-    | `#!cpp j["hobbies"]`  | `#!json ["hiking", "reading"]` |
-    | `#!cpp j["hobbies"][0]`  | `#!json "hiking"` |
-    | `#!cpp j["hobbies"][1]`  | `#!json "reading"` |
+    | expression              | value                                                                        |
+    |-------------------------|------------------------------------------------------------------------------|
+    | `#!cpp j`               | `#!json {"name": "Mary Smith", "age": 42, "hobbies": ["hiking", "reading"]}` |
+    | `#!cpp j["name"]`       | `#!json "Mary Smith"`                                                        |
+    | `#!cpp j["age"]`        | `#!json 42`                                                                  |
+    | `#!cpp j["hobbies"]`    | `#!json ["hiking", "reading"]`                                               |
+    | `#!cpp j["hobbies"][0]` | `#!json "hiking"`                                                            |
+    | `#!cpp j["hobbies"][1]` | `#!json "reading"`                                                           |
 
-The return value is a reference, so it can modify the original value. In case the passed object key is non-existing, a `#!json null` value is inserted which can be immediately be overwritten.
+The return value is a reference, so it can modify the original value. In case the passed object key is non-existing, a
+`#!json null` value is inserted which can be immediately be overwritten.
 
-??? example
+??? example "Write access"
 
     ```cpp
     j["name"] = "John Smith";
@@ -47,9 +49,10 @@
     }
     ```
 
-When accessing an invalid index (i.e., an index greater than or equal to the array size), the JSON array is resized such that the passed index is the new maximal index. Intermediate values are filled with `#!json null`.
+When accessing an invalid index (i.e., an index greater than or equal to the array size), the JSON array is resized such
+that the passed index is the new maximal index. Intermediate values are filled with `#!json null`.
 
-??? example
+??? example "Filling up arrays with `#!json null` values"
 
     ```cpp
     j["hobbies"][0] = "running";
@@ -76,17 +79,23 @@
     - `#!cpp std::vector::operator[]` never inserts a new element.
     - `#!cpp std::map::operator[]` is not available for const values.
     
-    The type `#!cpp json` wraps all JSON value types. It would be impossible to remove `operator[]` for const objects. At the same time, inserting elements for non-const objects is really convenient as it avoids awkward `insert` calls. To this end, we decided to have an inserting non-const behavior for both arrays and objects.
+    The type `#!cpp json` wraps all JSON value types. It would be impossible to remove
+    [`operator[]`](../../api/basic_json/operator%5B%5D.md) for const objects. At the same time, inserting elements for
+    non-const objects is really convenient as it avoids awkward `insert` calls. To this end, we decided to have an
+    inserting non-const behavior for both arrays and objects.
 
 !!! info
 
-    The access is unchecked. In case the passed object key does not exist or the passed array index is invalid, no exception is thrown.
+    The access is unchecked. In case the passed object key does not exist or the passed array index is invalid, no
+    exception is thrown.
 
 !!! danger
 
     - It is **undefined behavior** to access a const object with a non-existing key.
     - It is **undefined behavior** to access a const array with an invalid index.
-    - In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](../macros.md#json_assertx).
+    - In debug mode, an **assertion** will fire in both cases. You can disable assertions by defining the preprocessor
+      symbol `#!cpp NDEBUG` or redefine the macro [`JSON_ASSERT(x)`](../macros.md#json_assertx). See the documentation
+      on [runtime assertions](../assertions.md) for more information.
 
 !!! failure "Exceptions"
 
diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md
index 7e61f67..59ffbd5 100644
--- a/docs/mkdocs/docs/features/enum_conversion.md
+++ b/docs/mkdocs/docs/features/enum_conversion.md
@@ -58,3 +58,4 @@
   default pair carefully.
 - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the
   map will be returned when converting to or from JSON.
+- To disable the default serialization of enumerators as integers and force a compiler error instead, see [`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
diff --git a/docs/mkdocs/docs/features/iterators.md b/docs/mkdocs/docs/features/iterators.md
index 46f28f1..ce627e0 100644
--- a/docs/mkdocs/docs/features/iterators.md
+++ b/docs/mkdocs/docs/features/iterators.md
@@ -113,7 +113,7 @@
     ```cpp
     json j = {1, 2, 3, 4};
 
-    for (auto it = j.begin(); it != j.end(); ++it)
+    for (auto it = j.rbegin(); it != j.rend(); ++it)
     {
         std::cout << *it << std::endl;
     }
diff --git a/docs/mkdocs/docs/features/json_pointer.md b/docs/mkdocs/docs/features/json_pointer.md
index a7861ec..a3980b4 100644
--- a/docs/mkdocs/docs/features/json_pointer.md
+++ b/docs/mkdocs/docs/features/json_pointer.md
@@ -18,11 +18,11 @@
 }
 ```
 
-Then every value inside the JSON document can be idientified as follows:
+Then every value inside the JSON document can be identified as follows:
 
 | JSON Pointer      | JSON value                                                                       |
 |-------------------|----------------------------------------------------------------------------------|
-| `/`               | `#!json {"array":["A","B","C"],"nested":{"one":1,"two":2,"three":[true,false]}}` |
+| ``                | `#!json {"array":["A","B","C"],"nested":{"one":1,"two":2,"three":[true,false]}}` |
 | `/array`          | `#!json ["A","B","C"]`                                                           |
 | `/array/0`        | `#!json A`                                                                       |
 | `/array/1`        | `#!json B`                                                                       |
@@ -34,6 +34,9 @@
 | `/nested/three/0` | `#!json true`                                                                    |
 | `/nested/three/1` | `#!json false`                                                                   |
 
+Note `/` does not identify the root (i.e., the whole document), but an object entry with empty key `""`. See
+[RFC 6901](https://tools.ietf.org/html/rfc6901) for more information.
+
 ## JSON Pointer creation
 
 JSON Pointers can be created from a string:
diff --git a/docs/mkdocs/docs/features/macros.md b/docs/mkdocs/docs/features/macros.md
index e04a426..1be95d3 100644
--- a/docs/mkdocs/docs/features/macros.md
+++ b/docs/mkdocs/docs/features/macros.md
@@ -54,6 +54,12 @@
 
 See [full documentation of `JSON_NOEXCEPTION`](../api/macros/json_noexception.md).
 
+## `JSON_DISABLE_ENUM_SERIALIZATION`
+
+When defined, default parse and serialize functions for enums are excluded and have to be provided by the user, for example, using [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md).
+
+See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
+
 ## `JSON_NO_IO`
 
 When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
diff --git a/docs/mkdocs/docs/features/object_order.md b/docs/mkdocs/docs/features/object_order.md
index 0768f80..3ee16a9 100644
--- a/docs/mkdocs/docs/features/object_order.md
+++ b/docs/mkdocs/docs/features/object_order.md
@@ -42,30 +42,13 @@
 ??? example
 
     ```cpp
-    #include <iostream>
-    #include <nlohmann/json.hpp>
-    
-    using ordered_json = nlohmann::ordered_json;
-    
-    int main()
-    {
-        ordered_json j;
-        j["one"] = 1;
-        j["two"] = 2;
-        j["three"] = 3;
-        
-        std::cout << j.dump(2) << '\n';
-    }
+    --8<-- "examples/ordered_json.cpp"
     ```
     
     Output:
     
     ```json
-    {
-      "one": 1,
-      "two": 2,
-      "three": 3
-    }
+    --8<-- "examples/ordered_json.output"
     ```
 
 Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md
index 666d225..c9885ae 100644
--- a/docs/mkdocs/docs/home/exceptions.md
+++ b/docs/mkdocs/docs/home/exceptions.md
@@ -271,7 +271,7 @@
 
 A JSON Pointer array index must be a number.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.parse_error.109] parse error: array index 'one' is not a number
@@ -295,19 +295,34 @@
 
 ### json.exception.parse_error.112
 
-Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
+An unexpected byte was read in a [binary format](../features/binary_formats/index.md) or length information is invalid ([BSON](../features/binary_formats/bson.md)).
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0x1C
     ```
+    ```
+    [json.exception.parse_error.112] parse error at byte 1: syntax error while parsing MessagePack value: invalid byte: 0xC1
+    ```
+    ```
+    [json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x02
+    ```
+    ```
+    [json.exception.parse_error.112] parse error at byte 4: syntax error while parsing UBJSON size: expected '#' after type information; last byte: 0x02
+    ```
+    ```
+    [json.exception.parse_error.112] parse error at byte 10: syntax error while parsing BSON string: string length must be at least 1, is -2147483648
+    ```
+    ```
+    [json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
+    ```
 
 ### json.exception.parse_error.113
 
 While parsing a map key, a value that is not a string has been read.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF
@@ -374,7 +389,7 @@
 
 In the [erase](../api/basic_json/erase.md) or insert function, the passed iterator `pos` does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.invalid_iterator.202] iterator does not fit current value
@@ -538,7 +553,7 @@
 
 During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.302] type must be object, but is null
@@ -551,7 +566,7 @@
 
 To retrieve a reference to a value stored in a `basic_json` object with `get_ref`, the type of the reference must match the value type. For instance, for a JSON array, the `ReferenceType` must be `array_t &`.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object
@@ -564,7 +579,7 @@
 
 The `at()` member functions can only be executed for certain JSON types.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.304] cannot use at() with string
@@ -577,7 +592,7 @@
 
 The `operator[]` member functions can only be executed for certain JSON types.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.305] cannot use operator[] with a string argument with array
@@ -620,7 +635,7 @@
 
 The `insert()` member functions can only be executed for certain JSON types.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.309] cannot use insert() with array
@@ -643,7 +658,7 @@
 
 The `emplace()` and `emplace_back()` member functions can only be executed for certain JSON types.
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     ```
     [json.exception.type_error.311] cannot use emplace() with number
@@ -718,7 +733,7 @@
 
 The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON)
 
-!!! failure "Example message"
+!!! failure "Example messages"
 
     Serializing `#!json null` to BSON:
     ```
diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml
index 8ee9bba..80c077d 100644
--- a/docs/mkdocs/mkdocs.yml
+++ b/docs/mkdocs/mkdocs.yml
@@ -42,6 +42,7 @@
     - features/arbitrary_types.md
     - Binary Formats:
       - features/binary_formats/index.md
+      - features/binary_formats/bjdata.md
       - features/binary_formats/bson.md
       - features/binary_formats/cbor.md
       - features/binary_formats/messagepack.md
@@ -109,6 +110,7 @@
         - 'exception': api/basic_json/exception.md
         - 'find': api/basic_json/find.md
         - 'flatten': api/basic_json/flatten.md
+        - 'from_bjdata': api/basic_json/from_bjdata.md
         - 'from_bson': api/basic_json/from_bson.md
         - 'from_cbor': api/basic_json/from_cbor.md
         - 'from_msgpack': api/basic_json/from_msgpack.md
@@ -152,15 +154,16 @@
         - 'operator value_t': api/basic_json/operator_value_t.md
         - 'operator[]': api/basic_json/operator[].md
         - 'operator=': api/basic_json/operator=.md
+        - 'operator+=': api/basic_json/operator+=.md
         - 'operator==': api/basic_json/operator_eq.md
         - 'operator!=': api/basic_json/operator_ne.md
         - 'operator<': api/basic_json/operator_lt.md
-        - 'operator<<': api/basic_json/operator_ltlt.md
-        - 'operator<=': api/basic_json/operator_le.md
         - 'operator>': api/basic_json/operator_gt.md
-        - 'operator>>': api/basic_json/operator_gtgt.md
+        - 'operator<=': api/basic_json/operator_le.md
         - 'operator>=': api/basic_json/operator_ge.md
-        - 'operator+=': api/basic_json/operator+=.md
+        - 'operator<=>': api/basic_json/operator_spaceship.md
+        - 'operator<<': api/basic_json/operator_ltlt.md
+        - 'operator>>': api/basic_json/operator_gtgt.md
         - 'operator""_json': api/basic_json/operator_literal_json.md
         - 'operator""_json_pointer': api/basic_json/operator_literal_json_pointer.md
         - 'out_of_range': api/basic_json/out_of_range.md
@@ -178,6 +181,7 @@
         - 'string_t': api/basic_json/string_t.md
         - 'swap': api/basic_json/swap.md
         - 'std::swap&lt;basic_json&gt;': api/basic_json/std_swap.md
+        - 'to_bjdata': api/basic_json/to_bjdata.md
         - 'to_bon8': api/basic_json/to_bon8.md
         - 'to_bson': api/basic_json/to_bson.md
         - 'to_cbor': api/basic_json/to_cbor.md
@@ -238,12 +242,15 @@
         - 'JSON_ASSERT': api/macros/json_assert.md
         - 'JSON_CATCH_USER': api/macros/json_throw_user.md
         - 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
+        - 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
         - 'JSON_HAS_CPP_11': api/macros/json_has_cpp_11.md
         - 'JSON_HAS_CPP_14': api/macros/json_has_cpp_11.md
         - 'JSON_HAS_CPP_17': api/macros/json_has_cpp_11.md
         - 'JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
         - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md
         - 'JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
+        - 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
+        - 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
         - 'JSON_NOEXCEPTION': api/macros/json_noexception.md
         - 'JSON_NO_IO': api/macros/json_no_io.md
         - 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
@@ -251,6 +258,7 @@
         - 'JSON_THROW_USER': api/macros/json_throw_user.md
         - 'JSON_TRY_USER': api/macros/json_throw_user.md
         - 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
+        - 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
         - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE': api/macros/nlohmann_define_type_intrusive.md
         - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_intrusive.md
         - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md
@@ -317,5 +325,8 @@
         minify_html: true
     - git-revision-date-localized
 
+extra_css:
+    - css/custom.css
+
 extra_javascript:
   - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML
diff --git a/docs/mkdocs/scripts/check_structure.py b/docs/mkdocs/scripts/check_structure.py
index 92f6ff1..56de6e8 100755
--- a/docs/mkdocs/scripts/check_structure.py
+++ b/docs/mkdocs/scripts/check_structure.py
@@ -47,14 +47,17 @@
     files = sorted(glob.glob('api/**/*.md', recursive=True))
     for file in files:
         with open(file) as file_content:
-            section_idx = -1
-            existing_sections = []
-            in_initial_code_example = False
-            previous_line = None
-            h1sections = 0
+            section_idx = -1                 # the index of the current h2 section
+            existing_sections = []           # the list of h2 sections in the file
+            in_initial_code_example = False  # whether we are inside the first code example block
+            previous_line = None             # the previous read line
+            h1sections = 0                   # the number of h1 sections in the file
+            last_overload = 0                # the last seen overload number in the code example
+            documented_overloads = {}        # the overloads that have been documented in the current block
+            current_section = None           # the name of the current section
 
-            for lineno, line in enumerate(file_content.readlines()):
-                line = line.strip()
+            for lineno, original_line in enumerate(file_content.readlines()):
+                line = original_line.strip()
 
                 if line.startswith('# '):
                     h1sections += 1
@@ -70,10 +73,21 @@
 
                 # lines longer than 160 characters are bad (unless they are tables)
                 if len(line) > 160 and '|' not in line:
-                    report('whitespace/line_length', f'{file}:{lineno+1}', f'line is too long ({len(line)} vs. 160 chars)')
+                    report('whitespace/line_length', f'{file}:{lineno+1} ({current_section})', f'line is too long ({len(line)} vs. 160 chars)')
 
                 # check if sections are correct
                 if line.startswith('## '):
+                    # before starting a new section, check if the previous one documented all overloads
+                    if current_section in documented_overloads and last_overload != 0:
+                        if len(documented_overloads[current_section]) > 0 and len(documented_overloads[current_section]) != last_overload:
+                            expected = list(range(1, last_overload+1))
+                            undocumented = [x for x in expected if x not in documented_overloads[current_section]]
+                            unexpected = [x for x in documented_overloads[current_section] if x not in expected]
+                            if len(undocumented):
+                                report('style/numbering', f'{file}:{lineno} ({current_section})', f'undocumented overloads: {", ".join([f"({x})" for x in undocumented])}')
+                            if len(unexpected):
+                                report('style/numbering', f'{file}:{lineno} ({current_section})', f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
+
                     current_section = line.strip('## ')
                     existing_sections.append(current_section)
 
@@ -83,13 +97,29 @@
                             report('structure/section_order', f'{file}:{lineno+1}', f'section "{current_section}" is in an unexpected order (should be before "{expected_sections[section_idx]}")')
                         section_idx = idx
                     else:
-                        report('structure/unknown_section', f'{file}:{lineno+1}', f'section "{current_section}" is not part of the expected sections')
+                        if 'index.md' not in file:  # index.md files may have a different structure
+                            report('structure/unknown_section', f'{file}:{lineno+1}', f'section "{current_section}" is not part of the expected sections')
+
+                # collect the numbered items of the current section to later check if they match the number of overloads
+                if last_overload != 0 and not in_initial_code_example:
+                    if len(original_line) and original_line[0].isdigit():
+                        number = int(re.findall(r"^(\d+).", original_line)[0])
+                        if current_section not in documented_overloads:
+                            documented_overloads[current_section] = []
+                        documented_overloads[current_section].append(number)
 
                 # code example
                 if line == '```cpp' and section_idx == -1:
                     in_initial_code_example = True
 
                 if in_initial_code_example and line.startswith('//'):
+                    # check numbering of overloads
+                    if any(map(str.isdigit, line)):
+                        number = int(re.findall(r'\d+', line)[0])
+                        if number != last_overload + 1:
+                            report('style/numbering', f'{file}:{lineno+1}', f'expected number ({number}) to be ({last_overload +1 })')
+                        last_overload = number
+
                     if any(map(str.isdigit, line)) and '(' not in line:
                         report('style/numbering', f'{file}:{lineno+1}', 'number should be in parentheses: {line}')
 
@@ -98,18 +128,19 @@
 
                 # consecutive blank lines are bad
                 if line == '' and previous_line == '':
-                    report('whitespace/blank_lines', f'{file}:{lineno}-{lineno+1}', 'consecutive blank lines')
+                    report('whitespace/blank_lines', f'{file}:{lineno}-{lineno+1} ({current_section})', 'consecutive blank lines')
 
                 # check that non-example admonitions have titles
                 untitled_admonition = re.match(r'^(\?\?\?|!!!) ([^ ]+)$', line)
                 if untitled_admonition and untitled_admonition.group(2) != 'example':
-                    report('style/admonition_title', f'{file}:{lineno}', f'"{untitled_admonition.group(2)}" admonitions should have a title')
+                    report('style/admonition_title', f'{file}:{lineno} ({current_section})', f'"{untitled_admonition.group(2)}" admonitions should have a title')
 
                 previous_line = line
 
-            for required_section in required_sections:
-                if required_section not in existing_sections:
-                    report('structure/missing_section', f'{file}:{lineno+1}', f'required section "{required_section}" was not found')
+            if 'index.md' not in file:  # index.md files may have a different structure
+                for required_section in required_sections:
+                    if required_section not in existing_sections:
+                        report('structure/missing_section', f'{file}:{lineno+1}', f'required section "{required_section}" was not found')
 
 
 def check_examples():
diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp
index 079baa7..ed4e6de 100644
--- a/include/nlohmann/detail/conversions/from_json.hpp
+++ b/include/nlohmann/detail/conversions/from_json.hpp
@@ -39,7 +39,7 @@
 namespace detail
 {
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
     {
@@ -86,7 +86,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
     {
@@ -96,7 +96,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -111,7 +111,7 @@
         std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
         && !std::is_same<typename BasicJsonType::string_t, StringType>::value
         && !is_json_ref<StringType>::value, int > = 0 >
-void from_json(const BasicJsonType& j, StringType& s)
+inline void from_json(const BasicJsonType& j, StringType& s)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -122,36 +122,38 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
+#if !JSON_DISABLE_ENUM_SERIALIZATION
 template<typename BasicJsonType, typename EnumType,
          enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void from_json(const BasicJsonType& j, EnumType& e)
+inline void from_json(const BasicJsonType& j, EnumType& e)
 {
     typename std::underlying_type<EnumType>::type val;
     get_arithmetic_value(j, val);
     e = static_cast<EnumType>(val);
 }
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
 
 // forward_list doesn't have an insert method
 template<typename BasicJsonType, typename T, typename Allocator,
          enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -168,7 +170,7 @@
 // valarray doesn't have an insert method
 template<typename BasicJsonType, typename T,
          enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::valarray<T>& l)
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -193,7 +195,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
 {
     arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
 }
@@ -237,8 +239,8 @@
          enable_if_t<
              std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
              int> = 0>
-void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
-                          priority_tag<0> /*unused*/)
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+                                 priority_tag<0> /*unused*/)
 {
     using std::end;
 
@@ -295,7 +297,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
     {
@@ -307,7 +309,7 @@
 
 template<typename BasicJsonType, typename ConstructibleObjectType,
          enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
-void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
     {
@@ -339,7 +341,7 @@
                !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
                !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
                int > = 0 >
-void from_json(const BasicJsonType& j, ArithmeticType& val)
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
 {
     switch (static_cast<value_t>(j))
     {
@@ -389,7 +391,7 @@
 }
 
 template<typename BasicJsonType, typename A1, typename A2>
-void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
 {
     p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
 }
@@ -401,7 +403,7 @@
 }
 
 template<typename BasicJsonType, typename... Args>
-void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
 {
     t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
 }
@@ -421,7 +423,7 @@
 template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
            typename = enable_if_t < !std::is_constructible <
                                         typename BasicJsonType::string_t, Key >::value >>
-void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -441,7 +443,7 @@
 template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
            typename = enable_if_t < !std::is_constructible <
                                         typename BasicJsonType::string_t, Key >::value >>
-void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -460,7 +462,7 @@
 
 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, std_fs::path& p)
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -482,11 +484,16 @@
 };
 }  // namespace detail
 
+#ifndef JSON_HAS_CPP_17
 /// namespace to hold default `from_json` function
 /// to see why this is required:
 /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
 namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
 {
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
 } // namespace
+#endif
 } // namespace nlohmann
diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp
index 7628ae0..1d7a86a 100644
--- a/include/nlohmann/detail/conversions/to_json.hpp
+++ b/include/nlohmann/detail/conversions/to_json.hpp
@@ -267,55 +267,64 @@
 
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
-void to_json(BasicJsonType& j, T b) noexcept
+inline void to_json(BasicJsonType& j, T b) noexcept
 {
     external_constructor<value_t::boolean>::construct(j, b);
 }
 
+template<typename BasicJsonType,
+         enable_if_t<std::is_convertible<const std::vector<bool>::reference&, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::vector<bool>::reference& b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
 template<typename BasicJsonType, typename CompatibleString,
          enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleString& s)
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
 {
     external_constructor<value_t::string>::construct(j, s);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
 {
     external_constructor<value_t::string>::construct(j, std::move(s));
 }
 
 template<typename BasicJsonType, typename FloatType,
          enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
-void to_json(BasicJsonType& j, FloatType val) noexcept
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
 {
     external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
 }
 
 template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
          enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
 {
     external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
 }
 
 template<typename BasicJsonType, typename CompatibleNumberIntegerType,
          enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
 {
     external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
 }
 
+#if !JSON_DISABLE_ENUM_SERIALIZATION
 template<typename BasicJsonType, typename EnumType,
          enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void to_json(BasicJsonType& j, EnumType e) noexcept
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
 {
     using underlying_type = typename std::underlying_type<EnumType>::type;
     external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
 }
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std::vector<bool>& e)
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
 {
     external_constructor<value_t::array>::construct(j, e);
 }
@@ -328,39 +337,39 @@
                          !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
                          !is_basic_json<CompatibleArrayType>::value,
                          int > = 0 >
-void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
 {
     external_constructor<value_t::array>::construct(j, arr);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
 {
     external_constructor<value_t::binary>::construct(j, bin);
 }
 
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
-void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
 {
     external_constructor<value_t::array>::construct(j, std::move(arr));
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
 {
     external_constructor<value_t::array>::construct(j, std::move(arr));
 }
 
 template < typename BasicJsonType, typename CompatibleObjectType,
            enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
-void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
 {
     external_constructor<value_t::object>::construct(j, obj);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
 {
     external_constructor<value_t::object>::construct(j, std::move(obj));
 }
@@ -370,13 +379,13 @@
     enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
                   const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
                   int > = 0 >
-void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
 {
     external_constructor<value_t::array>::construct(j, arr);
 }
 
 template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
-void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
 {
     j = { p.first, p.second };
 }
@@ -384,26 +393,26 @@
 // for https://github.com/nlohmann/json/pull/1134
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
-void to_json(BasicJsonType& j, const T& b)
+inline void to_json(BasicJsonType& j, const T& b)
 {
     j = { {b.key(), b.value()} };
 }
 
 template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
 {
     j = { std::get<Idx>(t)... };
 }
 
 template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
-void to_json(BasicJsonType& j, const T& t)
+inline void to_json(BasicJsonType& j, const T& t)
 {
     to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
 }
 
 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std_fs::path& p)
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
 {
     j = p.string();
 }
@@ -420,11 +429,16 @@
 };
 }  // namespace detail
 
+#ifndef JSON_HAS_CPP_17
 /// namespace to hold default `to_json` function
 /// to see why this is required:
 /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
 namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
 {
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
 } // namespace
+#endif
 } // namespace nlohmann
diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp
index 5246f69..90bdaa6 100644
--- a/include/nlohmann/detail/input/binary_reader.hpp
+++ b/include/nlohmann/detail/input/binary_reader.hpp
@@ -622,7 +622,8 @@
             case 0x95:
             case 0x96:
             case 0x97:
-                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+                return get_cbor_array(
+                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
 
             case 0x98: // array (one-byte uint8_t for n follows)
             {
@@ -639,13 +640,13 @@
             case 0x9A: // array (four-byte uint32_t for n follow)
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0x9B: // array (eight-byte uint64_t for n follow)
             {
                 std::uint64_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0x9F: // array (indefinite length)
@@ -676,7 +677,7 @@
             case 0xB5:
             case 0xB6:
             case 0xB7:
-                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+                return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
 
             case 0xB8: // map (one-byte uint8_t for n follows)
             {
@@ -693,13 +694,13 @@
             case 0xBA: // map (four-byte uint32_t for n follow)
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0xBB: // map (eight-byte uint64_t for n follow)
             {
                 std::uint64_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0xBF: // map (indefinite length)
@@ -1346,7 +1347,7 @@
             case 0x8D:
             case 0x8E:
             case 0x8F:
-                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
 
             // fixarray
             case 0x90:
@@ -1365,7 +1366,7 @@
             case 0x9D:
             case 0x9E:
             case 0x9F:
-                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
 
             // fixstr
             case 0xA0:
@@ -1502,7 +1503,7 @@
             case 0xDD: // array 32
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
             }
 
             case 0xDE: // map 16
@@ -1514,7 +1515,7 @@
             case 0xDF: // map 32
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
             }
 
             // negative fixint
@@ -1942,8 +1943,9 @@
     {
         std::pair<std::size_t, char_int_type> size_and_type;
         size_t dimlen = 0;
+        bool no_ndarray = true;
 
-        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
         {
             return false;
         }
@@ -1956,7 +1958,7 @@
                 {
                     for (std::size_t i = 0; i < size_and_type.first; ++i)
                     {
-                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, size_and_type.second)))
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
                         {
                             return false;
                         }
@@ -1968,7 +1970,7 @@
             {
                 for (std::size_t i = 0; i < size_and_type.first; ++i)
                 {
-                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen)))
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
                     {
                         return false;
                     }
@@ -1980,7 +1982,7 @@
         {
             while (current != ']')
             {
-                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, current)))
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
                 {
                     return false;
                 }
@@ -1993,9 +1995,16 @@
 
     /*!
     @param[out] result  determined size
+    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector
+                               or ndarray dimension is not allowed; `false` means ndarray
+                               is allowed; for output, `true` means an ndarray is found;
+                               is_ndarray can only return `true` when its initial value
+                               is `false`
+    @param[in] prefix  type marker if already read, otherwise set to 0
+
     @return whether size determination completed
     */
-    bool get_ubjson_size_value(std::size_t& result, char_int_type prefix = 0)
+    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
     {
         if (prefix == 0)
         {
@@ -2022,6 +2031,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
                 return true;
             }
@@ -2033,6 +2047,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -2044,6 +2063,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -2055,6 +2079,16 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -2085,7 +2119,7 @@
                 {
                     return false;
                 }
-                result = static_cast<std::size_t>(number);
+                result = conditional_static_cast<std::size_t>(number);
                 return true;
             }
 
@@ -2100,6 +2134,11 @@
                 {
                     return false;
                 }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
                 result = detail::conditional_static_cast<std::size_t>(number);
                 return true;
             }
@@ -2110,6 +2149,10 @@
                 {
                     break;
                 }
+                if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimentional vector is not allowed", "size"), nullptr));
+                }
                 std::vector<size_t> dim;
                 if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
                 {
@@ -2122,6 +2165,15 @@
                 }
                 if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format
                 {
+                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+                    {
+                        if ( i == 0 )
+                        {
+                            result = 0;
+                            return true;
+                        }
+                    }
+
                     string_t key = "_ArraySize_";
                     if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
                     {
@@ -2131,12 +2183,16 @@
                     for (auto i : dim)
                     {
                         result *= i;
-                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(static_cast<number_integer_t>(i))))
+                        if (result == 0 || result == string_t::npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be string_t::npos as it is used to initialize size in get_ubjson_size_type()
+                        {
+                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
                         {
                             return false;
                         }
                     }
-                    result |= (1ull << (sizeof(result) * 8 - 1)); // low 63 bit of result stores the total element count, sign-bit indicates ndarray
+                    is_ndarray = true;
                     return sax->end_array();
                 }
                 result = 0;
@@ -2167,13 +2223,15 @@
     for a more compact representation.
 
     @param[out] result  pair of the size and the type
+    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector
 
     @return whether pair creation completed
     */
-    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)
+    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
     {
         result.first = string_t::npos; // size
         result.second = 0; // type
+        bool is_ndarray = false;
 
         get_ignore_noop();
 
@@ -2182,7 +2240,14 @@
             std::vector<char_int_type> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
 
             result.second = get();  // must not ignore 'N', because 'N' maybe the type
-            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type") || (input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() )))
+            if (JSON_HEDLEY_UNLIKELY( input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() ))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
             {
                 return false;
             }
@@ -2199,12 +2264,28 @@
                                         exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
             }
 
-            return get_ubjson_size_value(result.first);
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                if (inside_ndarray)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                            exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+                }
+                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+            }
+            return is_error;
         }
 
         if (current == '#')
         {
-            return get_ubjson_size_value(result.first);
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                        exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+            }
+            return is_error;
         }
 
         return true;
@@ -2405,17 +2486,26 @@
             return false;
         }
 
-        // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
         // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
 
-        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1)))
+        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && (size_and_type.second & (1 << 8)) != 0)
         {
             std::map<char_int_type, string_t> bjdtype = {{'U', "uint8"},  {'i', "int8"},  {'u', "uint16"}, {'I', "int16"},
                 {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"}
             };
 
+            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
+
             string_t key = "_ArrayType_";
-            if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0 || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) ))
+            if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0))
+            {
+                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));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(bjdtype[size_and_type.second]) ))
             {
                 return false;
             }
@@ -2425,7 +2515,6 @@
                 size_and_type.second = 'U';
             }
 
-            size_and_type.first &= ~(1ull << (sizeof(std::size_t) * 8 - 1));
             key = "_ArrayData_";
             if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
             {
@@ -2505,9 +2594,12 @@
             return false;
         }
 
-        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1)))
+        // do not accept ND-array size in objects in BJData
+        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && (size_and_type.second & (1 << 8)) != 0)
         {
-            return false;
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
         }
 
         string_t key;
@@ -2581,7 +2673,8 @@
     {
         // get size of following number string
         std::size_t size{};
-        auto res = get_ubjson_size_value(size);
+        bool no_ndarray = true;
+        auto res = get_ubjson_size_value(size, no_ndarray);
         if (JSON_HEDLEY_UNLIKELY(!res))
         {
             return res;
diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp
index 4060081..9355137 100644
--- a/include/nlohmann/detail/input/json_sax.hpp
+++ b/include/nlohmann/detail/input/json_sax.hpp
@@ -233,6 +233,9 @@
 
     bool key(string_t& val)
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
         // add null at given key and store the reference for later
         object_element = &(ref_stack.back()->m_value.object->operator[](val));
         return true;
@@ -240,6 +243,9 @@
 
     bool end_object()
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
         ref_stack.back()->set_parents();
         ref_stack.pop_back();
         return true;
@@ -259,6 +265,9 @@
 
     bool end_array()
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_array());
+
         ref_stack.back()->set_parents();
         ref_stack.pop_back();
         return true;
diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp
index cdcdaff..028de40 100644
--- a/include/nlohmann/detail/iterators/iter_impl.hpp
+++ b/include/nlohmann/detail/iterators/iter_impl.hpp
@@ -51,9 +51,12 @@
     // make sure BasicJsonType is basic_json or const basic_json
     static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
                   "iter_impl only accepts (const) basic_json");
+    // superficial check for the LegacyBidirectionalIterator named requirement
+    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+                  "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
 
   public:
-
     /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
     /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
     /// A user-defined iterator should provide publicly accessible typedefs named
diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp
index 9994b36..a75614d 100644
--- a/include/nlohmann/detail/iterators/iteration_proxy.hpp
+++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp
@@ -6,6 +6,10 @@
 #include <tuple> // tuple_size, get, tuple_element
 #include <utility> // move
 
+#if JSON_HAS_RANGES
+    #include <ranges> // enable_borrowed_range
+#endif
+
 #include <nlohmann/detail/meta/type_traits.hpp>
 #include <nlohmann/detail/value_t.hpp>
 
@@ -25,14 +29,14 @@
   public:
     using difference_type = std::ptrdiff_t;
     using value_type = iteration_proxy_value;
-    using pointer = value_type * ;
-    using reference = value_type & ;
+    using pointer = value_type *;
+    using reference = value_type &;
     using iterator_category = std::input_iterator_tag;
     using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
 
   private:
     /// the iterator
-    IteratorType anchor;
+    IteratorType anchor{};
     /// an index for arrays (used to create key names)
     std::size_t array_index = 0;
     /// last stringified array index
@@ -40,15 +44,30 @@
     /// a string representation of the array index
     mutable string_type array_index_str = "0";
     /// an empty string (to return a reference for primitive values)
-    const string_type empty_str{};
+    string_type empty_str{};
 
   public:
-    explicit iteration_proxy_value(IteratorType it) noexcept
+    explicit iteration_proxy_value() = default;
+    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_default_constructible<string_type>::value)
         : anchor(std::move(it))
+        , array_index(array_index_)
     {}
 
+    iteration_proxy_value(iteration_proxy_value const&) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+    iteration_proxy_value(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_move_constructible<string_type>::value) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+             && std::is_nothrow_move_assignable<string_type>::value) = default;
+    ~iteration_proxy_value() = default;
+
     /// dereference operator (needed for range-based for)
-    iteration_proxy_value& operator*()
+    const iteration_proxy_value& operator*() const
     {
         return *this;
     }
@@ -62,6 +81,14 @@
         return *this;
     }
 
+    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto tmp = iteration_proxy_value(anchor, array_index);
+        ++anchor;
+        ++array_index;
+        return tmp;
+    }
+
     /// equality operator (needed for InputIterator)
     bool operator==(const iteration_proxy_value& o) const
     {
@@ -122,25 +149,34 @@
 {
   private:
     /// the container to iterate
-    typename IteratorType::reference container;
+    typename IteratorType::pointer container = nullptr;
 
   public:
+    explicit iteration_proxy() = default;
+
     /// construct iteration proxy from a container
     explicit iteration_proxy(typename IteratorType::reference cont) noexcept
-        : container(cont) {}
+        : container(&cont) {}
+
+    iteration_proxy(iteration_proxy const&) = default;
+    iteration_proxy& operator=(iteration_proxy const&) = default;
+    iteration_proxy(iteration_proxy&&) noexcept = default;
+    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+    ~iteration_proxy() = default;
 
     /// return iterator begin (needed for range-based for)
-    iteration_proxy_value<IteratorType> begin() noexcept
+    iteration_proxy_value<IteratorType> begin() const noexcept
     {
-        return iteration_proxy_value<IteratorType>(container.begin());
+        return iteration_proxy_value<IteratorType>(container->begin());
     }
 
     /// return iterator end (needed for range-based for)
-    iteration_proxy_value<IteratorType> end() noexcept
+    iteration_proxy_value<IteratorType> end() const noexcept
     {
-        return iteration_proxy_value<IteratorType>(container.end());
+        return iteration_proxy_value<IteratorType>(container->end());
     }
 };
+
 // Structured Bindings Support
 // For further reference see https://blog.tartanllama.xyz/structured-bindings/
 // And see https://github.com/nlohmann/json/pull/1391
@@ -187,3 +223,8 @@
     #pragma clang diagnostic pop
 #endif
 } // namespace std
+
+#if JSON_HAS_RANGES
+    template <typename IteratorType>
+    inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
+#endif
diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp
index cc9ac5f..706f1b9 100644
--- a/include/nlohmann/detail/macro_scope.hpp
+++ b/include/nlohmann/detail/macro_scope.hpp
@@ -37,6 +37,12 @@
     #define JSON_HAS_CPP_11
 #endif
 
+#ifdef __has_include
+    #if __has_include(<version>)
+        #include <version>
+    #endif
+#endif
+
 #if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
     #ifdef JSON_HAS_CPP_17
         #if defined(__cpp_lib_filesystem)
@@ -98,14 +104,31 @@
 #endif
 
 #ifndef JSON_HAS_THREE_WAY_COMPARISON
-    #if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \
-        && defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L
+    #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+        && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
         #define JSON_HAS_THREE_WAY_COMPARISON 1
     #else
         #define JSON_HAS_THREE_WAY_COMPARISON 0
     #endif
 #endif
 
+#ifndef JSON_HAS_RANGES
+    // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+    #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+        #define JSON_HAS_RANGES 0
+    #elif defined(__cpp_lib_ranges)
+        #define JSON_HAS_RANGES 1
+    #else
+        #define JSON_HAS_RANGES 0
+    #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+    #define JSON_INLINE_VARIABLE inline
+#else
+    #define JSON_INLINE_VARIABLE
+#endif
+
 #if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
     #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
 #else
@@ -429,3 +452,11 @@
 #ifndef JSON_DIAGNOSTICS
     #define JSON_DIAGNOSTICS 0
 #endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+    #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp
index ec57b02..09f677b 100644
--- a/include/nlohmann/detail/macro_unscope.hpp
+++ b/include/nlohmann/detail/macro_unscope.hpp
@@ -14,7 +14,9 @@
 #undef NLOHMANN_BASIC_JSON_TPL
 #undef JSON_EXPLICIT
 #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
 #undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
 
 #ifndef JSON_TEST_KEEP_MACROS
     #undef JSON_CATCH
@@ -26,6 +28,8 @@
     #undef JSON_HAS_FILESYSTEM
     #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
     #undef JSON_HAS_THREE_WAY_COMPARISON
+    #undef JSON_HAS_RANGES
+    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 #endif
 
 #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp
index 2cc13f3..cc34831 100644
--- a/include/nlohmann/detail/meta/type_traits.hpp
+++ b/include/nlohmann/detail/meta/type_traits.hpp
@@ -183,7 +183,7 @@
 template<class B> struct conjunction<B> : B { };
 template<class B, class... Bn>
 struct conjunction<B, Bn...>
-: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
 
 // https://en.cppreference.com/w/cpp/types/negation
 template<class B> struct negation : std::integral_constant < bool, !B::value > { };
@@ -347,8 +347,15 @@
 template<typename BasicJsonType, typename ConstructibleStringType>
 struct is_constructible_string_type
 {
+    // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+    using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+    using laundered_type = ConstructibleStringType;
+#endif
+
     static constexpr auto value =
-        is_constructible<ConstructibleStringType,
+        is_constructible<laundered_type,
         typename BasicJsonType::string_t>::value;
 };
 
@@ -570,5 +577,100 @@
     return value;
 }
 
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+      all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+         bool OfTypeSigned = std::is_signed<OfType>::value,
+         bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) >= static_cast<CommonType>(std::numeric_limits<OfType>::min())
+               && static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T,
+         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+         typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+    static constexpr bool test(T val)
+    {
+        return value_in_range_of_impl2<OfType, T>::test(val);
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+    static constexpr bool test(T /*val*/)
+    {
+        return true;
+    }
+};
+
+template<typename OfType, typename T>
+inline constexpr bool value_in_range_of(T val)
+{
+    return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
 }  // namespace detail
 }  // namespace nlohmann
diff --git a/include/nlohmann/detail/value_t.hpp b/include/nlohmann/detail/value_t.hpp
index a98c435..5a32b64 100644
--- a/include/nlohmann/detail/value_t.hpp
+++ b/include/nlohmann/detail/value_t.hpp
@@ -5,6 +5,11 @@
 #include <cstdint> // uint8_t
 #include <string> // string
 
+#include <nlohmann/detail/macro_scope.hpp>
+#if JSON_HAS_THREE_WAY_COMPARISON
+    #include <compare> // partial_ordering
+#endif
+
 namespace nlohmann
 {
 namespace detail
@@ -64,7 +69,11 @@
 
 @since version 1.0.0
 */
-inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#if JSON_HAS_THREE_WAY_COMPARISON
+    inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+    inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
 {
     static constexpr std::array<std::uint8_t, 9> order = {{
             0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
@@ -75,7 +84,26 @@
 
     const auto l_index = static_cast<std::size_t>(lhs);
     const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+    if (l_index < order.size() && r_index < order.size())
+    {
+        return order[l_index] <=> order[r_index]; // *NOPAD*
+    }
+    return std::partial_ordering::unordered;
+#else
     return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
 }
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
 }  // namespace detail
 }  // namespace nlohmann
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 6d10f8a..cf84b35 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -33,7 +33,7 @@
  * contains the most recent documentation and should also be applicable to  *
  * previous versions; documentation for deprecated functions is not         *
  * removed, but marked deprecated. See "Generate documentation" section in  *
- * file docs/README.md.                                                      *
+ * file docs/README.md.                                                     *
 \****************************************************************************/
 
 #ifndef INCLUDE_NLOHMANN_JSON_HPP_
@@ -916,6 +916,7 @@
             default:            // LCOV_EXCL_LINE
                 JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
         }
+        JSON_ASSERT(m_type == val.type());
         set_parents();
         assert_invariant();
     }
@@ -1905,7 +1906,6 @@
                                         detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
                                         detail::negation<detail::is_basic_json<ValueType>>,
                                         detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
-
 #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
                                                 detail::negation<std::is_same<ValueType, std::string_view>>,
 #endif
@@ -3538,7 +3538,6 @@
 
     /// @}
 
-  public:
     //////////////////////////////////////////
     // lexicographical comparison operators //
     //////////////////////////////////////////
@@ -3546,6 +3545,212 @@
     /// @name lexicographical comparison operators
     /// @{
 
+    // note parentheses around operands are necessary; see
+    // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \
+    const auto lhs_type = lhs.type();                                                                    \
+    const auto rhs_type = rhs.type();                                                                    \
+    \
+    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \
+    {                                                                                                    \
+        switch (lhs_type)                                                                                \
+        {                                                                                                \
+            case value_t::array:                                                                         \
+                return (*lhs.m_value.array) op (*rhs.m_value.array);                                     \
+                \
+            case value_t::object:                                                                        \
+                return (*lhs.m_value.object) op (*rhs.m_value.object);                                   \
+                \
+            case value_t::null:                                                                          \
+                return (null_result);                                                                    \
+                \
+            case value_t::string:                                                                        \
+                return (*lhs.m_value.string) op (*rhs.m_value.string);                                   \
+                \
+            case value_t::boolean:                                                                       \
+                return (lhs.m_value.boolean) op (rhs.m_value.boolean);                                   \
+                \
+            case value_t::number_integer:                                                                \
+                return (lhs.m_value.number_integer) op (rhs.m_value.number_integer);                     \
+                \
+            case value_t::number_unsigned:                                                               \
+                return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned);                   \
+                \
+            case value_t::number_float:                                                                  \
+                return (lhs.m_value.number_float) op (rhs.m_value.number_float);                         \
+                \
+            case value_t::binary:                                                                        \
+                return (*lhs.m_value.binary) op (*rhs.m_value.binary);                                   \
+                \
+            case value_t::discarded:                                                                     \
+            default:                                                                                     \
+                return (unordered_result);                                                               \
+        }                                                                                                \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float;      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer);      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float;     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned);     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \
+    {                                                                                                    \
+        return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \
+    {                                                                                                    \
+        return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
+    }                                                                                                    \
+    else if(compares_unordered(lhs, rhs))\
+    {\
+        return (unordered_result);\
+    }\
+    \
+    return (default_result);
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // returns true if:
+    // - any operand is NaN and the other operand is of number type
+    // - any operand is discarded
+    // in legacy mode, discarded values are considered ordered if
+    // an operation is computed as an odd number of inverses of others
+    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
+    {
+        if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
+                || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
+        {
+            return true;
+        }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+        static_cast<void>(inverse);
+        return lhs.is_discarded() || rhs.is_discarded();
+#endif
+    }
+
+  private:
+    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+    {
+        return compares_unordered(*this, rhs, inverse);
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    bool operator==(const_reference rhs) const noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        const_reference lhs = *this;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator==(ScalarType rhs) const noexcept
+    {
+        return *this == basic_json(rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    bool operator!=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !operator==(rhs);
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+    {
+        const_reference lhs = *this;
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types.
+        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+                                std::partial_ordering::equivalent,
+                                std::partial_ordering::unordered,
+                                lhs_type <=> rhs_type) // *NOPAD*
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+    {
+        return *this <=> basic_json(rhs); // *NOPAD*
+    }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    // all operators that are computed as an odd number of inverses of others
+    // need to be overloaded to emulate the legacy comparison behavior
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator<=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < *this);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator<=(ScalarType rhs) const noexcept
+    {
+        return *this <= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator>=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(*this < rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator>=(ScalarType rhs) const noexcept
+    {
+        return *this >= basic_json(rhs);
+    }
+#endif
+#else
     /// @brief comparison: equal
     /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
     friend bool operator==(const_reference lhs, const_reference rhs) noexcept
@@ -3554,71 +3759,7 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    return *lhs.m_value.array == *rhs.m_value.array;
-
-                case value_t::object:
-                    return *lhs.m_value.object == *rhs.m_value.object;
-
-                case value_t::null:
-                    return true;
-
-                case value_t::string:
-                    return *lhs.m_value.string == *rhs.m_value.string;
-
-                case value_t::boolean:
-                    return lhs.m_value.boolean == rhs.m_value.boolean;
-
-                case value_t::number_integer:
-                    return lhs.m_value.number_integer == rhs.m_value.number_integer;
-
-                case value_t::number_unsigned:
-                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
-
-                case value_t::number_float:
-                    return lhs.m_value.number_float == rhs.m_value.number_float;
-
-                case value_t::binary:
-                    return *lhs.m_value.binary == *rhs.m_value.binary;
-
-                case value_t::discarded:
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
-        {
-            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
-        {
-            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
-        }
-
-        return false;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
 #endif
@@ -3646,6 +3787,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
     friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(lhs == rhs);
     }
 
@@ -3671,76 +3816,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
     friend bool operator<(const_reference lhs, const_reference rhs) noexcept
     {
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    // note parentheses are necessary, see
-                    // https://github.com/nlohmann/json/issues/1530
-                    return (*lhs.m_value.array) < (*rhs.m_value.array);
-
-                case value_t::object:
-                    return (*lhs.m_value.object) < (*rhs.m_value.object);
-
-                case value_t::null:
-                    return false;
-
-                case value_t::string:
-                    return (*lhs.m_value.string) < (*rhs.m_value.string);
-
-                case value_t::boolean:
-                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);
-
-                case value_t::number_integer:
-                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);
-
-                case value_t::number_unsigned:
-                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);
-
-                case value_t::number_float:
-                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);
-
-                case value_t::binary:
-                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);
-
-                case value_t::discarded:
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
-        {
-            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
-        }
-
-        // We only reach this line if we cannot compare values. In that case,
+        // default_result is used if we cannot compare values. In that case,
         // we compare types. Note we have to call the operator explicitly,
         // because MSVC has problems otherwise.
-        return operator<(lhs_type, rhs_type);
+        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
     }
 
     /// @brief comparison: less than
@@ -3765,6 +3844,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
     friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(rhs < lhs);
     }
 
@@ -3790,6 +3873,11 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
     friend bool operator>(const_reference lhs, const_reference rhs) noexcept
     {
+        // double inverse
+        if (compares_unordered(lhs, rhs))
+        {
+            return false;
+        }
         return !(lhs <= rhs);
     }
 
@@ -3815,6 +3903,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
     friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(lhs < rhs);
     }
 
@@ -3835,6 +3927,9 @@
     {
         return basic_json(lhs) >= rhs;
     }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
 
     /// @}
 
@@ -4426,29 +4521,6 @@
         return res ? result : basic_json(value_t::discarded);
     }
 
-    template<typename T>
-    JSON_HEDLEY_WARN_UNUSED_RESULT
-    static basic_json from_bjdata(const T* ptr, std::size_t len,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        return from_bjdata(ptr, ptr + len, strict, allow_exceptions);
-    }
-
-    JSON_HEDLEY_WARN_UNUSED_RESULT
-    static basic_json from_bjdata(detail::span_input_adapter&& i,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        auto ia = i.get();
-        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
-        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-
     /// @brief create a JSON value from an input in BSON format
     /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
     template<typename InputType>
@@ -5107,10 +5179,14 @@
     @brief compare two value_t enum values
     @since version 3.0.0
     */
-    bool operator()(nlohmann::detail::value_t lhs,
-                    nlohmann::detail::value_t rhs) const noexcept
+    bool operator()(::nlohmann::detail::value_t lhs,
+                    ::nlohmann::detail::value_t rhs) const noexcept
     {
-        return nlohmann::detail::operator<(lhs, rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+        return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+        return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
     }
 };
 
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 0c4ff8a..889f29c 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -33,7 +33,7 @@
  * contains the most recent documentation and should also be applicable to  *
  * previous versions; documentation for deprecated functions is not         *
  * removed, but marked deprecated. See "Generate documentation" section in  *
- * file docs/README.md.                                                      *
+ * file docs/README.md.                                                     *
 \****************************************************************************/
 
 #ifndef INCLUDE_NLOHMANN_JSON_HPP_
@@ -103,84 +103,6 @@
 #include <cstdint> // uint8_t
 #include <string> // string
 
-namespace nlohmann
-{
-namespace detail
-{
-///////////////////////////
-// JSON type enumeration //
-///////////////////////////
-
-/*!
-@brief the JSON type enumeration
-
-This enumeration collects the different JSON types. It is internally used to
-distinguish the stored values, and the functions @ref basic_json::is_null(),
-@ref basic_json::is_object(), @ref basic_json::is_array(),
-@ref basic_json::is_string(), @ref basic_json::is_boolean(),
-@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
-@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
-@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
-@ref basic_json::is_structured() rely on it.
-
-@note There are three enumeration entries (number_integer, number_unsigned, and
-number_float), because the library distinguishes these three types for numbers:
-@ref basic_json::number_unsigned_t is used for unsigned integers,
-@ref basic_json::number_integer_t is used for signed integers, and
-@ref basic_json::number_float_t is used for floating-point numbers or to
-approximate integers which do not fit in the limits of their respective type.
-
-@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
-value with the default value for a given type
-
-@since version 1.0.0
-*/
-enum class value_t : std::uint8_t
-{
-    null,             ///< null value
-    object,           ///< object (unordered set of name/value pairs)
-    array,            ///< array (ordered collection of values)
-    string,           ///< string value
-    boolean,          ///< boolean value
-    number_integer,   ///< number value (signed integer)
-    number_unsigned,  ///< number value (unsigned integer)
-    number_float,     ///< number value (floating-point)
-    binary,           ///< binary array (ordered collection of bytes)
-    discarded         ///< discarded by the parser callback function
-};
-
-/*!
-@brief comparison operator for JSON types
-
-Returns an ordering that is similar to Python:
-- order: null < boolean < number < object < array < string < binary
-- furthermore, each type is not smaller than itself
-- discarded values are not comparable
-- binary is represented as a b"" string in python and directly comparable to a
-  string; however, making a binary array directly comparable with a string would
-  be surprising behavior in a JSON file.
-
-@since version 1.0.0
-*/
-inline bool operator<(const value_t lhs, const value_t rhs) noexcept
-{
-    static constexpr std::array<std::uint8_t, 9> order = {{
-            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
-            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
-            6 /* binary */
-        }
-    };
-
-    const auto l_index = static_cast<std::size_t>(lhs);
-    const auto r_index = static_cast<std::size_t>(rhs);
-    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
-}
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/string_escape.hpp>
-
-
 // #include <nlohmann/detail/macro_scope.hpp>
 
 
@@ -2342,6 +2264,12 @@
     #define JSON_HAS_CPP_11
 #endif
 
+#ifdef __has_include
+    #if __has_include(<version>)
+        #include <version>
+    #endif
+#endif
+
 #if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
     #ifdef JSON_HAS_CPP_17
         #if defined(__cpp_lib_filesystem)
@@ -2403,14 +2331,31 @@
 #endif
 
 #ifndef JSON_HAS_THREE_WAY_COMPARISON
-    #if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \
-        && defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L
+    #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+        && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
         #define JSON_HAS_THREE_WAY_COMPARISON 1
     #else
         #define JSON_HAS_THREE_WAY_COMPARISON 0
     #endif
 #endif
 
+#ifndef JSON_HAS_RANGES
+    // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+    #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+        #define JSON_HAS_RANGES 0
+    #elif defined(__cpp_lib_ranges)
+        #define JSON_HAS_RANGES 1
+    #else
+        #define JSON_HAS_RANGES 0
+    #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+    #define JSON_INLINE_VARIABLE inline
+#else
+    #define JSON_INLINE_VARIABLE
+#endif
+
 #if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
     #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
 #else
@@ -2735,6 +2680,121 @@
     #define JSON_DIAGNOSTICS 0
 #endif
 
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+    #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+    #include <compare> // partial_ordering
+#endif
+
+namespace nlohmann
+{
+namespace detail
+{
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
+@since version 1.0.0
+*/
+enum class value_t : std::uint8_t
+{
+    null,             ///< null value
+    object,           ///< object (unordered set of name/value pairs)
+    array,            ///< array (ordered collection of values)
+    string,           ///< string value
+    boolean,          ///< boolean value
+    number_integer,   ///< number value (signed integer)
+    number_unsigned,  ///< number value (unsigned integer)
+    number_float,     ///< number value (floating-point)
+    binary,           ///< binary array (ordered collection of bytes)
+    discarded         ///< discarded by the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string < binary
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+- binary is represented as a b"" string in python and directly comparable to a
+  string; however, making a binary array directly comparable with a string would
+  be surprising behavior in a JSON file.
+
+@since version 1.0.0
+*/
+#if JSON_HAS_THREE_WAY_COMPARISON
+    inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+    inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
+{
+    static constexpr std::array<std::uint8_t, 9> order = {{
+            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+            6 /* binary */
+        }
+    };
+
+    const auto l_index = static_cast<std::size_t>(lhs);
+    const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+    if (l_index < order.size() && r_index < order.size())
+    {
+        return order[l_index] <=> order[r_index]; // *NOPAD*
+    }
+    return std::partial_ordering::unordered;
+#else
+    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
+}
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
+}  // namespace detail
+}  // namespace nlohmann
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
 
 namespace nlohmann
 {
@@ -3318,7 +3378,7 @@
 template<class B> struct conjunction<B> : B { };
 template<class B, class... Bn>
 struct conjunction<B, Bn...>
-: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
 
 // https://en.cppreference.com/w/cpp/types/negation
 template<class B> struct negation : std::integral_constant < bool, !B::value > { };
@@ -3482,8 +3542,15 @@
 template<typename BasicJsonType, typename ConstructibleStringType>
 struct is_constructible_string_type
 {
+    // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+    using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+    using laundered_type = ConstructibleStringType;
+#endif
+
     static constexpr auto value =
-        is_constructible<ConstructibleStringType,
+        is_constructible<laundered_type,
         typename BasicJsonType::string_t>::value;
 };
 
@@ -3705,6 +3772,101 @@
     return value;
 }
 
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+      all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+         bool OfTypeSigned = std::is_signed<OfType>::value,
+         bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) >= static_cast<CommonType>(std::numeric_limits<OfType>::min())
+               && static_cast<CommonType>(val) <= static_cast<CommonType>(std::numeric_limits<OfType>::max());
+    }
+};
+
+template<typename OfType, typename T,
+         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+         typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+    static constexpr bool test(T val)
+    {
+        return value_in_range_of_impl2<OfType, T>::test(val);
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+    static constexpr bool test(T /*val*/)
+    {
+        return true;
+    }
+};
+
+template<typename OfType, typename T>
+inline constexpr bool value_in_range_of(T val)
+{
+    return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
 }  // namespace detail
 }  // namespace nlohmann
 
@@ -4126,7 +4288,7 @@
 namespace detail
 {
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
     {
@@ -4173,7 +4335,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
     {
@@ -4183,7 +4345,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -4198,7 +4360,7 @@
         std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
         && !std::is_same<typename BasicJsonType::string_t, StringType>::value
         && !is_json_ref<StringType>::value, int > = 0 >
-void from_json(const BasicJsonType& j, StringType& s)
+inline void from_json(const BasicJsonType& j, StringType& s)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -4209,36 +4371,38 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
 {
     get_arithmetic_value(j, val);
 }
 
+#if !JSON_DISABLE_ENUM_SERIALIZATION
 template<typename BasicJsonType, typename EnumType,
          enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void from_json(const BasicJsonType& j, EnumType& e)
+inline void from_json(const BasicJsonType& j, EnumType& e)
 {
     typename std::underlying_type<EnumType>::type val;
     get_arithmetic_value(j, val);
     e = static_cast<EnumType>(val);
 }
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
 
 // forward_list doesn't have an insert method
 template<typename BasicJsonType, typename T, typename Allocator,
          enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -4255,7 +4419,7 @@
 // valarray doesn't have an insert method
 template<typename BasicJsonType, typename T,
          enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::valarray<T>& l)
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -4280,7 +4444,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
 {
     arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
 }
@@ -4324,8 +4488,8 @@
          enable_if_t<
              std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
              int> = 0>
-void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
-                          priority_tag<0> /*unused*/)
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+                                 priority_tag<0> /*unused*/)
 {
     using std::end;
 
@@ -4382,7 +4546,7 @@
 }
 
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
     {
@@ -4394,7 +4558,7 @@
 
 template<typename BasicJsonType, typename ConstructibleObjectType,
          enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
-void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
     {
@@ -4426,7 +4590,7 @@
                !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
                !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
                int > = 0 >
-void from_json(const BasicJsonType& j, ArithmeticType& val)
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
 {
     switch (static_cast<value_t>(j))
     {
@@ -4476,7 +4640,7 @@
 }
 
 template<typename BasicJsonType, typename A1, typename A2>
-void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
 {
     p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
 }
@@ -4488,7 +4652,7 @@
 }
 
 template<typename BasicJsonType, typename... Args>
-void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
 {
     t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
 }
@@ -4508,7 +4672,7 @@
 template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
            typename = enable_if_t < !std::is_constructible <
                                         typename BasicJsonType::string_t, Key >::value >>
-void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -4528,7 +4692,7 @@
 template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
            typename = enable_if_t < !std::is_constructible <
                                         typename BasicJsonType::string_t, Key >::value >>
-void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
     {
@@ -4547,7 +4711,7 @@
 
 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
 template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, std_fs::path& p)
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
 {
     if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
     {
@@ -4569,13 +4733,18 @@
 };
 }  // namespace detail
 
+#ifndef JSON_HAS_CPP_17
 /// namespace to hold default `from_json` function
 /// to see why this is required:
 /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
 namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
 {
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
 } // namespace
+#endif
 } // namespace nlohmann
 
 // #include <nlohmann/detail/conversions/to_json.hpp>
@@ -4601,6 +4770,10 @@
 #include <tuple> // tuple_size, get, tuple_element
 #include <utility> // move
 
+#if JSON_HAS_RANGES
+    #include <ranges> // enable_borrowed_range
+#endif
+
 // #include <nlohmann/detail/meta/type_traits.hpp>
 
 // #include <nlohmann/detail/value_t.hpp>
@@ -4622,14 +4795,14 @@
   public:
     using difference_type = std::ptrdiff_t;
     using value_type = iteration_proxy_value;
-    using pointer = value_type * ;
-    using reference = value_type & ;
+    using pointer = value_type *;
+    using reference = value_type &;
     using iterator_category = std::input_iterator_tag;
     using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
 
   private:
     /// the iterator
-    IteratorType anchor;
+    IteratorType anchor{};
     /// an index for arrays (used to create key names)
     std::size_t array_index = 0;
     /// last stringified array index
@@ -4637,15 +4810,30 @@
     /// a string representation of the array index
     mutable string_type array_index_str = "0";
     /// an empty string (to return a reference for primitive values)
-    const string_type empty_str{};
+    string_type empty_str{};
 
   public:
-    explicit iteration_proxy_value(IteratorType it) noexcept
+    explicit iteration_proxy_value() = default;
+    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_default_constructible<string_type>::value)
         : anchor(std::move(it))
+        , array_index(array_index_)
     {}
 
+    iteration_proxy_value(iteration_proxy_value const&) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+    iteration_proxy_value(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_move_constructible<string_type>::value) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+             && std::is_nothrow_move_assignable<string_type>::value) = default;
+    ~iteration_proxy_value() = default;
+
     /// dereference operator (needed for range-based for)
-    iteration_proxy_value& operator*()
+    const iteration_proxy_value& operator*() const
     {
         return *this;
     }
@@ -4659,6 +4847,14 @@
         return *this;
     }
 
+    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto tmp = iteration_proxy_value(anchor, array_index);
+        ++anchor;
+        ++array_index;
+        return tmp;
+    }
+
     /// equality operator (needed for InputIterator)
     bool operator==(const iteration_proxy_value& o) const
     {
@@ -4719,25 +4915,34 @@
 {
   private:
     /// the container to iterate
-    typename IteratorType::reference container;
+    typename IteratorType::pointer container = nullptr;
 
   public:
+    explicit iteration_proxy() = default;
+
     /// construct iteration proxy from a container
     explicit iteration_proxy(typename IteratorType::reference cont) noexcept
-        : container(cont) {}
+        : container(&cont) {}
+
+    iteration_proxy(iteration_proxy const&) = default;
+    iteration_proxy& operator=(iteration_proxy const&) = default;
+    iteration_proxy(iteration_proxy&&) noexcept = default;
+    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+    ~iteration_proxy() = default;
 
     /// return iterator begin (needed for range-based for)
-    iteration_proxy_value<IteratorType> begin() noexcept
+    iteration_proxy_value<IteratorType> begin() const noexcept
     {
-        return iteration_proxy_value<IteratorType>(container.begin());
+        return iteration_proxy_value<IteratorType>(container->begin());
     }
 
     /// return iterator end (needed for range-based for)
-    iteration_proxy_value<IteratorType> end() noexcept
+    iteration_proxy_value<IteratorType> end() const noexcept
     {
-        return iteration_proxy_value<IteratorType>(container.end());
+        return iteration_proxy_value<IteratorType>(container->end());
     }
 };
+
 // Structured Bindings Support
 // For further reference see https://blog.tartanllama.xyz/structured-bindings/
 // And see https://github.com/nlohmann/json/pull/1391
@@ -4785,6 +4990,11 @@
 #endif
 } // namespace std
 
+#if JSON_HAS_RANGES
+    template <typename IteratorType>
+    inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
+#endif
+
 // #include <nlohmann/detail/meta/cpp_future.hpp>
 
 // #include <nlohmann/detail/meta/type_traits.hpp>
@@ -5044,55 +5254,64 @@
 
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
-void to_json(BasicJsonType& j, T b) noexcept
+inline void to_json(BasicJsonType& j, T b) noexcept
 {
     external_constructor<value_t::boolean>::construct(j, b);
 }
 
+template<typename BasicJsonType,
+         enable_if_t<std::is_convertible<const std::vector<bool>::reference&, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::vector<bool>::reference& b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
 template<typename BasicJsonType, typename CompatibleString,
          enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleString& s)
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
 {
     external_constructor<value_t::string>::construct(j, s);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
 {
     external_constructor<value_t::string>::construct(j, std::move(s));
 }
 
 template<typename BasicJsonType, typename FloatType,
          enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
-void to_json(BasicJsonType& j, FloatType val) noexcept
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
 {
     external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
 }
 
 template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
          enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
 {
     external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
 }
 
 template<typename BasicJsonType, typename CompatibleNumberIntegerType,
          enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
 {
     external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
 }
 
+#if !JSON_DISABLE_ENUM_SERIALIZATION
 template<typename BasicJsonType, typename EnumType,
          enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void to_json(BasicJsonType& j, EnumType e) noexcept
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
 {
     using underlying_type = typename std::underlying_type<EnumType>::type;
     external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
 }
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std::vector<bool>& e)
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
 {
     external_constructor<value_t::array>::construct(j, e);
 }
@@ -5105,39 +5324,39 @@
                          !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
                          !is_basic_json<CompatibleArrayType>::value,
                          int > = 0 >
-void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
 {
     external_constructor<value_t::array>::construct(j, arr);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
 {
     external_constructor<value_t::binary>::construct(j, bin);
 }
 
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
-void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
 {
     external_constructor<value_t::array>::construct(j, std::move(arr));
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
 {
     external_constructor<value_t::array>::construct(j, std::move(arr));
 }
 
 template < typename BasicJsonType, typename CompatibleObjectType,
            enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
-void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
 {
     external_constructor<value_t::object>::construct(j, obj);
 }
 
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
 {
     external_constructor<value_t::object>::construct(j, std::move(obj));
 }
@@ -5147,13 +5366,13 @@
     enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
                   const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
                   int > = 0 >
-void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
 {
     external_constructor<value_t::array>::construct(j, arr);
 }
 
 template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
-void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
 {
     j = { p.first, p.second };
 }
@@ -5161,26 +5380,26 @@
 // for https://github.com/nlohmann/json/pull/1134
 template<typename BasicJsonType, typename T,
          enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
-void to_json(BasicJsonType& j, const T& b)
+inline void to_json(BasicJsonType& j, const T& b)
 {
     j = { {b.key(), b.value()} };
 }
 
 template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
 {
     j = { std::get<Idx>(t)... };
 }
 
 template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
-void to_json(BasicJsonType& j, const T& t)
+inline void to_json(BasicJsonType& j, const T& t)
 {
     to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
 }
 
 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
 template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std_fs::path& p)
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
 {
     j = p.string();
 }
@@ -5197,13 +5416,18 @@
 };
 }  // namespace detail
 
+#ifndef JSON_HAS_CPP_17
 /// namespace to hold default `to_json` function
 /// to see why this is required:
 /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
 namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
 {
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
 } // namespace
+#endif
 } // namespace nlohmann
 
 // #include <nlohmann/detail/meta/identity_tag.hpp>
@@ -6223,6 +6447,9 @@
 
     bool key(string_t& val)
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
         // add null at given key and store the reference for later
         object_element = &(ref_stack.back()->m_value.object->operator[](val));
         return true;
@@ -6230,6 +6457,9 @@
 
     bool end_object()
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
         ref_stack.back()->set_parents();
         ref_stack.pop_back();
         return true;
@@ -6249,6 +6479,9 @@
 
     bool end_array()
     {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_array());
+
         ref_stack.back()->set_parents();
         ref_stack.pop_back();
         return true;
@@ -9089,7 +9322,8 @@
             case 0x95:
             case 0x96:
             case 0x97:
-                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+                return get_cbor_array(
+                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
 
             case 0x98: // array (one-byte uint8_t for n follows)
             {
@@ -9106,13 +9340,13 @@
             case 0x9A: // array (four-byte uint32_t for n follow)
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0x9B: // array (eight-byte uint64_t for n follow)
             {
                 std::uint64_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0x9F: // array (indefinite length)
@@ -9143,7 +9377,7 @@
             case 0xB5:
             case 0xB6:
             case 0xB7:
-                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+                return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
 
             case 0xB8: // map (one-byte uint8_t for n follows)
             {
@@ -9160,13 +9394,13 @@
             case 0xBA: // map (four-byte uint32_t for n follow)
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0xBB: // map (eight-byte uint64_t for n follow)
             {
                 std::uint64_t len{};
-                return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast<std::size_t>(len), tag_handler);
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
             }
 
             case 0xBF: // map (indefinite length)
@@ -9813,7 +10047,7 @@
             case 0x8D:
             case 0x8E:
             case 0x8F:
-                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
 
             // fixarray
             case 0x90:
@@ -9832,7 +10066,7 @@
             case 0x9D:
             case 0x9E:
             case 0x9F:
-                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
 
             // fixstr
             case 0xA0:
@@ -9969,7 +10203,7 @@
             case 0xDD: // array 32
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
             }
 
             case 0xDE: // map 16
@@ -9981,7 +10215,7 @@
             case 0xDF: // map 32
             {
                 std::uint32_t len{};
-                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
             }
 
             // negative fixint
@@ -10409,8 +10643,9 @@
     {
         std::pair<std::size_t, char_int_type> size_and_type;
         size_t dimlen = 0;
+        bool no_ndarray = true;
 
-        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
         {
             return false;
         }
@@ -10423,7 +10658,7 @@
                 {
                     for (std::size_t i = 0; i < size_and_type.first; ++i)
                     {
-                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, size_and_type.second)))
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
                         {
                             return false;
                         }
@@ -10435,7 +10670,7 @@
             {
                 for (std::size_t i = 0; i < size_and_type.first; ++i)
                 {
-                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen)))
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
                     {
                         return false;
                     }
@@ -10447,7 +10682,7 @@
         {
             while (current != ']')
             {
-                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, current)))
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
                 {
                     return false;
                 }
@@ -10460,9 +10695,16 @@
 
     /*!
     @param[out] result  determined size
+    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector
+                               or ndarray dimension is not allowed; `false` means ndarray
+                               is allowed; for output, `true` means an ndarray is found;
+                               is_ndarray can only return `true` when its initial value
+                               is `false`
+    @param[in] prefix  type marker if already read, otherwise set to 0
+
     @return whether size determination completed
     */
-    bool get_ubjson_size_value(std::size_t& result, char_int_type prefix = 0)
+    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
     {
         if (prefix == 0)
         {
@@ -10489,6 +10731,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
                 return true;
             }
@@ -10500,6 +10747,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -10511,6 +10763,11 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -10522,6 +10779,16 @@
                 {
                     return false;
                 }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
                 result = static_cast<std::size_t>(number);
                 return true;
             }
@@ -10552,7 +10819,7 @@
                 {
                     return false;
                 }
-                result = static_cast<std::size_t>(number);
+                result = conditional_static_cast<std::size_t>(number);
                 return true;
             }
 
@@ -10567,6 +10834,11 @@
                 {
                     return false;
                 }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
                 result = detail::conditional_static_cast<std::size_t>(number);
                 return true;
             }
@@ -10577,6 +10849,10 @@
                 {
                     break;
                 }
+                if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimentional vector is not allowed", "size"), nullptr));
+                }
                 std::vector<size_t> dim;
                 if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
                 {
@@ -10589,6 +10865,15 @@
                 }
                 if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format
                 {
+                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+                    {
+                        if ( i == 0 )
+                        {
+                            result = 0;
+                            return true;
+                        }
+                    }
+
                     string_t key = "_ArraySize_";
                     if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
                     {
@@ -10598,12 +10883,16 @@
                     for (auto i : dim)
                     {
                         result *= i;
-                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(static_cast<number_integer_t>(i))))
+                        if (result == 0 || result == string_t::npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be string_t::npos as it is used to initialize size in get_ubjson_size_type()
+                        {
+                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
                         {
                             return false;
                         }
                     }
-                    result |= (1ull << (sizeof(result) * 8 - 1)); // low 63 bit of result stores the total element count, sign-bit indicates ndarray
+                    is_ndarray = true;
                     return sax->end_array();
                 }
                 result = 0;
@@ -10634,13 +10923,15 @@
     for a more compact representation.
 
     @param[out] result  pair of the size and the type
+    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector
 
     @return whether pair creation completed
     */
-    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)
+    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
     {
         result.first = string_t::npos; // size
         result.second = 0; // type
+        bool is_ndarray = false;
 
         get_ignore_noop();
 
@@ -10649,7 +10940,14 @@
             std::vector<char_int_type> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
 
             result.second = get();  // must not ignore 'N', because 'N' maybe the type
-            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type") || (input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() )))
+            if (JSON_HEDLEY_UNLIKELY( input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() ))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
             {
                 return false;
             }
@@ -10666,12 +10964,28 @@
                                         exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
             }
 
-            return get_ubjson_size_value(result.first);
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                if (inside_ndarray)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                            exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+                }
+                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+            }
+            return is_error;
         }
 
         if (current == '#')
         {
-            return get_ubjson_size_value(result.first);
+            bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                        exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+            }
+            return is_error;
         }
 
         return true;
@@ -10872,17 +11186,26 @@
             return false;
         }
 
-        // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
         // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
 
-        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1)))
+        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && (size_and_type.second & (1 << 8)) != 0)
         {
             std::map<char_int_type, string_t> bjdtype = {{'U', "uint8"},  {'i', "int8"},  {'u', "uint16"}, {'I', "int16"},
                 {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"}
             };
 
+            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
+
             string_t key = "_ArrayType_";
-            if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0 || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) ))
+            if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0))
+            {
+                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));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(bjdtype[size_and_type.second]) ))
             {
                 return false;
             }
@@ -10892,7 +11215,6 @@
                 size_and_type.second = 'U';
             }
 
-            size_and_type.first &= ~(1ull << (sizeof(std::size_t) * 8 - 1));
             key = "_ArrayData_";
             if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
             {
@@ -10972,9 +11294,12 @@
             return false;
         }
 
-        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1)))
+        // do not accept ND-array size in objects in BJData
+        if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && (size_and_type.second & (1 << 8)) != 0)
         {
-            return false;
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
         }
 
         string_t key;
@@ -11048,7 +11373,8 @@
     {
         // get size of following number string
         std::size_t size{};
-        auto res = get_ubjson_size_value(size);
+        bool no_ndarray = true;
+        auto res = get_ubjson_size_value(size, no_ndarray);
         if (JSON_HEDLEY_UNLIKELY(!res))
         {
             return res;
@@ -12368,9 +12694,12 @@
     // make sure BasicJsonType is basic_json or const basic_json
     static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
                   "iter_impl only accepts (const) basic_json");
+    // superficial check for the LegacyBidirectionalIterator named requirement
+    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+                  "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
 
   public:
-
     /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
     /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
     /// A user-defined iterator should provide publicly accessible typedefs named
@@ -19608,6 +19937,7 @@
             default:            // LCOV_EXCL_LINE
                 JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
         }
+        JSON_ASSERT(m_type == val.type());
         set_parents();
         assert_invariant();
     }
@@ -20597,7 +20927,6 @@
                                         detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
                                         detail::negation<detail::is_basic_json<ValueType>>,
                                         detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
-
 #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
                                                 detail::negation<std::is_same<ValueType, std::string_view>>,
 #endif
@@ -22230,7 +22559,6 @@
 
     /// @}
 
-  public:
     //////////////////////////////////////////
     // lexicographical comparison operators //
     //////////////////////////////////////////
@@ -22238,6 +22566,212 @@
     /// @name lexicographical comparison operators
     /// @{
 
+    // note parentheses around operands are necessary; see
+    // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \
+    const auto lhs_type = lhs.type();                                                                    \
+    const auto rhs_type = rhs.type();                                                                    \
+    \
+    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \
+    {                                                                                                    \
+        switch (lhs_type)                                                                                \
+        {                                                                                                \
+            case value_t::array:                                                                         \
+                return (*lhs.m_value.array) op (*rhs.m_value.array);                                     \
+                \
+            case value_t::object:                                                                        \
+                return (*lhs.m_value.object) op (*rhs.m_value.object);                                   \
+                \
+            case value_t::null:                                                                          \
+                return (null_result);                                                                    \
+                \
+            case value_t::string:                                                                        \
+                return (*lhs.m_value.string) op (*rhs.m_value.string);                                   \
+                \
+            case value_t::boolean:                                                                       \
+                return (lhs.m_value.boolean) op (rhs.m_value.boolean);                                   \
+                \
+            case value_t::number_integer:                                                                \
+                return (lhs.m_value.number_integer) op (rhs.m_value.number_integer);                     \
+                \
+            case value_t::number_unsigned:                                                               \
+                return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned);                   \
+                \
+            case value_t::number_float:                                                                  \
+                return (lhs.m_value.number_float) op (rhs.m_value.number_float);                         \
+                \
+            case value_t::binary:                                                                        \
+                return (*lhs.m_value.binary) op (*rhs.m_value.binary);                                   \
+                \
+            case value_t::discarded:                                                                     \
+            default:                                                                                     \
+                return (unordered_result);                                                               \
+        }                                                                                                \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float;      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer);      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float;     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \
+    {                                                                                                    \
+        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned);     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \
+    {                                                                                                    \
+        return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \
+    {                                                                                                    \
+        return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
+    }                                                                                                    \
+    else if(compares_unordered(lhs, rhs))\
+    {\
+        return (unordered_result);\
+    }\
+    \
+    return (default_result);
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // returns true if:
+    // - any operand is NaN and the other operand is of number type
+    // - any operand is discarded
+    // in legacy mode, discarded values are considered ordered if
+    // an operation is computed as an odd number of inverses of others
+    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
+    {
+        if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
+                || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
+        {
+            return true;
+        }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+        static_cast<void>(inverse);
+        return lhs.is_discarded() || rhs.is_discarded();
+#endif
+    }
+
+  private:
+    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+    {
+        return compares_unordered(*this, rhs, inverse);
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    bool operator==(const_reference rhs) const noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        const_reference lhs = *this;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator==(ScalarType rhs) const noexcept
+    {
+        return *this == basic_json(rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    bool operator!=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !operator==(rhs);
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+    {
+        const_reference lhs = *this;
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types.
+        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+                                std::partial_ordering::equivalent,
+                                std::partial_ordering::unordered,
+                                lhs_type <=> rhs_type) // *NOPAD*
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+    {
+        return *this <=> basic_json(rhs); // *NOPAD*
+    }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    // all operators that are computed as an odd number of inverses of others
+    // need to be overloaded to emulate the legacy comparison behavior
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator<=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < *this);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator<=(ScalarType rhs) const noexcept
+    {
+        return *this <= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator>=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(*this < rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator>=(ScalarType rhs) const noexcept
+    {
+        return *this >= basic_json(rhs);
+    }
+#endif
+#else
     /// @brief comparison: equal
     /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
     friend bool operator==(const_reference lhs, const_reference rhs) noexcept
@@ -22246,71 +22780,7 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    return *lhs.m_value.array == *rhs.m_value.array;
-
-                case value_t::object:
-                    return *lhs.m_value.object == *rhs.m_value.object;
-
-                case value_t::null:
-                    return true;
-
-                case value_t::string:
-                    return *lhs.m_value.string == *rhs.m_value.string;
-
-                case value_t::boolean:
-                    return lhs.m_value.boolean == rhs.m_value.boolean;
-
-                case value_t::number_integer:
-                    return lhs.m_value.number_integer == rhs.m_value.number_integer;
-
-                case value_t::number_unsigned:
-                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
-
-                case value_t::number_float:
-                    return lhs.m_value.number_float == rhs.m_value.number_float;
-
-                case value_t::binary:
-                    return *lhs.m_value.binary == *rhs.m_value.binary;
-
-                case value_t::discarded:
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
-        {
-            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
-        {
-            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
-        }
-
-        return false;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
 #endif
@@ -22338,6 +22808,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
     friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(lhs == rhs);
     }
 
@@ -22363,76 +22837,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
     friend bool operator<(const_reference lhs, const_reference rhs) noexcept
     {
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    // note parentheses are necessary, see
-                    // https://github.com/nlohmann/json/issues/1530
-                    return (*lhs.m_value.array) < (*rhs.m_value.array);
-
-                case value_t::object:
-                    return (*lhs.m_value.object) < (*rhs.m_value.object);
-
-                case value_t::null:
-                    return false;
-
-                case value_t::string:
-                    return (*lhs.m_value.string) < (*rhs.m_value.string);
-
-                case value_t::boolean:
-                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);
-
-                case value_t::number_integer:
-                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);
-
-                case value_t::number_unsigned:
-                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);
-
-                case value_t::number_float:
-                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);
-
-                case value_t::binary:
-                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);
-
-                case value_t::discarded:
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
-        {
-            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
-        }
-
-        // We only reach this line if we cannot compare values. In that case,
+        // default_result is used if we cannot compare values. In that case,
         // we compare types. Note we have to call the operator explicitly,
         // because MSVC has problems otherwise.
-        return operator<(lhs_type, rhs_type);
+        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
     }
 
     /// @brief comparison: less than
@@ -22457,6 +22865,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
     friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(rhs < lhs);
     }
 
@@ -22482,6 +22894,11 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
     friend bool operator>(const_reference lhs, const_reference rhs) noexcept
     {
+        // double inverse
+        if (compares_unordered(lhs, rhs))
+        {
+            return false;
+        }
         return !(lhs <= rhs);
     }
 
@@ -22507,6 +22924,10 @@
     /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
     friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
     {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
         return !(lhs < rhs);
     }
 
@@ -22527,6 +22948,9 @@
     {
         return basic_json(lhs) >= rhs;
     }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
 
     /// @}
 
@@ -23118,29 +23542,6 @@
         return res ? result : basic_json(value_t::discarded);
     }
 
-    template<typename T>
-    JSON_HEDLEY_WARN_UNUSED_RESULT
-    static basic_json from_bjdata(const T* ptr, std::size_t len,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        return from_bjdata(ptr, ptr + len, strict, allow_exceptions);
-    }
-
-    JSON_HEDLEY_WARN_UNUSED_RESULT
-    static basic_json from_bjdata(detail::span_input_adapter&& i,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        auto ia = i.get();
-        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
-        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-
     /// @brief create a JSON value from an input in BSON format
     /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
     template<typename InputType>
@@ -23799,10 +24200,14 @@
     @brief compare two value_t enum values
     @since version 3.0.0
     */
-    bool operator()(nlohmann::detail::value_t lhs,
-                    nlohmann::detail::value_t rhs) const noexcept
+    bool operator()(::nlohmann::detail::value_t lhs,
+                    ::nlohmann::detail::value_t rhs) const noexcept
     {
-        return nlohmann::detail::operator<(lhs, rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+        return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+        return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
     }
 };
 
@@ -23856,7 +24261,9 @@
 #undef NLOHMANN_BASIC_JSON_TPL
 #undef JSON_EXPLICIT
 #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
 #undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
 
 #ifndef JSON_TEST_KEEP_MACROS
     #undef JSON_CATCH
@@ -23868,6 +24275,8 @@
     #undef JSON_HAS_FILESYSTEM
     #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
     #undef JSON_HAS_THREE_WAY_COMPARISON
+    #undef JSON_HAS_RANGES
+    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 #endif
 
 // #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0df890a..8f9240f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -3,6 +3,7 @@
 option(JSON_Valgrind    "Execute test suite with Valgrind." OFF)
 option(JSON_FastTests   "Skip expensive/slow tests." OFF)
 
+set(JSON_32bitTest     AUTO CACHE STRING "Enable the 32bit unit test (ON/OFF/AUTO/ONLY).")
 set(JSON_TestStandards "" CACHE STRING "The list of standards to test explicitly.")
 
 include(test)
@@ -36,8 +37,7 @@
 add_library(test_main OBJECT src/unit.cpp)
 target_compile_definitions(test_main PUBLIC
     DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-    JSON_TEST_KEEP_MACROS
-)
+    JSON_TEST_KEEP_MACROS)
 target_compile_features(test_main PRIVATE cxx_std_11)
 target_compile_options(test_main PUBLIC
     $<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>>
@@ -49,14 +49,16 @@
     # https://github.com/nlohmann/json/issues/1114
     $<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
 
+    # https://github.com/nlohmann/json/pull/3229
+    $<$<CXX_COMPILER_ID:Intel>:-diag-disable=2196>
+
     $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated;-Wno-float-equal>
     $<$<CXX_COMPILER_ID:GNU>:-Wno-deprecated-declarations>
-)
+    $<$<CXX_COMPILER_ID:Intel>:-diag-disable=1786>)
 target_include_directories(test_main PUBLIC
     thirdparty/doctest
     thirdparty/fifo_map
-    ${PROJECT_BINARY_DIR}/include
-)
+    ${PROJECT_BINARY_DIR}/include)
 target_link_libraries(test_main PUBLIC ${NLOHMANN_JSON_TARGET_NAME})
 
 #############################################################################
@@ -114,11 +116,32 @@
 
 # *DO* use json_test_set_test_options() above this line
 
+json_test_should_build_32bit_test(json_32bit_test json_32bit_test_only "${JSON_32bitTest}")
 file(GLOB files src/unit-*.cpp)
+if(json_32bit_test_only)
+    set(files src/unit-32bit.cpp)    
+elseif(NOT json_32bit_test)
+    list(FILTER files EXCLUDE REGEX src/unit-32bit.cpp)
+endif()
+
 foreach(file ${files})
     json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force})
 endforeach()
 
+if(json_32bit_test_only)
+    # Skip all other tests in this file
+    return()
+endif()
+
+# test legacy comparison of discarded values
+json_test_set_test_options(test-comparison_legacy
+    COMPILE_DEFINITIONS JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1
+)
+json_test_add_test_for(src/unit-comparison.cpp
+    NAME test-comparison_legacy
+    MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
+)
+
 # *DO NOT* use json_test_set_test_options() below this line
 
 #############################################################################
diff --git a/tests/fuzzing.md b/tests/fuzzing.md
new file mode 100644
index 0000000..cfbf4f2
--- /dev/null
+++ b/tests/fuzzing.md
@@ -0,0 +1,81 @@
+# Fuzz testing
+
+Each parser of the library (JSON, BJData, BSON, CBOR, MessagePack, and UBJSON) can be fuzz tested. Currently,
+[libFuzzer](https://llvm.org/docs/LibFuzzer.html) and [afl++](https://github.com/AFLplusplus/AFLplusplus) are supported.
+
+## Corpus creation
+
+For most effective fuzzing, a [corpus](https://llvm.org/docs/LibFuzzer.html#corpus) should be provided. A corpus is a
+directory with some simple input files that cover several features of the parser and is hence a good starting point
+for mutations.
+
+```shell
+TEST_DATA_VERSION=3.1.0
+wget https://github.com/nlohmann/json_test_data/archive/refs/tags/v$TEST_DATA_VERSION.zip
+unzip v$TEST_DATA_VERSION.zip
+rm v$TEST_DATA_VERSION.zip
+for FORMAT in json bjdata bson cbor msgpack ubjson
+do
+  rm -fr corpus_$FORMAT
+  mkdir corpus_$FORMAT
+  find json_test_data-$TEST_DATA_VERSION -size -5k -name "*.$FORMAT" -exec cp "{}" "corpus_$FORMAT" \;
+done
+rm -fr json_test_data-$TEST_DATA_VERSION
+```
+
+The generated corpus can be used with both libFuzzer and afl++. The remainder of this documentation assumes the corpus
+directories have been created in the `tests` directory.
+
+## libFuzzer
+
+To use libFuzzer, you need to pass `-fsanitize=fuzzer` as `FUZZER_ENGINE`. In the `tests` directory, call
+
+```shell
+make fuzzers FUZZER_ENGINE="-fsanitize=fuzzer"
+```
+
+This creates a fuzz tester binary for each parser that supports these
+[command line options](https://llvm.org/docs/LibFuzzer.html#options).
+
+In case your default compiler is not a Clang compiler that includes libFuzzer (Clang 6.0 or later), you need to set the
+`CXX` variable accordingly. Note the compiler provided by Xcode (AppleClang) does not contain libFuzzer. Please install
+Clang via Homebrew calling `brew install llvm` and add `CXX=$(brew --prefix llvm)/bin/clang` to the `make` call:
+
+```shell
+make fuzzers FUZZER_ENGINE="-fsanitize=fuzzer" CXX=$(brew --prefix llvm)/bin/clang
+```
+
+Then pass the corpus directory as command-line argument (assuming it is located in `tests`):
+
+```shell
+./parse_cbor_fuzzer corpus_cbor
+```
+
+The fuzzer should be able to run indefinitely without crashing. In case of a crash, the tested input is dumped into
+a file starting with `crash-`.
+
+## afl++
+
+To use afl++, you need to pass `-fsanitize=fuzzer` as `FUZZER_ENGINE`. It will be replaced by a `libAFLDriver.a` to
+re-use the same code written for libFuzzer with afl++. Furthermore, set `afl-clang-fast++` as compiler.
+
+```shell
+CXX=afl-clang-fast++ make fuzzers FUZZER_ENGINE="-fsanitize=fuzzer" 
+```
+
+Then the fuzzer is called like this in the `tests` directory:
+
+```shell
+afl-fuzz -i corpus_cbor -o out  -- ./parse_cbor_fuzzer 
+```
+
+The fuzzer should be able to run indefinitely without crashing. In case of a crash, the tested input is written to the
+directory `out`.
+
+## OSS-Fuzz
+
+The library is further fuzz-tested 24/7 by Google's [OSS-Fuzz project](https://github.com/google/oss-fuzz). It uses
+the same `fuzzers` target as above and also relies on the `FUZZER_ENGINE` variable. See the used
+[build script](https://github.com/google/oss-fuzz/blob/master/projects/json/build.sh) for more information.
+
+In case the build at OSS-Fuzz fails, an issue will be created automatically.
diff --git a/tests/src/unit-32bit.cpp b/tests/src/unit-32bit.cpp
new file mode 100644
index 0000000..cbe07b4
--- /dev/null
+++ b/tests/src/unit-32bit.cpp
@@ -0,0 +1,128 @@
+#include "doctest_compatibility.h"
+
+#include <nlohmann/json.hpp>
+using nlohmann::json;
+
+#include <climits> // SIZE_MAX
+#include <limits> // numeric_limits
+
+
+template <typename OfType, typename T, bool MinInRange, bool MaxInRange>
+struct trait_test_arg
+{
+    using of_type = OfType;
+    using type = T;
+    static constexpr bool min_in_range = MinInRange;
+    static constexpr bool max_in_range = MaxInRange;
+};
+
+TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test)
+{
+    using nlohmann::detail::value_in_range_of;
+
+    using of_type = typename T::of_type;
+    using type = typename T::type;
+    constexpr bool min_in_range = T::min_in_range;
+    constexpr bool max_in_range = T::max_in_range;
+
+    type val_min = std::numeric_limits<type>::min();
+    type val_min2 = val_min + 1;
+    type val_max = std::numeric_limits<type>::max();
+    type val_max2 = val_max - 1;
+
+    REQUIRE(CHAR_BIT == 8);
+
+    std::string of_type_str;
+    if (std::is_unsigned<of_type>::value)
+    {
+        of_type_str += "u";
+    }
+    of_type_str += "int";
+    of_type_str += std::to_string(sizeof(of_type) * 8);
+
+    INFO("of_type := ", of_type_str);
+
+    std::string type_str;
+    if (std::is_unsigned<type>::value)
+    {
+        type_str += "u";
+    }
+    type_str += "int";
+    type_str += std::to_string(sizeof(type) * 8);
+
+    INFO("type := ", type_str);
+
+    CAPTURE(val_min);
+    CAPTURE(min_in_range);
+    CAPTURE(val_max);
+    CAPTURE(max_in_range);
+
+    if (min_in_range)
+    {
+        CHECK(value_in_range_of<of_type>(val_min));
+        CHECK(value_in_range_of<of_type>(val_min2));
+    }
+    else
+    {
+        CHECK_FALSE(value_in_range_of<of_type>(val_min));
+        CHECK_FALSE(value_in_range_of<of_type>(val_min2));
+    }
+
+    if (max_in_range)
+    {
+        CHECK(value_in_range_of<of_type>(val_max));
+        CHECK(value_in_range_of<of_type>(val_max2));
+    }
+    else
+    {
+        CHECK_FALSE(value_in_range_of<of_type>(val_max));
+        CHECK_FALSE(value_in_range_of<of_type>(val_max2));
+    }
+}
+
+
+TEST_CASE("32bit")
+{
+    REQUIRE(SIZE_MAX == 0xffffffff);
+}
+
+TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
+                          trait_test_arg<std::size_t, std::int32_t, false, true>, \
+                          trait_test_arg<std::size_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::size_t, std::int64_t, false, false>, \
+                          trait_test_arg<std::size_t, std::uint64_t, true, false>);
+
+TEST_CASE("BJData")
+{
+    SECTION("parse errors")
+    {
+        SECTION("array")
+        {
+            SECTION("optimized array: negative size")
+            {
+                std::vector<uint8_t> vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
+                std::vector<uint8_t> vMX = {'[', '$', 'U', '#', '[', 'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'U', 0x01, ']'};
+
+                json _;
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vM, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vMX, true, false).is_discarded());
+            }
+
+            SECTION("optimized array: integer value overflow")
+            {
+                std::vector<uint8_t> vL = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F};
+                std::vector<uint8_t> vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
+
+                json _;
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vL, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vM, true, false).is_discarded());
+            }
+        }
+    }
+}
diff --git a/tests/src/unit-binary_formats.cpp b/tests/src/unit-binary_formats.cpp
index c839250..05e889b 100644
--- a/tests/src/unit-binary_formats.cpp
+++ b/tests/src/unit-binary_formats.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 3.10.2
+|  |  |__   |  |  | | | |  version 3.10.5
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -43,40 +43,37 @@
         json j = json::parse(std::ifstream(filename));
 
         const auto json_size = j.dump().size();
+        const auto bjdata_1_size = json::to_bjdata(j).size();
+        const auto bjdata_2_size = json::to_bjdata(j, true).size();
+        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
         const auto bson_size = json::to_bson(j).size();
         const auto cbor_size = json::to_cbor(j).size();
         const auto msgpack_size = json::to_msgpack(j).size();
         const auto ubjson_1_size = json::to_ubjson(j).size();
         const auto ubjson_2_size = json::to_ubjson(j, true).size();
         const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
-        const auto bjdata_1_size = json::to_bjdata(j).size();
-        const auto bjdata_2_size = json::to_bjdata(j, true).size();
-        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
-        const auto bon8_size = json::to_bon8(j).size();
 
         CHECK(json_size == 2090303);
+        CHECK(bjdata_1_size == 1112030);
+        CHECK(bjdata_2_size == 1224148);
+        CHECK(bjdata_3_size == 1224148);
         CHECK(bson_size == 1794522);
         CHECK(cbor_size == 1055552);
         CHECK(msgpack_size == 1056145);
         CHECK(ubjson_1_size == 1112030);
         CHECK(ubjson_2_size == 1224148);
         CHECK(ubjson_3_size == 1169069);
-        CHECK(bjdata_1_size == 1112030);
-        CHECK(bjdata_2_size == 1224148);
-        CHECK(bjdata_3_size == 1224148);
-        CHECK(bon8_size == 1055789);
 
         CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
+        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(53.199));
+        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(58.563));
+        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(58.563));
         CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(85.849));
         CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(50.497));
         CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(50.526));
         CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(53.199));
         CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(58.563));
         CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(55.928));
-        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(53.199));
-        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(58.563));
-        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(58.563));
-        CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(50.509));
     }
 
     SECTION("twitter.json")
@@ -85,40 +82,37 @@
         json j = json::parse(std::ifstream(filename));
 
         const auto json_size = j.dump().size();
+        const auto bjdata_1_size = json::to_bjdata(j).size();
+        const auto bjdata_2_size = json::to_bjdata(j, true).size();
+        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
         const auto bson_size = json::to_bson(j).size();
         const auto cbor_size = json::to_cbor(j).size();
         const auto msgpack_size = json::to_msgpack(j).size();
         const auto ubjson_1_size = json::to_ubjson(j).size();
         const auto ubjson_2_size = json::to_ubjson(j, true).size();
         const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
-        const auto bjdata_1_size = json::to_bjdata(j).size();
-        const auto bjdata_2_size = json::to_bjdata(j, true).size();
-        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
-        const auto bon8_size = json::to_bon8(j).size();
 
         CHECK(json_size == 466906);
+        CHECK(bjdata_1_size == 425342);
+        CHECK(bjdata_2_size == 429970);
+        CHECK(bjdata_3_size == 429970);
         CHECK(bson_size == 444568);
         CHECK(cbor_size == 402814);
         CHECK(msgpack_size == 401510);
         CHECK(ubjson_1_size == 426160);
         CHECK(ubjson_2_size == 430788);
         CHECK(ubjson_3_size == 430798);
-        CHECK(bjdata_1_size == 425342);
-        CHECK(bjdata_2_size == 429970);
-        CHECK(bjdata_3_size == 429970);
-        CHECK(bon8_size == 391172);
 
         CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
+        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(91.097));
+        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(92.089));
+        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(92.089));
         CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(95.215));
         CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(86.273));
         CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(85.993));
         CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(91.273));
         CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(92.264));
         CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(92.266));
-        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(91.097));
-        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(92.089));
-        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(92.089));
-        CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(83.779));
     }
 
     SECTION("citm_catalog.json")
@@ -127,40 +121,37 @@
         json j = json::parse(std::ifstream(filename));
 
         const auto json_size = j.dump().size();
+        const auto bjdata_1_size = json::to_bjdata(j).size();
+        const auto bjdata_2_size = json::to_bjdata(j, true).size();
+        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
         const auto bson_size = json::to_bson(j).size();
         const auto cbor_size = json::to_cbor(j).size();
         const auto msgpack_size = json::to_msgpack(j).size();
         const auto ubjson_1_size = json::to_ubjson(j).size();
         const auto ubjson_2_size = json::to_ubjson(j, true).size();
         const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
-        const auto bjdata_1_size = json::to_bjdata(j).size();
-        const auto bjdata_2_size = json::to_bjdata(j, true).size();
-        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
-        const auto bon8_size = json::to_bon8(j).size();
 
         CHECK(json_size == 500299);
+        CHECK(bjdata_1_size == 390781);
+        CHECK(bjdata_2_size == 433557);
+        CHECK(bjdata_3_size == 432964);
         CHECK(bson_size == 479430);
         CHECK(cbor_size == 342373);
         CHECK(msgpack_size == 342473);
         CHECK(ubjson_1_size == 391463);
         CHECK(ubjson_2_size == 434239);
         CHECK(ubjson_3_size == 425073);
-        CHECK(bjdata_1_size == 390781);
-        CHECK(bjdata_2_size == 433557);
-        CHECK(bjdata_3_size == 432964);
-        CHECK(bon8_size == 317877);
 
         CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
+        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(78.109));
+        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(86.659));
+        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(86.541));
         CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(95.828));
         CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(68.433));
         CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(68.453));
         CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(78.245));
         CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(86.795));
         CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(84.963));
-        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(78.109));
-        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(86.659));
-        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(86.541));
-        CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(63.537));
     }
 
     SECTION("jeopardy.json")
@@ -169,40 +160,37 @@
         json j = json::parse(std::ifstream(filename));
 
         const auto json_size = j.dump().size();
+        const auto bjdata_1_size = json::to_bjdata(j).size();
+        const auto bjdata_2_size = json::to_bjdata(j, true).size();
+        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
         const auto bson_size = json::to_bson({{"", j}}).size(); // wrap array in object for BSON
         const auto cbor_size = json::to_cbor(j).size();
         const auto msgpack_size = json::to_msgpack(j).size();
         const auto ubjson_1_size = json::to_ubjson(j).size();
         const auto ubjson_2_size = json::to_ubjson(j, true).size();
         const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
-        const auto bjdata_1_size = json::to_bjdata(j).size();
-        const auto bjdata_2_size = json::to_bjdata(j, true).size();
-        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
-        const auto bon8_size = json::to_bon8(j).size();
 
         CHECK(json_size == 52508728);
+        CHECK(bjdata_1_size == 50710965);
+        CHECK(bjdata_2_size == 51144830);
+        CHECK(bjdata_3_size == 51144830);
         CHECK(bson_size == 56008520);
         CHECK(cbor_size == 46187320);
         CHECK(msgpack_size == 46158575);
         CHECK(ubjson_1_size == 50710965);
         CHECK(ubjson_2_size == 51144830);
         CHECK(ubjson_3_size == 49861422);
-        CHECK(bjdata_1_size == 50710965);
-        CHECK(bjdata_2_size == 51144830);
-        CHECK(bjdata_3_size == 51144830);
-        CHECK(bon8_size == 45942080);
 
         CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
+        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(96.576));
+        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(97.402));
+        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(97.402));
         CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(106.665));
         CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.961));
         CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.906));
         CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(96.576));
         CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(97.402));
         CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(94.958));
-        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(96.576));
-        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(97.402));
-        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(97.402));
-        CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(87.494));
     }
 
     SECTION("sample.json")
@@ -211,37 +199,34 @@
         json j = json::parse(std::ifstream(filename));
 
         const auto json_size = j.dump().size();
+        const auto bjdata_1_size = json::to_bjdata(j).size();
+        const auto bjdata_2_size = json::to_bjdata(j, true).size();
+        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
         // BSON cannot process the file as it contains code point  U+0000
         const auto cbor_size = json::to_cbor(j).size();
         const auto msgpack_size = json::to_msgpack(j).size();
         const auto ubjson_1_size = json::to_ubjson(j).size();
         const auto ubjson_2_size = json::to_ubjson(j, true).size();
         const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
-        const auto bjdata_1_size = json::to_bjdata(j).size();
-        const auto bjdata_2_size = json::to_bjdata(j, true).size();
-        const auto bjdata_3_size = json::to_bjdata(j, true, true).size();
-        const auto bon8_size = json::to_bon8(j).size();
 
         CHECK(json_size == 168677);
+        CHECK(bjdata_1_size == 148695);
+        CHECK(bjdata_2_size == 150569);
+        CHECK(bjdata_3_size == 150569);
         CHECK(cbor_size == 147095);
         CHECK(msgpack_size == 147017);
         CHECK(ubjson_1_size == 148695);
         CHECK(ubjson_2_size == 150569);
         CHECK(ubjson_3_size == 150883);
-        CHECK(bjdata_1_size == 148695);
-        CHECK(bjdata_2_size == 150569);
-        CHECK(bjdata_3_size == 150569);
-        CHECK(bon8_size == 144463);
 
         CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
+        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(88.153));
+        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(89.264));
+        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(89.264));
         CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(87.205));
         CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(87.158));
         CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(88.153));
         CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(89.264));
         CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(89.450));
-        CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(88.153));
-        CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(89.264));
-        CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(89.264));
-        CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(85.644));
     }
 }
diff --git a/tests/src/unit-bjdata.cpp b/tests/src/unit-bjdata.cpp
index bc6c528..7442ea5 100644
--- a/tests/src/unit-bjdata.cpp
+++ b/tests/src/unit-bjdata.cpp
@@ -29,6 +29,8 @@
 
 #include "doctest_compatibility.h"
 
+#include <climits>
+#include <limits>
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
@@ -116,6 +118,112 @@
 };
 } // namespace
 
+// at some point in the future, a unit test dedicated to type traits might be a good idea
+template <typename OfType, typename T, bool MinInRange, bool MaxInRange>
+struct trait_test_arg
+{
+    using of_type = OfType;
+    using type = T;
+    static constexpr bool min_in_range = MinInRange;
+    static constexpr bool max_in_range = MaxInRange;
+};
+
+TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test)
+{
+    using nlohmann::detail::value_in_range_of;
+
+    using of_type = typename T::of_type;
+    using type = typename T::type;
+    constexpr bool min_in_range = T::min_in_range;
+    constexpr bool max_in_range = T::max_in_range;
+
+    type val_min = std::numeric_limits<type>::min();
+    type val_min2 = val_min + 1;
+    type val_max = std::numeric_limits<type>::max();
+    type val_max2 = val_max - 1;
+
+    REQUIRE(CHAR_BIT == 8);
+
+    std::string of_type_str;
+    if (std::is_unsigned<of_type>::value)
+    {
+        of_type_str += "u";
+    }
+    of_type_str += "int";
+    of_type_str += std::to_string(sizeof(of_type) * 8);
+
+    INFO("of_type := ", of_type_str);
+
+    std::string type_str;
+    if (std::is_unsigned<type>::value)
+    {
+        type_str += "u";
+    }
+    type_str += "int";
+    type_str += std::to_string(sizeof(type) * 8);
+
+    INFO("type := ", type_str);
+
+    CAPTURE(val_min);
+    CAPTURE(min_in_range);
+    CAPTURE(val_max);
+    CAPTURE(max_in_range);
+
+    if (min_in_range)
+    {
+        CHECK(value_in_range_of<of_type>(val_min));
+        CHECK(value_in_range_of<of_type>(val_min2));
+    }
+    else
+    {
+        CHECK_FALSE(value_in_range_of<of_type>(val_min));
+        CHECK_FALSE(value_in_range_of<of_type>(val_min2));
+    }
+
+    if (max_in_range)
+    {
+        CHECK(value_in_range_of<of_type>(val_max));
+        CHECK(value_in_range_of<of_type>(val_max2));
+    }
+    else
+    {
+        CHECK_FALSE(value_in_range_of<of_type>(val_max));
+        CHECK_FALSE(value_in_range_of<of_type>(val_max2));
+    }
+}
+
+TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
+                          trait_test_arg<std::int32_t, std::int32_t, true, true>, \
+                          trait_test_arg<std::int32_t, std::uint32_t, true, false>, \
+                          trait_test_arg<std::uint32_t, std::int32_t, false, true>, \
+                          trait_test_arg<std::uint32_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::int32_t, std::int64_t, false, false>, \
+                          trait_test_arg<std::int32_t, std::uint64_t, true, false>, \
+                          trait_test_arg<std::uint32_t, std::int64_t, false, false>, \
+                          trait_test_arg<std::uint32_t, std::uint64_t, true, false>, \
+                          trait_test_arg<std::int64_t, std::int32_t, true, true>, \
+                          trait_test_arg<std::int64_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::uint64_t, std::int32_t, false, true>, \
+                          trait_test_arg<std::uint64_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::int64_t, std::int64_t, true, true>, \
+                          trait_test_arg<std::int64_t, std::uint64_t, true, false>, \
+                          trait_test_arg<std::uint64_t, std::int64_t, false, true>, \
+                          trait_test_arg<std::uint64_t, std::uint64_t, true, true>);
+
+#if SIZE_MAX == 0xffffffff
+TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
+                          trait_test_arg<std::size_t, std::int32_t, false, true>, \
+                          trait_test_arg<std::size_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::size_t, std::int64_t, false, false>, \
+                          trait_test_arg<std::size_t, std::uint64_t, true, false>);
+#else
+TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
+                          trait_test_arg<std::size_t, std::int32_t, false, true>, \
+                          trait_test_arg<std::size_t, std::uint32_t, true, true>, \
+                          trait_test_arg<std::size_t, std::int64_t, false, true>, \
+                          trait_test_arg<std::size_t, std::uint64_t, true, true>);
+#endif
+
 TEST_CASE("BJData")
 {
     SECTION("individual values")
@@ -1041,8 +1149,7 @@
                     {
                         json _;
                         std::vector<uint8_t> vec0 = {'h'};
-                        CHECK_THROWS_AS(_ = json::from_bjdata(vec0), json::parse_error&);
-                        CHECK_THROWS_WITH(_ = json::from_bjdata(vec0), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData number: unexpected end of input");
+                        CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec0), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
                         CHECK(json::from_bjdata(vec0, true, false).is_discarded());
                     }
 
@@ -1050,8 +1157,7 @@
                     {
                         json _;
                         std::vector<uint8_t> vec1 = {'h', 0x00};
-                        CHECK_THROWS_AS(_ = json::from_bjdata(vec1), json::parse_error&);
-                        CHECK_THROWS_WITH(_ = json::from_bjdata(vec1), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData number: unexpected end of input");
+                        CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec1), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
                         CHECK(json::from_bjdata(vec1, true, false).is_discarded());
                     }
                 }
@@ -1130,7 +1236,7 @@
                 {
                     json j = json::from_bjdata(std::vector<uint8_t>({'h', 0x00, 0x7c}));
                     json::number_float_t d{j};
-                    CHECK(!std::isfinite(d));
+                    CHECK_FALSE(std::isfinite(d));
                     CHECK(j.dump() == "null");
                 }
 
@@ -2022,9 +2128,8 @@
             SECTION("strict mode")
             {
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(vec), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_bjdata(vec),
-                                  "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: expected end of input; last byte: 0x5A");
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec),
+                                     "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: expected end of input; last byte: 0x5A", json::parse_error&);
             }
         }
     }
@@ -2035,77 +2140,98 @@
         {
             std::vector<uint8_t> v = {'[', 'T', 'F', ']'};
             SaxCountdown scp(0);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("start_object()")
         {
             std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
             SaxCountdown scp(0);
-            CHECK(!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 object")
         {
             std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
             SaxCountdown scp(1);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("start_array(len)")
         {
             std::vector<uint8_t> v = {'[', '#', 'i', '2', 'T', 'F'};
             SaxCountdown scp(0);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("start_object(len)")
         {
             std::vector<uint8_t> v = {'{', '#', 'i', '1', 3, 'f', 'o', 'o', 'F'};
             SaxCountdown scp(0);
-            CHECK(!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 object with length")
         {
             std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
             SaxCountdown scp(1);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("start_array() in ndarray _ArraySize_")
         {
             std::vector<uint8_t> v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
             SaxCountdown scp(2);
-            CHECK(!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_")
         {
             std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
             SaxCountdown scp(3);
-            CHECK(!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_")
         {
             std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
+            SaxCountdown scp(6);
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
+        }
+
+        SECTION("string() in ndarray _ArrayType_")
+        {
+            std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
+            SaxCountdown scp(7);
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
+        }
+
+        SECTION("key() in ndarray _ArrayData_")
+        {
+            std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
             SaxCountdown scp(8);
-            CHECK(!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 _ArrayData_")
+        {
+            std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
+            SaxCountdown scp(9);
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("string() in ndarray _ArrayType_")
         {
             std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 3, 2, 6, 5, 4, 3, 2, 1};
             SaxCountdown scp(11);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
 
         SECTION("start_array() in ndarray _ArrayData_")
         {
             std::vector<uint8_t> v = {'[', '$', 'U', '#', '[', 'i', 2, 'i', 3, ']', 6, 5, 4, 3, 2, 1};
             SaxCountdown scp(13);
-            CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata));
+            CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
         }
     }
 
@@ -2342,6 +2468,7 @@
             {
                 // create vector with two elements of the same type
                 std::vector<uint8_t> v_0 = {'[', '$', 'i', '#', '[', ']'};
+                std::vector<uint8_t> v_E = {'[', '$', 'i', '#', '[', 'i', 2, 'i', 0, ']'};
                 std::vector<uint8_t> v_i = {'[', '$', 'i', '#', '[', 'i', 1, 'i', 2, ']', 0x7F, 0x7F};
                 std::vector<uint8_t> v_U = {'[', '$', 'U', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF};
                 std::vector<uint8_t> v_I = {'[', '$', 'I', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0x7F, 0xFF, 0x7F};
@@ -2353,9 +2480,11 @@
                 std::vector<uint8_t> v_D = {'[', '$', 'D', '#', '[', 'i', 1, 'i', 2, ']', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40};
                 std::vector<uint8_t> v_S = {'[', '#', '[', 'i', 1, 'i', 2, ']', 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'};
                 std::vector<uint8_t> v_C = {'[', '$', 'C', '#', '[', 'i', 1, 'i', 2, ']', 'a', 'a'};
+                std::vector<uint8_t> v_R = {'[', '#', '[', 'i', 2, ']', 'i', 6, 'U', 7};
 
                 // check if vector is parsed correctly
                 CHECK(json::from_bjdata(v_0) == json::array());
+                CHECK(json::from_bjdata(v_E) == json::array());
                 CHECK(json::from_bjdata(v_i) == json({127, 127}));
                 CHECK(json::from_bjdata(v_U) == json({255, 255}));
                 CHECK(json::from_bjdata(v_I) == json({32767, 32767}));
@@ -2367,6 +2496,7 @@
                 CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926}));
                 CHECK(json::from_bjdata(v_S) == json({"a", "a"}));
                 CHECK(json::from_bjdata(v_C) == json({"a", "a"}));
+                CHECK(json::from_bjdata(v_R) == json({6, 7}));
             }
 
             SECTION("optimized ndarray (type and vector-size as size-optimized array)")
@@ -2408,34 +2538,6 @@
                 CHECK(json::from_bjdata(json::to_bjdata(j_type), true, true) == j_type);
                 CHECK(json::from_bjdata(json::to_bjdata(j_size), true, true) == j_size);
             }
-
-            SECTION("do not accept NTFZ markers in ndarray optimized type")
-            {
-                json _;
-                std::vector<uint8_t> v_N = {'[', '$', 'N', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
-                std::vector<uint8_t> v_T = {'[', '$', 'T', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
-                std::vector<uint8_t> v_F = {'[', '$', 'F', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
-                std::vector<uint8_t> v_Z = {'[', '$', 'Z', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
-
-                CHECK(json::from_bjdata(v_N, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_T, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_F, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_Z, true, true).is_discarded());
-            }
-
-            SECTION("do not accept NTFZ markers in ndarray optimized type")
-            {
-                json _;
-                std::vector<uint8_t> v_N = {'[', '$', 'N', '#', '[', 'i', 1, 'i', 2, ']'};
-                std::vector<uint8_t> v_T = {'[', '$', 'T', '#', '[', 'i', 1, 'i', 2, ']'};
-                std::vector<uint8_t> v_F = {'[', '$', 'F', '#', '[', 'i', 1, 'i', 2, ']'};
-                std::vector<uint8_t> v_Z = {'[', '$', 'Z', '#', '[', 'i', 1, 'i', 2, ']'};
-
-                CHECK(json::from_bjdata(v_N, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_T, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_F, true, true).is_discarded());
-                CHECK(json::from_bjdata(v_Z, true, true).is_discarded());
-            }
         }
     }
 
@@ -2444,9 +2546,8 @@
         SECTION("empty byte vector")
         {
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>()), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(std::vector<uint8_t>()),
-                              "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(std::vector<uint8_t>()),
+                                 "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
         }
 
         SECTION("char")
@@ -2455,15 +2556,13 @@
             {
                 std::vector<uint8_t> v = {'C'};
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input");
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input", json::parse_error&);
             }
 
             SECTION("byte out of range")
             {
                 std::vector<uint8_t> v = {'C', 130};
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
                 CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82");
             }
         }
@@ -2474,16 +2573,14 @@
             {
                 std::vector<uint8_t> v = {'S'};
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input");
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             }
 
             SECTION("invalid byte")
             {
                 std::vector<uint8_t> v = {'S', '1', 'a'};
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31");
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
             }
 
             SECTION("parse bjdata markers in ubjson")
@@ -2495,14 +2592,9 @@
 
                 json _;
                 // check if string is parsed correctly to "a"
-                CHECK_THROWS_AS(_ = json::from_ubjson(s_u), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_ubjson(s_u), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x75");
-
-                CHECK_THROWS_AS(_ = json::from_ubjson(s_m), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_ubjson(s_m), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x6D");
-
-                CHECK_THROWS_AS(_ = json::from_ubjson(s_M), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_ubjson(s_M), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x4D");
+                CHECK_THROWS_WITH_AS(_ = json::from_ubjson(s_u), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x75", json::parse_error&);
+                CHECK_THROWS_WITH_AS(_ = json::from_ubjson(s_m), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x6D", json::parse_error&);
+                CHECK_THROWS_WITH_AS(_ = json::from_ubjson(s_M), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x4D", json::parse_error&);
             }
         }
 
@@ -2512,8 +2604,124 @@
             {
                 std::vector<uint8_t> v = {'[', '$', 'i', 2};
                 json _;
-                CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-                CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x02");
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x02", json::parse_error&);
+            }
+
+            SECTION("optimized array: negative size")
+            {
+                std::vector<uint8_t> v1 = {'[', '#', 'i', 0xF1};
+                std::vector<uint8_t> v2 = {'[', '$', 'I', '#', 'i', 0xF2};
+                std::vector<uint8_t> v3 = {'[', '$', 'I', '#', '[', 'i', 0xF4, 'i', 0x02, ']'};
+                std::vector<uint8_t> v4 = {'[', '$', 0xF6, '#', 'i', 0xF7};
+                std::vector<uint8_t> v5 = {'[', '$', 'I', '#', '[', 'i', 0xF5, 'i', 0xF1, ']'};
+                std::vector<uint8_t> v6 = {'[', '#', '[', 'i', 0xF3, 'i', 0x02, ']'};
+
+                std::vector<uint8_t> vI = {'[', '#', 'I', 0x00, 0xF1};
+                std::vector<uint8_t> vl = {'[', '#', 'l', 0x00, 0x00, 0x00, 0xF2};
+                std::vector<uint8_t> vL = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3};
+                std::vector<uint8_t> vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
+                std::vector<uint8_t> vMX = {'[', '$', 'U', '#', '[', 'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'U', 0x01, ']'};
+
+                json _;
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v1), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v1, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v2), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v2, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v3), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v3, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v4), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v4, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v5), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v5, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v6), "[json.exception.parse_error.113] parse error at byte 5: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(v6, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vI), "[json.exception.parse_error.113] parse error at byte 5: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(vI, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vl), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(vl, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.parse_error.113] parse error at byte 11: syntax error while parsing BJData size: count in an optimized container must be positive", json::parse_error&);
+                CHECK(json::from_bjdata(vL, true, false).is_discarded());
+
+#if SIZE_MAX != 0xffffffff
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: excessive ndarray size caused overflow", json::out_of_range&);
+#else
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+#endif
+                CHECK(json::from_bjdata(vM, true, false).is_discarded());
+
+#if SIZE_MAX != 0xffffffff
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: excessive ndarray size caused overflow", json::out_of_range&);
+#else
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+#endif
+                CHECK(json::from_bjdata(vMX, true, false).is_discarded());
+            }
+
+            SECTION("optimized array: integer value overflow")
+            {
+                std::vector<uint8_t> vL = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F};
+                std::vector<uint8_t> vM = {'[', '$', 'M', '#', '[', 'I', 0x00, 0x20, 'M', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, ']'};
+
+                json _;
+#if SIZE_MAX == 0xffffffff
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vL, true, false).is_discarded());
+#endif
+
+#if SIZE_MAX == 0xffffffff
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
+                CHECK(json::from_bjdata(vM, true, false).is_discarded());
+#endif
+            }
+
+            SECTION("do not accept NTFZ markers in ndarray optimized type (with count)")
+            {
+                json _;
+                std::vector<uint8_t> v_N = {'[', '$', 'N', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
+                std::vector<uint8_t> v_T = {'[', '$', 'T', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
+                std::vector<uint8_t> v_F = {'[', '$', 'F', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
+                std::vector<uint8_t> v_Z = {'[', '$', 'Z', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2};
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_N), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x4E is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_N, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_T), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x54 is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_T, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_F), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x46 is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_F, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_Z), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x5A is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_Z, true, false).is_discarded());
+            }
+
+            SECTION("do not accept NTFZ markers in ndarray optimized type (without count)")
+            {
+                json _;
+                std::vector<uint8_t> v_N = {'[', '$', 'N', '#', '[', 'i', 1, 'i', 2, ']'};
+                std::vector<uint8_t> v_T = {'[', '$', 'T', '#', '[', 'i', 1, 'i', 2, ']'};
+                std::vector<uint8_t> v_F = {'[', '$', 'F', '#', '[', 'i', 1, 'i', 2, ']'};
+                std::vector<uint8_t> v_Z = {'[', '$', 'Z', '#', '[', 'i', 1, 'i', 2, ']'};
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_N), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x4E is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_N, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_T), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x54 is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_T, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_F), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x46 is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_F, true, false).is_discarded());
+
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v_Z), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x5A is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v_Z, true, false).is_discarded());
             }
         }
 
@@ -2521,18 +2729,15 @@
         {
             std::vector<uint8_t> vS = {'S'};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vS, true, false).is_discarded());
 
             std::vector<uint8_t> v = {'S', 'i', '2', 'a'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData string: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData string: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v, true, false).is_discarded());
 
             std::vector<uint8_t> vC = {'C'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vC), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vC), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vC), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vC, true, false).is_discarded());
         }
 
@@ -2540,47 +2745,38 @@
         {
             std::vector<uint8_t> vU = {'[', '#', 'U'};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vU, true, false).is_discarded());
 
             std::vector<uint8_t> vi = {'[', '#', 'i'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vi), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vi, true, false).is_discarded());
 
             std::vector<uint8_t> vI = {'[', '#', 'I'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vI), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vI), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vI), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vI, true, false).is_discarded());
 
             std::vector<uint8_t> vu = {'[', '#', 'u'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vu), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vu, true, false).is_discarded());
 
             std::vector<uint8_t> vl = {'[', '#', 'l'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vl), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vl), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vl), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vl, true, false).is_discarded());
 
             std::vector<uint8_t> vm = {'[', '#', 'm'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vm), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vm, true, false).is_discarded());
 
             std::vector<uint8_t> vL = {'[', '#', 'L'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vL), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vL), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vL, true, false).is_discarded());
 
             std::vector<uint8_t> vM = {'[', '#', 'M'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vM), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vM, true, false).is_discarded());
 
             std::vector<uint8_t> v0 = {'[', '#', 'T', ']'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v0), json::parse_error&);
             CHECK_THROWS_WITH(_ = json::from_bjdata(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x54");
             CHECK(json::from_bjdata(v0, true, false).is_discarded());
         }
@@ -2589,23 +2785,19 @@
         {
             json _;
             std::vector<uint8_t> vu = {'[', '#', 'u'};
-            CHECK_THROWS_AS(_ = json::from_ubjson(vu), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_ubjson(vu), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x75");
+            CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vu), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x75", json::parse_error&);
             CHECK(json::from_ubjson(vu, true, false).is_discarded());
 
             std::vector<uint8_t> vm = {'[', '#', 'm'};
-            CHECK_THROWS_AS(_ = json::from_ubjson(vm), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_ubjson(vm), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x6D");
+            CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vm), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x6D", json::parse_error&);
             CHECK(json::from_ubjson(vm, true, false).is_discarded());
 
             std::vector<uint8_t> vM = {'[', '#', 'M'};
-            CHECK_THROWS_AS(_ = json::from_ubjson(vM), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_ubjson(vM), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x4D");
+            CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vM), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x4D", json::parse_error&);
             CHECK(json::from_ubjson(vM, true, false).is_discarded());
 
             std::vector<uint8_t> v0 = {'[', '#', '['};
-            CHECK_THROWS_AS(_ = json::from_ubjson(v0), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x5B");
+            CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x5B", json::parse_error&);
             CHECK(json::from_ubjson(v0, true, false).is_discarded());
         }
 
@@ -2613,37 +2805,35 @@
         {
             std::vector<uint8_t> v0 = {'[', '$'};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(v0), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v0), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData type: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v0), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData type: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v0, true, false).is_discarded());
 
             std::vector<uint8_t> vi = {'[', '$', '#'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vi), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vi, true, false).is_discarded());
 
             std::vector<uint8_t> vU = {'[', '$', 'U'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vU, true, false).is_discarded());
+
+            std::vector<uint8_t> v1 = {'[', '$', '['};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v1), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x5B is not a permitted optimized array type", json::parse_error&);
+            CHECK(json::from_bjdata(v1, true, false).is_discarded());
         }
 
         SECTION("arrays")
         {
             std::vector<uint8_t> vST = {'[', '$', 'i', '#', 'i', 2, 1};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vST, true, false).is_discarded());
 
             std::vector<uint8_t> vS = {'[', '#', 'i', 2, 'i', 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vS, true, false).is_discarded());
 
             std::vector<uint8_t> v = {'[', 'i', 2, 'i', 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v, true, false).is_discarded());
         }
 
@@ -2651,53 +2841,43 @@
         {
             std::vector<uint8_t> vST = {'[', '$', 'i', '#', '[', '$', 'i', '#'};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0xFF");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vST), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0xFF", json::parse_error&);
             CHECK(json::from_bjdata(vST, true, false).is_discarded());
 
             std::vector<uint8_t> v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 1, 2};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 13: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 13: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v, true, false).is_discarded());
 
             std::vector<uint8_t> vS0 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vS0), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vS0), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vS0), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vS0, true, false).is_discarded());
 
             std::vector<uint8_t> vS = {'[', '$', 'i', '#', '[', '#', 'i', 2, 1, 2, 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x01");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vS), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x01", json::parse_error&);
             CHECK(json::from_bjdata(vS, true, false).is_discarded());
 
             std::vector<uint8_t> vT = {'[', '$', 'i', '#', '[', 'i', 2, 'i'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vT), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vT), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vT), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vT, true, false).is_discarded());
 
             std::vector<uint8_t> vT0 = {'[', '$', 'i', '#', '[', 'i'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vT0), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vT0), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vT0), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vT0, true, false).is_discarded());
 
             std::vector<uint8_t> vu = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'u', 1, 0};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vu), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vu, true, false).is_discarded());
 
             std::vector<uint8_t> vm = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'm', 1, 0, 0, 0};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vm), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vm, true, false).is_discarded());
 
             std::vector<uint8_t> vM = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'M', 1, 0, 0, 0, 0, 0, 0, 0};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vM), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vM, true, false).is_discarded());
 
             std::vector<uint8_t> vU = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 1, 2, 3, 4, 5};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vU, true, false).is_discarded());
 
             std::vector<uint8_t> vT1 = {'[', '$', 'T', '#', '[', '$', 'i', '#', 'i', 2, 2, 3};
@@ -2705,53 +2885,86 @@
 
             std::vector<uint8_t> vh = {'[', '$', 'h', '#', '[', '$', 'i', '#', 'i', 2, 2, 3};
             CHECK(json::from_bjdata(vh, true, false).is_discarded());
+
+            std::vector<uint8_t> vR = {'[', '$', 'i', '#', '[', 'i', 1, '[', ']', ']', 1};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR), "[json.exception.parse_error.113] parse error at byte 8: syntax error while parsing BJData size: ndarray dimentional vector is not allowed", json::parse_error&);
+            CHECK(json::from_bjdata(vR, true, false).is_discarded());
+
+            std::vector<uint8_t> vRo = {'[', '$', 'i', '#', '[', 'i', 0, '{', '}', ']', 1};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vRo), "[json.exception.parse_error.113] parse error at byte 8: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x7B", json::parse_error&);
+            CHECK(json::from_bjdata(vRo, true, false).is_discarded());
+
+            std::vector<uint8_t> vR1 = {'[', '$', 'i', '#', '[', '[', 'i', 1, ']', ']', 1};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR1), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: ndarray dimentional vector is not allowed", json::parse_error&);
+            CHECK(json::from_bjdata(vR1, true, false).is_discarded());
+
+            std::vector<uint8_t> vR2 = {'[', '$', 'i', '#', '[', '#', '[', 'i', 1, ']', ']', 1};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR2), "[json.exception.parse_error.113] parse error at byte 11: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x5D", json::parse_error&);
+            CHECK(json::from_bjdata(vR2, true, false).is_discarded());
+
+            std::vector<uint8_t> vR3 = {'[', '#', '[', 'i', '2', 'i', 2, ']'};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR3), "[json.exception.parse_error.112] parse error at byte 8: syntax error while parsing BJData size: ndarray requires both type and size", json::parse_error&);
+            CHECK(json::from_bjdata(vR3, true, false).is_discarded());
+
+            std::vector<uint8_t> vR4 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', 1, ']', 1};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR4), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
+            CHECK(json::from_bjdata(vR4, true, false).is_discarded());
+
+            std::vector<uint8_t> vR5 = {'[', '$', 'i', '#', '[', '[', '[', ']', ']', ']'};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR5), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: ndarray dimentional vector is not allowed", json::parse_error&);
+            CHECK(json::from_bjdata(vR5, true, false).is_discarded());
+
+            std::vector<uint8_t> vR6 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR6), "[json.exception.parse_error.112] parse error at byte 14: syntax error while parsing BJData size: ndarray can not be recursive", json::parse_error&);
+            CHECK(json::from_bjdata(vR6, true, false).is_discarded());
+
+            std::vector<uint8_t> vH = {'[', 'H', '[', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vH), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: ndarray dimentional vector is not allowed", json::parse_error&);
+            CHECK(json::from_bjdata(vH, true, false).is_discarded());
         }
 
         SECTION("objects")
         {
             std::vector<uint8_t> vST = {'{', '$', 'i', '#', 'i', 2, 'i', 1, 'a', 1};
             json _;
-            CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 11: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 11: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vST, true, false).is_discarded());
 
             std::vector<uint8_t> vT = {'{', '$', 'i', 'i', 1, 'a', 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vT), json::parse_error&);
             CHECK_THROWS_WITH(_ = json::from_bjdata(vT), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x69");
             CHECK(json::from_bjdata(vT, true, false).is_discarded());
 
             std::vector<uint8_t> vS = {'{', '#', 'i', 2, 'i', 1, 'a', 'i', 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vS, true, false).is_discarded());
 
             std::vector<uint8_t> v = {'{', 'i', 1, 'a', 'i', 1};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v, true, false).is_discarded());
 
             std::vector<uint8_t> v2 = {'{', 'i', 1, 'a', 'i', 1, 'i'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v2), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v2, true, false).is_discarded());
 
             std::vector<uint8_t> v3 = {'{', 'i', 1, 'a'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(v3), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(v3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(v3, true, false).is_discarded());
 
             std::vector<uint8_t> vST1 = {'{', '$', 'd', '#', 'i', 2, 'i', 1, 'a'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vST1), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vST1), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData number: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vST1), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vST1, true, false).is_discarded());
 
             std::vector<uint8_t> vST2 = {'{', '#', 'i', 2, 'i', 1, 'a'};
-            CHECK_THROWS_AS(_ = json::from_bjdata(vST2), json::parse_error&);
-            CHECK_THROWS_WITH(_ = json::from_bjdata(vST2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData value: unexpected end of input");
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vST2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
             CHECK(json::from_bjdata(vST2, true, false).is_discarded());
 
             std::vector<uint8_t> vO = {'{', '#', '[', 'i', 2, 'i', 1, ']', 'i', 1, 'a', 'i', 1, 'i', 1, 'b', 'i', 2};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vO), "[json.exception.parse_error.112] parse error at byte 8: syntax error while parsing BJData size: ndarray requires both type and size", json::parse_error&);
             CHECK(json::from_bjdata(vO, true, false).is_discarded());
+
+            std::vector<uint8_t> vO2 = {'{', '$', 'i', '#', '[', 'i', 2, 'i', 1, ']', 'i', 1, 'a', 1, 'i', 1, 'b', 2};
+            CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vO2), "[json.exception.parse_error.112] parse error at byte 10: syntax error while parsing BJData object: BJData object does not support ND-array size in optimized format", json::parse_error&);
+            CHECK(json::from_bjdata(vO2, true, false).is_discarded());
         }
     }
 
@@ -3188,14 +3401,18 @@
         {
             SECTION("Array")
             {
+                json _;
                 std::vector<uint8_t> v = {'[', '$', 'N', '#', 'I', 0x00, 0x02};
-                CHECK(json::from_bjdata(v, true, true).is_discarded());
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x4E is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v, true, false).is_discarded());
             }
 
             SECTION("Object")
             {
+                json _;
                 std::vector<uint8_t> v = {'{', '$', 'Z', '#', 'i', 3, 'i', 4, 'n', 'a', 'm', 'e', 'i', 8, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', 'i', 5, 'e', 'm', 'a', 'i', 'l'};
-                CHECK(json::from_bjdata(v, true, true).is_discarded());
+                CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x5A is not a permitted optimized array type", json::parse_error&);
+                CHECK(json::from_bjdata(v, true, false).is_discarded());
             }
         }
     }
@@ -3320,21 +3537,6 @@
             }
 
             {
-                INFO_WITH_TEMP(filename + ": uint8_t* and size");
-                // parse JSON file
-                std::ifstream f_json(filename);
-                json j1 = json::parse(f_json);
-
-                // parse BJData file
-                auto packed = utils::read_binary_file(filename + ".bjdata");
-                json j2;
-                CHECK_NOTHROW(j2 = json::from_bjdata({packed.data(), packed.size()}));
-
-                // compare parsed JSON values
-                CHECK(j1 == j2);
-            }
-
-            {
                 INFO_WITH_TEMP(filename + ": output to output adapters");
                 // parse JSON file
                 std::ifstream f_json(filename);
diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp
index 95a958d..3d26773 100644
--- a/tests/src/unit-class_parser.cpp
+++ b/tests/src/unit-class_parser.cpp
@@ -1454,17 +1454,17 @@
 
         SECTION("filter specific element")
         {
-            json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
+            json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
             {
                 // filter all number(2) elements
-                return j != json(2);
+                return event != json::parse_event_t::value || j != json(2);
             });
 
             CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
 
-            json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
+            json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
             {
-                return j != json(2);
+                return event != json::parse_event_t::value || j != json(2);
             });
 
             CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));
diff --git a/tests/src/unit-comparison.cpp b/tests/src/unit-comparison.cpp
index 6c94add..f310a70 100644
--- a/tests/src/unit-comparison.cpp
+++ b/tests/src/unit-comparison.cpp
@@ -27,11 +27,49 @@
 SOFTWARE.
 */
 
+// cmake/test.cmake selects the C++ standard versions with which to build a
+// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
+// When using macros that are only defined for particular versions of the standard
+// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
+// version macro in a comment close by, like this:
+// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
+
 #include "doctest_compatibility.h"
 
+#define JSON_TESTS_PRIVATE
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
+#if JSON_HAS_THREE_WAY_COMPARISON
+// this can be replaced with the doctest stl extension header in version 2.5
+namespace doctest
+{
+template<> struct StringMaker<std::partial_ordering>
+{
+    static String convert(const std::partial_ordering& order)
+    {
+        if (order == std::partial_ordering::less)
+        {
+            return "std::partial_ordering::less";
+        }
+        if (order == std::partial_ordering::equivalent)
+        {
+            return "std::partial_ordering::equivalent";
+        }
+        if (order == std::partial_ordering::greater)
+        {
+            return "std::partial_ordering::greater";
+        }
+        if (order == std::partial_ordering::unordered)
+        {
+            return "std::partial_ordering::unordered";
+        }
+        return "{?}";
+    }
+};
+} // namespace doctest
+#endif
+
 namespace
 {
 // helper function to check std::less<json::value_t>
@@ -45,6 +83,27 @@
 
 TEST_CASE("lexicographical comparison operators")
 {
+    constexpr auto f_ = false;
+    constexpr auto _t = true;
+    constexpr auto nan = std::numeric_limits<json::number_float_t>::quiet_NaN();
+#if JSON_HAS_THREE_WAY_COMPARISON
+    constexpr auto lt = std::partial_ordering::less;
+    constexpr auto gt = std::partial_ordering::greater;
+    constexpr auto eq = std::partial_ordering::equivalent;
+    constexpr auto un = std::partial_ordering::unordered;
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+    INFO("using 3-way comparison");
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    INFO("using legacy comparison");
+#endif
+
+    //REQUIRE(std::numeric_limits<json::number_float_t>::has_quiet_NaN);
+    REQUIRE(std::isnan(nan));
+
     SECTION("types")
     {
         std::vector<json::value_t> j_types =
@@ -57,97 +116,268 @@
             json::value_t::object,
             json::value_t::array,
             json::value_t::string,
-            json::value_t::binary
+            json::value_t::binary,
+            json::value_t::discarded
+        };
+
+        std::vector<std::vector<bool>> expected_lt =
+        {
+            //0   1   2   3   4   5   6   7   8   9
+            {f_, _t, _t, _t, _t, _t, _t, _t, _t, f_}, //  0
+            {f_, f_, _t, _t, _t, _t, _t, _t, _t, f_}, //  1
+            {f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, //  2
+            {f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, //  3
+            {f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, //  4
+            {f_, f_, f_, f_, f_, f_, _t, _t, _t, f_}, //  5
+            {f_, f_, f_, f_, f_, f_, f_, _t, _t, f_}, //  6
+            {f_, f_, f_, f_, f_, f_, f_, f_, _t, f_}, //  7
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  8
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  9
         };
 
         SECTION("comparison: less")
         {
-            std::vector<std::vector<bool>> expected =
-            {
-                {false, true, true, true, true, true, true, true, true},
-                {false, false, true, true, true, true, true, true, true},
-                {false, false, false, false, false, true, true, true, true},
-                {false, false, false, false, false, true, true, true, true},
-                {false, false, false, false, false, true, true, true, true},
-                {false, false, false, false, false, false, true, true, true},
-                {false, false, false, false, false, false, false, true, true},
-                {false, false, false, false, false, false, false, false, true},
-                {false, false, false, false, false, false, false, false, false}
-            };
-
+            REQUIRE(expected_lt.size() == j_types.size());
             for (size_t i = 0; i < j_types.size(); ++i)
             {
+                REQUIRE(expected_lt[i].size() == j_types.size());
                 for (size_t j = 0; j < j_types.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
                     // check precomputed values
-                    CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]);
-                    CHECK(f(j_types[i], j_types[j]) == expected[i][j]);
+#if JSON_HAS_THREE_WAY_COMPARISON
+                    // JSON_HAS_CPP_20 (do not remove; see note at top of file)
+                    CHECK((j_types[i] < j_types[j]) == expected_lt[i][j]);
+#else
+                    CHECK(operator<(j_types[i], j_types[j]) == expected_lt[i][j]);
+#endif
+                    CHECK(f(j_types[i], j_types[j]) == expected_lt[i][j]);
                 }
             }
         }
+#if JSON_HAS_THREE_WAY_COMPARISON
+        // JSON_HAS_CPP_20 (do not remove; see note at top of file)
+        SECTION("comparison: 3-way")
+        {
+            std::vector<std::vector<std::partial_ordering>> expected =
+            {
+                //0   1   2   3   4   5   6   7   8   9
+                {eq, lt, lt, lt, lt, lt, lt, lt, lt, un}, //  0
+                {gt, eq, lt, lt, lt, lt, lt, lt, lt, un}, //  1
+                {gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, //  2
+                {gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, //  3
+                {gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, //  4
+                {gt, gt, gt, gt, gt, eq, lt, lt, lt, un}, //  5
+                {gt, gt, gt, gt, gt, gt, eq, lt, lt, un}, //  6
+                {gt, gt, gt, gt, gt, gt, gt, eq, lt, un}, //  7
+                {gt, gt, gt, gt, gt, gt, gt, gt, eq, un}, //  8
+                {un, un, un, un, un, un, un, un, un, un}, //  9
+            };
+
+            // check expected partial_ordering against expected boolean
+            REQUIRE(expected.size() == expected_lt.size());
+            for (size_t i = 0; i < expected.size(); ++i)
+            {
+                REQUIRE(expected[i].size() == expected_lt[i].size());
+                for (size_t j = 0; j < expected[i].size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
+                }
+            }
+
+            // check 3-way comparison against expected partial_ordering
+            REQUIRE(expected.size() == j_types.size());
+            for (size_t i = 0; i < j_types.size(); ++i)
+            {
+                REQUIRE(expected[i].size() == j_types.size());
+                for (size_t j = 0; j < j_types.size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK((j_types[i] <=> j_types[j]) == expected[i][j]); // *NOPAD*
+                }
+            }
+        }
+#endif
     }
 
     SECTION("values")
     {
         json j_values =
         {
-            nullptr, nullptr,
-            -17, 42,
-            8u, 13u,
-            3.14159, 23.42,
-            "foo", "bar",
-            true, false,
-            {1, 2, 3}, {"one", "two", "three"},
-            {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}},
-            json::binary({1, 2, 3}), json::binary({1, 2, 4})
+            nullptr, nullptr,                                              // 0 1
+            -17, 42,                                                       // 2 3
+            8u, 13u,                                                       // 4 5
+            3.14159, 23.42,                                                // 6 7
+            nan, nan,                                                      // 8 9
+            "foo", "bar",                                                  // 10 11
+            true, false,                                                   // 12 13
+            {1, 2, 3}, {"one", "two", "three"},                            // 14 15
+            {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}},     // 16 17
+            json::binary({1, 2, 3}), json::binary({1, 2, 4}),              // 18 19
+            json(json::value_t::discarded), json(json::value_t::discarded) // 20 21
         };
 
-        SECTION("comparison: equal")
+        std::vector<std::vector<bool>> expected_eq =
+        {
+            //0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
+            {_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  0
+            {_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  1
+            {f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  2
+            {f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  3
+            {f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  4
+            {f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  5
+            {f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  6
+            {f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  7
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  8
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  9
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_}, // 14
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_}, // 15
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_}, // 16
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_}, // 17
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_}, // 18
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 19
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
+        };
+
+        std::vector<std::vector<bool>> expected_lt =
+        {
+            //0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
+            {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, //  0
+            {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, //  1
+            {f_, f_, f_, _t, _t, _t, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  2
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  3
+            {f_, f_, f_, _t, f_, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  4
+            {f_, f_, f_, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  5
+            {f_, f_, f_, _t, _t, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  6
+            {f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  7
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  8
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, //  9
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 10
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 11
+            {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 12
+            {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 13
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_}, // 14
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 15
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_}, // 16
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, f_, _t, _t, f_, f_}, // 17
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 18
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
+            {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
+        };
+
+        SECTION("compares unordered")
         {
             std::vector<std::vector<bool>> expected =
             {
-                {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
+                //0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  0
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  1
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  2
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  3
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  4
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  5
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  6
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  7
+                {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  8
+                {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, //  9
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 10
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 11
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 12
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 13
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 14
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 15
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 16
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 17
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 18
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 19
+                {_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 20
+                {_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 21
             };
 
+            // check if two values compare unordered as expected
+            REQUIRE(expected.size() == j_values.size());
             for (size_t i = 0; i < j_values.size(); ++i)
             {
+                REQUIRE(expected[i].size() == j_values.size());
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK(json::compares_unordered(j_values[i], j_values[j]) == expected[i][j]);
+                }
+            }
+        }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+        SECTION("compares unordered (inverse)")
+        {
+            std::vector<std::vector<bool>> expected =
+            {
+                //0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  0
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  1
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  2
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  3
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  4
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  5
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  6
+                {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  7
+                {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  8
+                {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, //  9
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 14
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 15
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 16
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 17
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 18
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
+                {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
+            };
+
+            // check that two values compare unordered as expected (with legacy-mode enabled)
+            REQUIRE(expected.size() == j_values.size());
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                REQUIRE(expected[i].size() == j_values.size());
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
                     CAPTURE(j_values[i])
                     CAPTURE(j_values[j])
-                    // check precomputed values
-                    CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
+                    CHECK(json::compares_unordered(j_values[i], j_values[j], true) == expected[i][j]);
                 }
             }
+        }
+#endif
 
-            // comparison with discarded elements
-            json j_discarded(json::value_t::discarded);
-            for (const auto& v : j_values)
+        SECTION("comparison: equal")
+        {
+            // check that two values compare equal
+            REQUIRE(expected_eq.size() == j_values.size());
+            for (size_t i = 0; i < j_values.size(); ++i)
             {
-                CHECK( (v == j_discarded) == false);
-                CHECK( (j_discarded == v) == false);
-                CHECK( (j_discarded == j_discarded) == false);
+                REQUIRE(expected_eq[i].size() == j_values.size());
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
+                }
             }
 
             // compare with null pointer
@@ -158,121 +388,229 @@
 
         SECTION("comparison: not equal")
         {
+            // check that two values compare unequal as expected
             for (size_t i = 0; i < j_values.size(); ++i)
             {
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
-                    // check definition
-                    CHECK( (j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]) );
+
+                    if (json::compares_unordered(j_values[i], j_values[j], true))
+                    {
+                        // if two values compare unordered,
+                        // check that the boolean comparison result is always false
+                        CHECK_FALSE(j_values[i] != j_values[j]);
+                    }
+                    else
+                    {
+                        // otherwise, check that they compare according to their definition
+                        // as the inverse of equal
+                        CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
+                    }
                 }
             }
 
             // compare with null pointer
             json j_null;
-            CHECK( (j_null != nullptr) == false);
-            CHECK( (nullptr != j_null) == false);
-            CHECK( (j_null != nullptr) == !(j_null == nullptr));
-            CHECK( (nullptr != j_null) == !(nullptr == j_null));
+            CHECK((j_null != nullptr) == false);
+            CHECK((nullptr != j_null) == false);
+            CHECK((j_null != nullptr) == !(j_null == nullptr));
+            CHECK((nullptr != j_null) == !(nullptr == j_null));
         }
 
         SECTION("comparison: less")
         {
-            std::vector<std::vector<bool>> expected =
-            {
-                {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
-                {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
-                {false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true},
-                {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, true},
-                {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
-                {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true},
-                {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false, true, true},
-                {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, true, true},
-                {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, true, true},
-                {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false, true, true},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true},
-                {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
-            };
-
+            // check that two values compare less than as expected
+            REQUIRE(expected_lt.size() == j_values.size());
             for (size_t i = 0; i < j_values.size(); ++i)
             {
+                REQUIRE(expected_lt[i].size() == j_values.size());
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
-                    // Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix
-                    // See issue #3207
-#if defined(JSON_HAS_CPP_20) || JSON_HAS_THREE_WAY_COMPARISON
-                    if ((i == 12 && j == 13) || (i == 13 && j == 12))
-                    {
-                        continue;
-                    }
-#endif
                     CAPTURE(i)
                     CAPTURE(j)
-                    CAPTURE(j_values[i])
-                    CAPTURE(j_values[j])
-                    // check precomputed values
-                    CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
+                    CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
                 }
             }
-
-            // comparison with discarded elements
-            json j_discarded(json::value_t::discarded);
-            for (size_t i = 0; i < j_values.size(); ++i)
-            {
-                CAPTURE(i)
-                CHECK( (j_values[i] < j_discarded) == false);
-                CHECK( (j_discarded < j_values[i]) == false);
-                CHECK( (j_discarded < j_discarded) == false);
-            }
         }
 
         SECTION("comparison: less than or equal equal")
         {
+            // check that two values compare less than or equal as expected
             for (size_t i = 0; i < j_values.size(); ++i)
             {
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
-                    // check definition
-                    CHECK( (j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]) );
+                    if (json::compares_unordered(j_values[i], j_values[j], true))
+                    {
+                        // if two values compare unordered,
+                        // check that the boolean comparison result is always false
+                        CHECK_FALSE(j_values[i] <= j_values[j]);
+                    }
+                    else
+                    {
+                        // otherwise, check that they compare according to their definition
+                        // as the inverse of less than with the operand order reversed
+                        CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]));
+                    }
                 }
             }
         }
 
         SECTION("comparison: greater than")
         {
+            // check that two values compare greater than as expected
             for (size_t i = 0; i < j_values.size(); ++i)
             {
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
-                    // check definition
-                    CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
+                    if (json::compares_unordered(j_values[i], j_values[j]))
+                    {
+                        // if two values compare unordered,
+                        // check that the boolean comparison result is always false
+                        CHECK_FALSE(j_values[i] > j_values[j]);
+                    }
+                    else
+                    {
+                        // otherwise, check that they compare according to their definition
+                        // as the inverse of less than or equal which is defined as
+                        // the inverse of less than with the operand order reversed
+                        CHECK((j_values[i] > j_values[j]) == !(j_values[i] <= j_values[j]));
+                        CHECK((j_values[i] > j_values[j]) == !!(j_values[j] < j_values[i]));
+                    }
                 }
             }
         }
 
         SECTION("comparison: greater than or equal")
         {
+            // check that two values compare greater than or equal as expected
             for (size_t i = 0; i < j_values.size(); ++i)
             {
                 for (size_t j = 0; j < j_values.size(); ++j)
                 {
                     CAPTURE(i)
                     CAPTURE(j)
-                    // check definition
-                    CHECK( (j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]) );
+                    if (json::compares_unordered(j_values[i], j_values[j], true))
+                    {
+                        // if two values compare unordered,
+                        // check that the boolean result is always false
+                        CHECK_FALSE(j_values[i] >= j_values[j]);
+                    }
+                    else
+                    {
+                        // otherwise, check that they compare according to their definition
+                        // as the inverse of less than
+                        CHECK((j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]));
+                    }
                 }
             }
         }
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+        // JSON_HAS_CPP_20 (do not remove; see note at top of file)
+        SECTION("comparison: 3-way")
+        {
+            std::vector<std::vector<std::partial_ordering>> expected =
+            {
+                //0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
+                {eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, //  0
+                {eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, //  1
+                {gt, gt, eq, lt, lt, lt, lt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  2
+                {gt, gt, gt, eq, gt, gt, gt, gt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  3
+                {gt, gt, gt, lt, eq, lt, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  4
+                {gt, gt, gt, lt, gt, eq, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  5
+                {gt, gt, gt, lt, lt, lt, eq, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  6
+                {gt, gt, gt, lt, gt, gt, gt, eq, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  7
+                {gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  8
+                {gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, //  9
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, gt, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 10
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, eq, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 11
+                {gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, gt, lt, lt, lt, lt, lt, lt, un, un}, // 12
+                {gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, lt, lt, lt, lt, lt, lt, un, un}, // 13
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, eq, lt, gt, gt, lt, lt, un, un}, // 14
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, gt, eq, gt, gt, lt, lt, un, un}, // 15
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, eq, gt, lt, lt, un, un}, // 16
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, lt, eq, lt, lt, un, un}, // 17
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, lt, un, un}, // 18
+                {gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, un, un}, // 19
+                {un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 20
+                {un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 21
+            };
+
+            // check expected partial_ordering against expected booleans
+            REQUIRE(expected.size() == expected_eq.size());
+            REQUIRE(expected.size() == expected_lt.size());
+            for (size_t i = 0; i < expected.size(); ++i)
+            {
+                REQUIRE(expected[i].size() == expected_eq[i].size());
+                REQUIRE(expected[i].size() == expected_lt[i].size());
+                for (size_t j = 0; j < expected[i].size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK(std::is_eq(expected[i][j]) == expected_eq[i][j]);
+                    CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
+                    if (std::is_gt(expected[i][j]))
+                    {
+                        CHECK((!expected_eq[i][j] && !expected_lt[i][j]));
+                    }
+                }
+            }
+
+            // check that two values compare according to their expected ordering
+            REQUIRE(expected.size() == j_values.size());
+            for (size_t i = 0; i < j_values.size(); ++i)
+            {
+                REQUIRE(expected[i].size() == j_values.size());
+                for (size_t j = 0; j < j_values.size(); ++j)
+                {
+                    CAPTURE(i)
+                    CAPTURE(j)
+                    CHECK((j_values[i] <=> j_values[j]) == expected[i][j]); // *NOPAD*
+                }
+            }
+        }
+#endif
     }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    SECTION("parser callback regression")
+    {
+        SECTION("filter specific element")
+        {
+            const auto* s_object = R"(
+                {
+                    "foo": 2,
+                    "bar": {
+                        "baz": 1
+                    }
+                }
+            )";
+            const auto* s_array = R"(
+                [1,2,[3,4,5],4,5]
+            )";
+
+            json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
+            {
+                // filter all number(2) elements
+                return j != json(2);
+            });
+
+            CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
+
+            json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
+            {
+                return j != json(2);
+            });
+
+            CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));
+        }
+    }
+#endif
 }
diff --git a/tests/src/unit-constructor1.cpp b/tests/src/unit-constructor1.cpp
index 1955212..21f21e3 100644
--- a/tests/src/unit-constructor1.cpp
+++ b/tests/src/unit-constructor1.cpp
@@ -474,6 +474,13 @@
             json j(false);
             CHECK(j.type() == json::value_t::boolean);
         }
+
+        SECTION("from std::vector<bool>::refrence")
+        {
+            std::vector<bool> v{true};
+            json j(v[0]);
+            CHECK(j.type() == json::value_t::boolean);
+        }
     }
 
     SECTION("create a binary (explicit)")
diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp
index c61b249..bfa9bb9 100644
--- a/tests/src/unit-conversions.cpp
+++ b/tests/src/unit-conversions.cpp
@@ -27,6 +27,13 @@
 SOFTWARE.
 */
 
+// cmake/test.cmake selects the C++ standard versions with which to build a
+// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
+// When using macros that are only defined for particular versions of the standard
+// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
+// version macro in a comment close by, like this:
+// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
+
 #include "doctest_compatibility.h"
 
 #define JSON_TESTS_PRIVATE
@@ -1582,12 +1589,4 @@
     }
 }
 
-#ifdef JSON_HAS_CPP_17
-    #undef JSON_HAS_CPP_17
-#endif
-
-#ifdef JSON_HAS_CPP_14
-    #undef JSON_HAS_CPP_14
-#endif
-
 DOCTEST_CLANG_SUPPRESS_WARNING_POP
diff --git a/tests/src/unit-items.cpp b/tests/src/unit-items.cpp
index fa40f46..64d92b5 100644
--- a/tests/src/unit-items.cpp
+++ b/tests/src/unit-items.cpp
@@ -80,7 +80,7 @@
             json j = { {"A", 1}, {"B", 2} };
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -226,7 +226,7 @@
             const json j = { {"A", 1}, {"B", 2} };
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -361,7 +361,7 @@
             json j = { "A", "B" };
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -507,7 +507,7 @@
             const json j = { "A", "B" };
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -624,7 +624,7 @@
             json j = 1;
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 ++counter;
                 CHECK(i.key() == "");
@@ -693,7 +693,7 @@
             const json j = 1;
             int counter = 1;
 
-            for (auto& i : json::iterator_wrapper(j))
+            for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
             {
                 ++counter;
                 CHECK(i.key() == "");
@@ -777,7 +777,7 @@
             json j = { {"A", 1}, {"B", 2} };
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -939,7 +939,7 @@
             const json j = { {"A", 1}, {"B", 2} };
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -1074,7 +1074,7 @@
             json j = { "A", "B" };
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -1220,7 +1220,7 @@
             const json j = { "A", "B" };
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 switch (counter++)
                 {
@@ -1337,7 +1337,7 @@
             json j = 1;
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 ++counter;
                 CHECK(i.key() == "");
@@ -1406,7 +1406,7 @@
             const json j = 1;
             int counter = 1;
 
-            for (auto& i : j.items())
+            for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
             {
                 ++counter;
                 CHECK(i.key() == "");
@@ -1448,13 +1448,5 @@
     }
 }
 
-#ifdef JSON_HAS_CPP_17
-    #undef JSON_HAS_CPP_17
-#endif
-
-#ifdef JSON_HAS_CPP_14
-    #undef JSON_HAS_CPP_14
-#endif
-
 DOCTEST_GCC_SUPPRESS_WARNING_POP
 DOCTEST_CLANG_SUPPRESS_WARNING_POP
diff --git a/tests/src/unit-iterators2.cpp b/tests/src/unit-iterators2.cpp
index 976d0bc..b9c418c 100644
--- a/tests/src/unit-iterators2.cpp
+++ b/tests/src/unit-iterators2.cpp
@@ -27,11 +27,23 @@
 SOFTWARE.
 */
 
+// cmake/test.cmake selects the C++ standard versions with which to build a
+// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
+// When using macros that are only defined for particular versions of the standard
+// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
+// version macro in a comment close by, like this:
+// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
+
 #include "doctest_compatibility.h"
 
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
+#if JSON_HAS_RANGES
+    #include <algorithm>
+    #include <ranges>
+#endif
+
 TEST_CASE("iterators 2")
 {
     SECTION("iterator comparisons")
@@ -881,4 +893,101 @@
             }
         }
     }
+
+
+#if JSON_HAS_RANGES
+    // JSON_HAS_CPP_20 (do not remove; see note at top of file)
+    SECTION("ranges")
+    {
+        SECTION("concepts")
+        {
+            using nlohmann::detail::iteration_proxy_value;
+            CHECK(std::bidirectional_iterator<json::iterator>);
+            CHECK(std::input_iterator<iteration_proxy_value<json::iterator>>);
+
+            CHECK(std::is_same<json::iterator, std::ranges::iterator_t<json>>::value);
+            CHECK(std::ranges::bidirectional_range<json>);
+
+            using nlohmann::detail::iteration_proxy;
+            using items_type = decltype(std::declval<json&>().items());
+            CHECK(std::is_same<items_type, iteration_proxy<json::iterator>>::value);
+            CHECK(std::is_same<iteration_proxy_value<json::iterator>, std::ranges::iterator_t<items_type>>::value);
+            CHECK(std::ranges::input_range<items_type>);
+        }
+
+        // libstdc++ algorithms don't work with Clang 15 (04/2022)
+#if !defined(__clang__) || (defined(__clang__) && defined(__GLIBCXX__))
+        SECTION("algorithms")
+        {
+            SECTION("copy")
+            {
+                json j{"foo", "bar"};
+                auto j_copied = json::array();
+
+                std::ranges::copy(j, std::back_inserter(j_copied));
+
+                CHECK(j == j_copied);
+            }
+
+            SECTION("find_if")
+            {
+                json j{1, 3, 2, 4};
+                auto j_even = json::array();
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+                auto it = std::ranges::find_if(j, [](int v) noexcept
+                {
+                    return (v % 2) == 0;
+                });
+#else
+                auto it = std::ranges::find_if(j, [](const json & j) noexcept
+                {
+                    int v;
+                    j.get_to(v);
+                    return (v % 2) == 0;
+                });
+#endif
+
+                CHECK(*it == 2);
+            }
+        }
+#endif
+
+        // libstdc++ views don't work with Clang 15 (04/2022)
+        // libc++ hides limited ranges implementation behind guard macro
+#if !(defined(__clang__) && (defined(__GLIBCXX__) || defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)))
+        SECTION("views")
+        {
+            SECTION("reverse")
+            {
+                json j{1, 2, 3, 4, 5};
+                json j_expected{5, 4, 3, 2, 1};
+
+                auto reversed = j | std::views::reverse;
+                CHECK(reversed == j_expected);
+            }
+
+            SECTION("transform")
+            {
+                json j
+                {
+                    { "a_key", "a_value"},
+                    { "b_key", "b_value"},
+                    { "c_key", "c_value"},
+                };
+                json j_expected{"a_key", "b_key", "c_key"};
+
+                auto transformed = j.items() | std::views::transform([](const auto & item)
+                {
+                    return item.key();
+                });
+                auto j_transformed = json::array();
+                std::ranges::copy(transformed, std::back_inserter(j_transformed));
+
+                CHECK(j_transformed == j_expected);
+            }
+        }
+#endif
+    }
+#endif
 }
diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp
index e034593..3ee234e 100644
--- a/tests/src/unit-regression2.cpp
+++ b/tests/src/unit-regression2.cpp
@@ -27,6 +27,13 @@
 SOFTWARE.
 */
 
+// cmake/test.cmake selects the C++ standard versions with which to build a
+// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
+// When using macros that are only defined for particular versions of the standard
+// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
+// version macro in a comment close by, like this:
+// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
+
 #include "doctest_compatibility.h"
 
 // for some reason including this after the json header leads to linker errors with VS 2017...
@@ -48,7 +55,6 @@
 #endif
 
 #if JSON_HAS_EXPERIMENTAL_FILESYSTEM
-// JSON_HAS_CPP_17 (magic keyword; do not remove)
 #include <experimental/filesystem>
 namespace nlohmann::detail
 {
@@ -512,12 +518,15 @@
 
     SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type")
     {
+        // does not compile on ICPC when targeting C++20
+#if !(defined(__INTEL_COMPILER) && __cplusplus >= 202000)
         {
             json j;
             NonDefaultFromJsonStruct x(j);
             NonDefaultFromJsonStruct y;
             CHECK(x == y);
         }
+#endif
 
         auto val = nlohmann::json("one").get<for_1647>();
         CHECK(val == for_1647::one);
@@ -785,6 +794,7 @@
     }
 
 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    // JSON_HAS_CPP_17 (do not remove; see note at top of file)
     SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
     {
         nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
@@ -793,8 +803,8 @@
         const auto j_path = j.get<nlohmann::detail::std_fs::path>();
         CHECK(j_path == text_path);
 
-#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4)
-        // works everywhere but on MSVC and GCC <8.4
+#if defined(__clang__) || ((defined(__GNUC__) && !defined(__INTEL_COMPILER)) && (__GNUC__ > 8 || (__GNUC__ == 8 && __GNUC_MINOR__ >= 4)))
+        // only known to work on Clang and GCC >=8.4
         CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
 #endif
     }
diff --git a/tests/src/unit-wstring.cpp b/tests/src/unit-wstring.cpp
index d15ac84..fa65598 100644
--- a/tests/src/unit-wstring.cpp
+++ b/tests/src/unit-wstring.cpp
@@ -32,6 +32,8 @@
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
+// ICPC errors out on multibyte character sequences in source files
+#ifndef __INTEL_COMPILER
 namespace
 {
 bool wstring_is_utf16();
@@ -115,3 +117,4 @@
         }
     }
 }
+#endif